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.
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.
The flash will be our message storage object. Let's look at how, where, and when to set messages.
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
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!
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.
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.
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.
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] %>
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.
OMG 10 years! And still relevant!