Useful Flash Messages in Rails

11 July 2008

Useful Flash Messages in Rails


I've ripped this code out of a project I've been working on and made it a little more generic. Initially I was going to just insert a link in the flash message, but as I was rightly pointed out by my pair and elegant coder extraordinaire, that would mean I'd be polluting my controller with logic and content that should be handled by the view. How do we solve a problem like pollution?

First, we updated the controller so that the flash message now looked something like this:

flash[:error] = "Username and password do not match. If you have 
                   forgotten your password you can %s"

The %s is there to use a string substitution later. If you wanted to insert your link somewhere else, move the %s. Next, we created a new key in flash to hold our additional information (the link path and text):

flash[:error_item] = ["reset it here", forgot_path]

Quite simply, it's an array with the first item being the text and the second the path. Now for handling it within our view, here's what I've got in application_helper.rb:

FLASH_NOTICE_KEYS = [:error, :notice, :warning]

  def flash_messages
    return unless messages ={|k| FLASH_NOTICE_KEYS.include?(k)}
    formatted_messages = do |type|      
      content_tag :div, :class => type.to_s do
        message_for_item(flash[type], flash["#{type}_item".to_sym])

  def message_for_item(message, item = nil)
    if item.is_a?(Array)
      message % link_to(*item)
      message % item

The reason for the constant is that I don't use just :error and :notice, I've also got :warning for times when something needs action but not because of something the user has done wrong (I don't want to make them feel bad when they've not had the opportunity to be good!). In flash messages I exit out on the first line if we don't have content to process for the flashes I expect to output (:error, :notice, and :warning) otherwise I loop over each and create a div to contain the message. The class on the div will be either error, notice or warning so you can style it properly. We defer working out the content to message_for_item

In message_for_item I check if the input is an Array, and if so I use the build in string substitution method to insert the link in the relevant place. As you may notice, I've called splat on item by putting an asterisk in front of it. That means your array can essentially just be the parameters you'd pass in to link_to, allowing you to add additional classes or options if you wish. If it's not an array, I skip the link to and just to the substitution. This will handle traditional flash messages as well as some that might want substitution of a string or other object. Wherever you'd previously inserted your flash messages, now you call:

and the end result is something like this:

So what else could you do with this? Well we extended it to be a little more intelligent based on the object that was passed in. You could also use it as part of a before filter if you've got flash messages that are relatively similar across actions on controllers. Just put %s in the bit to substitute, and then at the relevant part of your action you can insert the a string into flash[:error_item] or the appropriate key, and have it displayed. Like this:

before_filter :set_message, :only => [:create, :update]

  def create
    @order = Order.create!(params[:order])
    flash[:notice_item] = "#{@order.items} items" 

  def update
    @order = Order.find(params[:id])

    def set_message

      flash[:notice] = "Thank you, we have updated your order with %s" 

I hope it helps. Coming up next, my sexy form builder!
