When you get a report from your site users or your clients complaining about seeing flash messages twice or seeing a flash message on the incorrect page, don’t listen to your coworker/boss when they tell you it must be a rails bug. It’s a feature.
When you do this…
@user = User.find params[:id]
if @user.update_attributes params[:user]
flash[:success] = 'User attributes have been updated'
flash[:failure] = 'Could not save user.'
render :action => :edit
flash[:failure] call you’re making is going to leave the flash set for
the next request to display.
Now, since you’re rendering the edit view, which is probably a form that’s going to display some errors and POST right back to #update, you might not find this in normal application use. But if you want to see it, you can probably just click away from that edit page to some other link, and then you’ll see your failure flash on the page when you didn’t expect to.
flash.now[:failure] = 'Could not save user.'
render :action => :edit
You still access its value via flash[:failure] from your views, but it will get removed on the page render, rather than on the next request, which is what you want because you don’t want to keep it around for the next request.
From the rails docs…
This method enables you to use the flash as a central messaging system in your app. When you need to pass an object to the next action, you use the standard flash assign (=). When you need to pass an object to the current action, you use now, and your object will vanish when the current action is done.
One downside to this is you can’t do…
assert_equal 'Could not save user.', flash[:failure]
…in your functional tests anymore — because after the view is processed and your functional test is running, that value is already gone! Instead, you can use…
assert_select 'div.flash-failure p', 'Could not save user.'
…which will check that the view had a p element, surrounded by a div with the right class, where the p has your well written save failure copy contained in it.
So the rule is…
- Use flash when you’re going to redirect, so that the next action will be able to display the flash value
- Use flash.now when you’re going to render, so that the current view can display the value and the next view won’t (additionally and wrongly) display it.
Additional rules to keep in mind…
- Use the flash at the top of a page to tell someone THAT they are not fit to be using your app
- Use errormessagesfor down in the view to tell them WHY they are not fit to be using your app