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):
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:
<%= flash_messages%>
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!
Iâve been meaning to release this for quite a while now, Iâve finally got around to packaging it up for some form of public consumption. Basically, I got sick of having to manually create label tags for each of my form inputs. They should be there by default so that users with visual or other impairments have enough additional information to use the site. Iâm also lazy, and would have a tendency to forget to put them in otherwise. I also wanted a consistent way to display any additional contextual information, so here it is.
Iâll go through the code in detail here, but Iâll keep it updated with any changes and available for download at the semantic form builder git repo. So now on to the code.
Personally I think they are a terribly underused aspect of the Rails framework. Anyway, stepping through what weâve got here. We inherit from ActionView::Helpers::FormBuilder and then begin overriding the various tags that will be called within a form_for. All we do for most methods is call field_settings followed by wrapping. In the method field_settings we set the name of the field to the standard rails convention like person_first_name (@object is created for us automatically by form_for so we donât need to worry about it here). Next we set the label text to display for this form input, it will default to something sensible (generally the name of the field you are generating the input for) or you can override it per input by passing :label in as an option. And finally, we check if you have passed :required => true in and if so we set a required class so we can style it differently. All the relevant info gets returned, so we can pass it into the wrapping method.
So where exactly is wrapping? Iâve moved it out into a helper, as I wanted to use this functionality in regular form_tag calls as well as form_for. So take a look in semantic_form_helper:
}
to_return << field
to_return << %Q{} if ["radio","check"].include?(type)
to_return << %Q{
}
end
def semantic_group(type, field_name, label, fields, options = {})
help = %Q{#{options[:help]}} if options[:help]
to_return = []
to_return << %Q{
}
to_return << %Q{}
to_return << %Q{
}
to_return << fields.join
to_return << %Q{
}
end
def boolean_field_wrapper(input, name, value, text, help = nil)
field = []
field << %Q{}
field << %Q{
#{help}
} if help
field
end
def check_box_tag_group(name, values, options = {})
selections = []
values.each do |item|
if item.is_a?(Hash)
value = item[:value]
text = item[:label]
help = item.delete(:help)
else
value = item
text = item
end
box = check_box_tag(name, value)
selections << boolean_field_wrapper(box, name, value, text)
end
label = options[:label]
semantic_group("check-box", name, label, selections, options)
end
end
There are 3 cases to look at initially. Wrapping a normal input, wrapping a boolean input (like a check box), and wrapping a group of check boxes. For wrapping a normal input, we create a label on the left and insert the field into a div on the right. Iâve got the boolean one there for the âremember me?â type of scenario on a login page, where it looked to make more sense to have the label to the right of the checkbox rather than the left. And finally, wrapping a group of elements which isnât handled at all well in the current Rails setup. Overwhelmed yet? Letâs just look at how you use it then:
<% form_for @document do |f|
field_set_tag "Form Details" do %>
<%= f.date_select :date, :required => true, :help => "date the something happened" %>
<%= f.text_field :number, :required => true, :help => "the reference number for this thing" %>
<%= f.select :external_id, [["Choose an option...",""]] + @externals.map{|c| [c.name, c.id]}, :required => true, :label => "options", :help => "select something from the list" %>
<%= check_box_tag_group "document[other_items][]", @others.map{|u| { :value => u.id, :label => u.description }}, :label => "including these?", :help => "tick the whatever boxes are appropriate for this thing" %>
<%= f.text_field :name, :help => "what was Willis talkin' about?" %>
<%= f.check_box :list, :label => "mailing list", :help => "can we send you a bunch of spam?" %>
<%= f.submit_and_cancel("save", "cancel") %>
<% end %>
Youâll see we can pass in :required => true to visually identify required fields to the user. If we donât like the form name (like the generic âlistâ attribute, then we can override with :label =>. And if there is additional contextual help to provide, weâve got :help =>. Weâve also got a pretty way of showing a selection of check boxes with check_box_tag_group, but youâll have to specify the field name yourself. Iâve also put a convenience method in called submit_and_cancel that takes the text for a submit, and cancel button respectively. Catching the cancel action is up to you though.
And voila! The finished product. Any problems with it, please let me know or fork and fix/pull request on github. Iâve tested it on Safari 3, Firefox 2/3, and IE7. No idea what it looks like on IE6 just yet.
We wanted to push some long running tasks off to the background so that we didnât tie up a mongrel needlessly. Iâve wrestled (and won!) with backgroundrb in the past, but it just seemed like a chore. And theyâve since changed the API enough to mean it would be back to the drawing board, so we may as well assess some of the other options. So without too much further ado, the complete guide to installing, using, and monitoring starling.
For those that havenât heard Starling is the back-end queuing system that Twitter use. And despite the stability issues Twitter have, if initial impressions are anything to go by I donât think Starling is the reason. Itâs fairly light, sits on top of MemCache, and is very quick. The initial lure to Starling though was via the Workling plug-in. We could get a prototype out the door using just the Spawn workling, and then when a queue became important switch over.
Installing Workling
Weâre going to take the same approach I did, and get a prototype background process happening with Spawn first. So letâs install Workling and Spawn:
Now itâs time to create a worker, basically to define what it is exactly that should be executed in the background. For the same of illustration, Iâm just going to loop and create some records. I create the follow app/workers/example_worker.rb file:
class ExampleWorker < Workling::Base
def create_new_records
1000.times do
Item.create :some_name => options[:some_name]
end
end
end
Calling Your New Workling Background Worker
Now in whichever controller would normally need to start this task, you can place a call to the workling worker:
Workling will create a new method for us to call, by prefixing out method name with async_ to allow us to call it asynchronously. Pass in a hash of values you want to use into the method, workling will add a key named :uid containing the unique identifier assigned to this task.
And now weâre done here too! This will use Spawn to fork the job, take it out of the mongrel process, and let your rails stack continue and not hold up your other users.
Installing Starling
Install Starling by grabbing the gem and the MemCache Client (which strangely has a dependency on ZenTest which I need to investigateâ¦):
sudo gem install memcache-client starling
Now the dependencies are installed, we need to start up the required services. Youâre going to need the Starling daemon running, as well as the workling client. Iâd rather not have Starling run as root so first I create a user, and then give it somewhere to create itâs pid and spool files (you can probably skip this and run as root on your local dev box):
A note of particular importance here. The workling_starling_client will run in whatever environment RAILS_ENV is set to, and is not passed in via a command line option. It also does some fruity things with loading up your workers at run time, so if youâre running development and test on the one box youâll need to stop the workling client and restart it each time you want to change environments (or best yet, set your rake:spec/rake:test task to do it for you).
Switching Over To Starling For Background Processing
Switching between background processing systems is pretty simple, we just update the environment setting we made earlier to:
It is all fine and dandy having it up and running now, but itâd be nice to keep it that way. Thatâs where god comes in and some tomfoolery with our app.god config. If youâve not already done so, I suggest you read my previous article on monitoring rails with god which talks about creating a generic config file in your rails app so you donât need to manage god configs for each instance.
Iâm going to change the example app.god in that article to look like:
class Rubypond
attr_reader :ports, :server_names, :workling_client
def initialize
@ports = [5000, 5001]
@server_names = "www.rubypond.com rubypond.com"
@workling_client = true
end
end
@apps << Rubypond.new
And within the god.conf on the server Iâll add the following methods:
Okay, so monitor_app is actually just renaming monitor_rails_app to be more generic. Make sure you change references everywhere though. Elsewhere in the god.conf file we had a block similar to the following:
@apps.each do |app|
app_name = app.class.name.downcase
rails_root = "/var/www/apps/#{app_name}/current"
app.ports.each do |port|
God.watch do |w|
configure_mongrel(w, rails_root, app_name, port)
monitor_rails_app(w)
end
end
God.watch do |w|
configure_workling_client(w, rails_root, app_name)
monitor_app(w)
end if app.respond_to? :workling_client
end
The last God.watch there is where the magic happens. It will look at the app weâve defined in app.god, and if weâve provided an attribute called :workling_client then it will automatically monitor a workling client for this app. Weâre not quite done yet though. We also need Starling to be running, but thatâs not on a per app basis we just have one for the server. So Iâm just going to insert this directly into god.conf:
Glenn Gillen is a ruby, merb, and ruby on rails developer with clients in London, New York, Los Angeles, and Australia. Contact Ruby Pond if you wish to discuss hiring Glenn or one of our other developers for your own project.