Kerry Buckley

What’s the simplest thing that could possibly go wrong?

Correct use of the flash in Rails

5 comments

[Update 20 April 2010]

I recently had problems testing flash.now, and Google kept leading me back to this post. Unfortunately it doesn't seem to work with the current version of Rails (I'm using 2.3.5 at the moment).

This post from Pluit Solutions gives an alternative approach which seems to work. I haven't tried it with Rails 3 though.


I don't know whether this has caught anyone else out, or whether we just didn't read the documentation properly (it's covered briefly on p153 of AWDwR), but I thought I'd mention it anyway.

Anyone who's written a Rails app will know that the 'flash' is used to store error and status messages, usually on form submissions. Model validation failure messages automatically get copied into the flash, but you often want to do it manually too.

RUBY:
  1. flash[:notice] = "User Details updated."
  2. redirect_to edit_user_path(@user)

The gotcha comes when you want to display a message and render a page, as opposed to redirecting – for example when errors are preventing a form from being submitted. This is how not to do it:

RUBY:
  1. flash[:error] = "Password doesn't match confirmation." # WRONG!
  2. render :action => 'change_password'

The problem is that the flash is stored for the next request. Because we're no longer doing a redirect, that means the message may appear wherever the user goes next, not just on the page that we just rendered. To avoid this, use flash.now, which is only used for the current request:

RUBY:
  1. flash.now[:error] = "Password doesn't match confirmation."
  2. render :action => 'change_password'

The rule of thumb is to use flash if you're redirecting, and flash.now if you're rendering (either explicitly, or by dropping through to the default view for the action).

All very well, but whatever you put in flash.now is cleared out at the end of the request, so how do you test it? The answer (for RSpec, at least) lies in a comment on this RSpec feature request – basically just add the following to spec_helper.rb:

RUBY:
  1. module ActionController
  2.   module Flash
  3.     class FlashHash
  4.       def initialize
  5.         @hash = {}
  6.         @now_hash = {}
  7.       end
  8.    
  9.       def [](key)
  10.         @hash[key]
  11.       end
  12.    
  13.       def []=(key, obj)
  14.         @hash[key] = obj
  15.       end
  16.    
  17.       def discard(k = nil)
  18.         initialize
  19.       end
  20.    
  21.       def now
  22.         @now_hash
  23.       end
  24.    
  25.       def update(hash)
  26.         @hash.update(hash)
  27.       end
  28.      
  29.       def sweep
  30.         # do nothing
  31.       end
  32.     end
  33.   end
  34. end

You can now do something like this:

RUBY:
  1. describe "When a user tries to change his password with an invalid verification code" do
  2.   ...
  3.  
  4.   it "should put an error message in the flash" do
  5.     flash.now[:error].should == "Incorrect verification code or password."
  6.   end
  7.  
  8.   it "should not persist the flash" do
  9.     flash[:error].should be_nil
  10.   end
  11. end

Technorati Tags: , , ,

Written by Kerry

July 4th, 2007 at 2:37 pm

Posted in Rails

5 Responses to 'Correct use of the flash in Rails'

Subscribe to comments with RSS or TrackBack to 'Correct use of the flash in Rails'.

  1. Kerry

    thanks – you’ve just helped me clear a pile of pending specs

    and good to know that there’s at least one other rails/rspec fans at the labs

    Cheers

    Rupert

    Rupert Voelcker

    16 Sep 07 at 10:35 am

  2. Sweet, thanks for posting this. It was driving me crazy not being able to spec my flash.now messages.

    Ben Mabey

    25 Oct 07 at 4:51 pm

  3. Thanks for this! Brilliant!

    Have you thought of having FlashHash iteself extend Hash rather than reimplementing [] and []=? Was there any reason why you didn’t go that way?

    Daniel

    Swombat

    28 Nov 07 at 1:20 pm

  4. No, no reason, other than I didn’t think of doing it that way at the time!

    Kerry

    28 Nov 07 at 3:35 pm

  5. This worked very well but when I tried to test a helper, I really couldn’t get it to work.

    my test looked something like this:

    it “should…”
    flash[:warning] = “warning”
    show_flash_helper

    this didn’t set the flash in the helper when I used this solution. In the end I gave up and used the less pretty solution:

    response.should have_tag(“div.warning”)

    caifara

    27 Dec 07 at 4:34 pm

Leave a Reply