Skip to content

Instantly share code, notes, and snippets.

@jcasimir
Created September 11, 2011 16:51
Show Gist options
  • Save jcasimir/1209811 to your computer and use it in GitHub Desktop.
Save jcasimir/1209811 to your computer and use it in GitHub Desktop.
Managing the Flash

Managing the Flash

HTTP is a stateless protocol. The server, generally speaking, has no idea if request number 243 came from the same user as request number 236. That's beautiful and, at the same time, annoying.

In the context of web applications we frequently want to persist state between requests. That might mean something like a shopping cart that follows a user through the online store, but it can be as simple as a status message.

In modern applications users expect feedback. They click a delete link and they expect to no just see the item disappear, they'll expect a "Item Deleted" message. In Rails we handle these messages using the flash.

Flash as Hash

Developers often refer to the flash as a hash. Is it? As an experiment, within a controller action, you could try this:

def index
  raise flash.class.to_s
  #...
end

Trigger that method from a browser and you'll find that the flash is an instance of ActionDispatch::Flash::FlashHash. When you write flash, you're calling a helper method which returns this object to you. It acts a lot like a Ruby Hash, yes, but adds more functionality under the hood.

For our purposes, though, we can think of it as a hash-like object.

Flash Messages

The flash will be our message storage object. Let's look at how, where, and when to set messages.

Normal Way

The easiest and most common way is to set a message explicitly in the controller. For instance, in a destroy action:

class ArticlesController < ApplicationController
  def destroy
    article = Article.find(params[:id])
    article.destroy
    flash[:notice] = "Article '#{article.title}' was deleted."
    redirect_to articles_path                
  end
end

Common Keys

In this example we used the :notice key. You can set whatever keys you want within the flash to hold your messages. It's just a matter of matching up the setting in the controller with the display in the view template. But a few keys are commonly used based on their implications for the user:

  • :notice: "Everything is cool, just letting you know."
  • :alert: "Something is wrong, but it can probably be fixed."
  • :error: "Something is really wrong and is probably irrecoverable"

If those don't feel like a good fit for your application just make up your own!

In the Redirect

The actions where we want to set a flash message are often the same ones that necessitate a redirect. In Rails 3 the redirect_to helper added options to specify the flash message in the redirect call itself.

Instead of this:

class ArticlesController < ApplicationController
  def destroy
    article = Article.find(params[:id])
    article.destroy
    flash[:notice] = "Article '#{article.title}' was deleted."
    redirect_to articles_path                
  end
end

We can pass a :notice => argument to redirect_to like this:

class ArticlesController < ApplicationController
  def destroy
    article = Article.find(params[:id])
    article.destroy
    redirect_to articles_path, :notice => "Article '#{article.title}' was deleted."
  end
end

redirect_to will accept :notice and/or :alert keys. If you want to set a custom key, like :error, you could do it like this:

class ArticlesController < ApplicationController
  def destroy
    article = Article.find(params[:id])
    article.destroy
    redirect_to articles_path, :flash => {:error => "Article '#{article.title}' was deleted."}
  end
end

But, then, what's really the point? If you're using custom keys, it's easier to just set the flash in its own line.

Displaying the Flash

You set the flash message in the controller then you have to display it in the view layer.

It's generally a good idea to display the flash in your layout. That way it's available on every page and you don't have to remember it in individual view templates.

Specific Keys

The flash helper method is available in the view just like it was in the controller. A simple ERB injection and hash-style lookup will fetch the data:

<p class='flash notice'>
  <%= flash[:notice] %>
</p>

That will display a message stored under the :notice key.

Adding Conditions

But what about when there is no flash[:notice]? You'll get a set of empty <p> tags, which could look weird in your layout.

In Ruby, if you ask a hash for a key that doesn't exist you'll get back nil. Ruby considers nil to be "falsey", so we can write a condition like this:

<% if flash[:notice] %>
  <p class='flash notice'>
    <%= flash[:notice] %>
  </p>
<% end %>

5 lines to display one message? We can refactor it by using the content_tag helper and an inline condition:

<%= content_tag :p, flash[:notice], :class => "flash notice" if flash[:notice] %>

Iterating

If our application uses multiple keys, like :notice, :alert, and :error, then we'd need to do that three times:

<%= content_tag :p, flash[:notice], :class => "flash notice" if flash[:notice] %>
<%= content_tag :p, flash[:alert], :class => "flash alert" if flash[:alert] %>
<%= content_tag :p, flash[:error], :class => "flash error" if flash[:error] %>

Iteration allows us to reduce the redundancy:

<% flash.each do |key, message| %>
  <%= content_tag :p, message, :class => "flash #{key}" %>
<% end %>

The condition is no longer necessary because if there aren't keys in the flash, the iteration will just never run.

@yshmarov
Copy link

OMG 10 years! And still relevant!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment