Skip to content

Instantly share code, notes, and snippets.

@exocode
Last active April 20, 2018 06:38
Show Gist options
  • Save exocode/8415229 to your computer and use it in GitHub Desktop.
Save exocode/8415229 to your computer and use it in GitHub Desktop.
Useful Flash Messages in #Rails
Inevitably somewhere in your rails app you display flash messages to your users to inform them that an action has (or hasn't) taken place. Sometimes they need to provide more info, maybe you've just created some new information for them. Why make them sit around, or wonder where they need to go next when you can take them straight to it in the message itself?
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 = flash.keys.select{|k| FLASH_NOTICE_KEYS.include?(k)}
formatted_messages = messages.map do |type|
content_tag :div, :class => type.to_s do
message_for_item(flash[type], flash["#{type}_item".to_sym])
end
end
formatted_messages.join
end
def message_for_item(message, item = nil)
if item.is_a?(Array)
message % link_to(*item)
else
message % item
end
end
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"
end
def update
@order = Order.find(params[:id])
@order.update_attributes(params[:order])
flash[:notice_item] = "#{@order.items} items"
end
private
def set_message
flash[:notice] = "Thank you, we have updated your order with %s"
end
I hope it helps. Coming up next, my sexy form builder!
http://glenngillen.com/thoughts/useful-flash-messages-in-rails
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment