Comparing Rails' flash hashes will not respect their internal lists of used entries

Posted . Visible to the public.

Rails flashes (FlashHash) track a list of used keys, which is not respected when comparing flash hashes.

This does not concern you under most circumstances.

Basics

When ActionController picks up a flash object, it will call the #sweep method once; that method checks the list of used flash entries and deletes those. All other entries are flagged as used. This means they will be deleted on the next request, but are still be available for rendering during the current request.

Fun facts: When redirecting, this does not happen. Also, using #keep on a flash will reset that list of used records.

The issue

You will rarely run into this, but: assume you want to compare two FlashHash objects:

>> flash1
=> {:hello=>"Universe"}
>> flash2
=> {:hello=>"Universe"}
>> flash1 == flash2
=> true

So far so good. But: When flash1 was sweeped while flash2 was not this happens:

>> flash1.instance_variable_get('@used')
=> {:hello=>true}
>> flash2.instance_variable_get('@used')
=> {:hello=>false}
>> flash1 == flash2
=> true

While the keys are still the same, both flashes actually are not the same, since one will discard its records when processing the next request, while the other will not.

Monkey-patch away

While you don't want to change "regular" comparison methods (like == or eql?), you can use this initializer to get a FlashHash#same? method which checks for that:

ActionController::Flash::FlashHash.class_eval do
  # This is from a Rails 2 project. For Rails 3, say:
  # ActionDispatch::Flash::FlashHash.class_eval do

  def same?(other_flash)
    self == other_flash && used == other_flash.used
  end

  protected

  def used
    @used
  end

end

There you go:

>> flash1.same? flash2
=> true
>> flash1.sweep
>> flash1.same? flash2
=> false
Arne Hartherz
Last edit
License
Source code in this card is licensed under the MIT License.
Posted by Arne Hartherz to makandra dev (2013-03-18 16:30)