Flash access changes in Rails 4.1

Vipul

By Vipul

on July 24, 2014

Prior to upgrading to Rails 4.1 we had a helper to display flash messages and to add css class to the message based on flash type. Here is the code.

1
2module FlashHelper
3  ALERT_TYPES = [:success, :info, :warning, :danger]
4
5  def bootstrap_flash
6    flash_messages = []
7    flash.each do |type, message|
8      next if message.blank?
9
10      type = :success if type == :notice
11      type = :danger  if type == :alert
12      type = :danger  if type == :error
13
14      next unless ALERT_TYPES.include?(type)
15
16      ....
17      Array(message).each do |msg|
18        text = content_tag(:div, msg.html_safe, :class => "alert fade in alert-#{type} ")
19        flash_messages << text if msg
20      end
21    end
22    flash_messages.join("\n").html_safe
23  end
24end
25

After upgrading to Rails 4.1, we started using the new Cookies serializer. Following code was added to an initializer.

1
2  Rails.application.config.action_dispatch.cookies_serializer = :json
3

Soon after this our flash helper started misbehaving and all flash messages disappeared from the application.

JSON Cookies Serializer

Before we move ahead, a word on the new JSON Cookies Serializer. Applications created before Rails 4.1 uses Marshal to serialize cookie values into the signed and encrypted cookie jars.

Commits like this and this made it possible to have Cookies serializer and defaulted from Marshal Serializer to a secure Serializer using JSON.

The JSON Serializer works on JSON objects. Thus objects like Date and Time will be stored as strings. Hash keys will be stored as strings.

JSON serializer makes the application much safer since it is safer to pass around strings compare to passing around arbitrary values which is what was happens when values are marshalled and passed around.

Coming back to our problem, change Stringify the incoming hash in FlashHash coupled with above serialization changes meant that even if we put a symbol as a key in the flash we have to retrieve it as "string" since the keys are internally being converted into strings.

The difference is clearly illustrated below.

1
2flash["string"] = "a string"
3flash[:symbol] = "a symbol"
4
5# Rails < 4.1
6flash.keys # => ["string", :symbol]
7
8# Rails >= 4.1
9flash.keys # => ["string", "symbol"]
10

Solution

Now that we know the root cause of the problem the fix was simple. Instead of relying on symbols use "string" to access value from flash.

1
2module BootstrapFlashHelper
3  ALERT_TYPES = ['success', 'info', 'warning', 'danger']
4
5  def bootstrap_flash
6    flash_messages = []
7    flash.each do |type, message|
8      compare_type = type.to_s # Notice the stringifying of keys here, to make this work even with symbols.
9      next if message.blank?
10
11      compare_type = 'success' if compare_type == 'notice'
12      compare_type = 'danger'  if compare_type == 'alert'
13      compare_type = 'danger'  if compare_type == 'error'
14
15      next unless ALERT_TYPES.include?(compare_type)
16
17      Array(message).each do |msg|
18        text = content_tag(:div, msg.html_safe, :class => "alert fade in alert-#{compare_type} ")
19        flash_messages << text if msg
20      end
21    end
22    flash_messages.join("\n").html_safe
23  end
24end
25

Stay up to date with our blogs. Sign up for our newsletter.

We write about Ruby on Rails, ReactJS, React Native, remote work,open source, engineering & design.