Sometimes you want to store some data in the Rails Session for the next request; for example, to display an error message or notice to the user after redirecting them to another page.
You can store this message in the session using flash, and access it in the next request. The flash is cleared in the next request.
Most of the time, we want to use the flash for two reasons: to show a notice to the user, or to alert them about something. Hence, out of the box, Rails provides two accessors on the flash object, called notice
and alert
, which let you create and access the notice and alert messages in the controllers and views respectively.
class FlashController < ApplicationController
def show
flash.alert = 'usage limit exceeded!'
flash.notice = 'the post was successfully saved'
end
end
It is also possible to assign a flash message when you redirect the request. Simply pass either :notice
or :alert
option to the redirect_to
method and the value will be stored in the flash.
render home_path, notice: "You have successfully signed out."
redirect_to home_path, alert: "Oops! Something went wrong."
Anything you store in the flash is available in the views. You can access the flash both as an object or a Hash.
<%= flash.notice %>
<%= flash[:alert] %>
What's more, you don't even need the flash, as Rails provides the notice
and alert
helper methods, so you can simply do this:
<%= notice %>
<%= alert %>
What if you want to add a custom flash type?
If you need a different flash type than notice
or alert
, you can simply treat the flash
object as a plain Ruby Hash and register the type as a new key-value pair with the standard Hash syntax.
The following code registers a new type called warning
.
class FlashController < ApplicationController
def show
flash[:warning] = 'WARNING'
end
end
Then, in your views, access the warning message as a standard Hash.
<%= flash[:warning] %>
However, there're two drawbacks to this approach.
1) You don't get convenient view helpers like notice
and alert
. You have to use the Hash accessors in your views.
2) You can't pass the warning
option in your redirect_to
helper. Instead, you'll have to set the warning manually, using the Hash syntax.
class FlashController < ApplicationController
def show
# not possible
# redirect_to home_path, warning: "logged out"
redirect_to root_path, flash: { warning: 'logged out' }
end
end
Can we do better?
A Better Solution
You can create custom flash types using the add_flash_types
method. Any new types you create will behave like the existing notice
and alert
types.
First, add the custom flash type in ApplicationController
, the base class of all your Rails controllers. This will create the custom flash helper methods that are available in all views.
class ApplicationController < ActionController::Base
add_flash_types :warning
end
Now you can use the warning
type in the redirects and access the warning
helper in the views.
class FlashController < ApplicationController
def show
redirect_to root_path, warning: "Incomplete profiles"
end
end
# in the view
<%= warning %>
Pretty cool.
Note: Remember that you use the add_flash_types
in the ApplicationController
, which creates helpers that you can use in views for all the controllers that inherit from ApplicationController
.
If you add it to FlashController
and redirect to UsersController#show
, Rails won't recognize the warning
helper in the users/show.html.erb
view.
(I know because I just spent half an hour debugging and scratching my head figuring out why it couldn't find my helper.)
Check out this article for an in-depth explanation of Rails sessions.
Internal Implementation
Let's open the Rails codebase and see how the add_flash_types
method is implemented:
# actionpack/lib/action_controller/metal/flash.rb
def add_flash_types(*types)
types.each do |type|
next if _flash_types.include?(type)
define_method(type) do
request.flash[type]
end
helper_method(type) if respond_to?(:helper_method)
self._flash_types += [type]
end
end
Right away, we notice that the method accepts multiple parameters (see *types
). That means you can create multiple flash types in one line as follows:
add_flash_types :warning, :info
Next, it loops over each type and skips the rest of the code block if it's already registered. For this, Rails checks the _flash_types
array, which is the internal storage for the flash types.
Then it creates a new method named after the custom type using the define_method
method in Ruby. All this method does is access the custom flash value from the request.flash
object.
Next, the newly created method is registered as a helper method on the current controller, using the helper_method
method in Rails. This is how you can access the flash type in the views, such as <%= warning %>
.
Note that we also make sure that the controller has a helper_method
using the respond_to?
method in Ruby. This method verifies if you can call a method on an object.
Finally, the new type is added to the internal storage, i.e. the _flash_types
array.
And that's how Rails registers a custom flash type.
Have you used any custom flash types it in your apps? If yes, which new flash types did you add? Let me know in the comments below.
That's a wrap. I hope you liked this article and you learned something new.
As always, if you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. I look forward to hearing from you.
If you'd like to receive future articles directly in your email, please subscribe to my blog. If you're already a subscriber, thank you.