July 2008 Archives

Useful Flash Messages in Rails

Posted by glenn on Friday, July 11, 2008

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:

<%= 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!

view comments

Sexy Forms in Rails

Posted by glenn on Wednesday, July 16, 2008

sexy-forms-in-rails

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.

It started with a custom FormBuilder.

class SemanticFormBuilder < ActionView::Helpers::FormBuilder
  include SemanticFormHelper

  def field_settings(method, options = {}, tag_value = nil)
    field_name = "#{@object_name}_#{method.to_s}" 
    default_label = tag_value.nil? ? "#{method.to_s.gsub(/\_/, " ")}" : "#{tag_value.to_s.gsub(/\_/, " ")}" 
    label = options[:label] ? options.delete(:label) : default_label
    options[:class] ||= "" 
    options[:class] += options[:required] ? " required" : "" 
    label += "*" if options[:required]
    [field_name, label, options]
  end

  def text_field(method, options = {})
    field_name, label, options = field_settings(method, options)
    wrapping("text", field_name, label, super, options)
  end

  def file_field(method, options = {})
    field_name, label, options = field_settings(method, options)
    wrapping("file", field_name, label, super, options)
  end

  def datetime_select(method, options = {})
    field_name, label, options = field_settings(method, options)
    wrapping("datetime", field_name, label, super, options)
  end

  def date_select(method, options = {})
    field_name, label, options = field_settings(method, options)
    wrapping("date", field_name, label, super, options)
  end

  def radio_button(method, tag_value, options = {})
    field_name, label, options = field_settings(method, options)
    wrapping("radio", field_name, label, super, options)
  end

  def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
    field_name, label, options = field_settings(method, options)
    wrapping("check-box", field_name, label, super, options)
  end

  def select(method, choices, options = {}, html_options = {})
    field_name, label, options = field_settings(method, options)
    wrapping("select", field_name, label, super, options)
  end

  def time_zone_select(method, choices, options = {}, html_options = {})
    field_name, label, options = field_settings(method, options)
    # wrapping("time-zone-select", field_name, label, super, options)
    select_box = this_check_box = @template.select(@object_name, method, choices, options.merge(:object => @object), html_options)
    wrapping("time-zone-select", field_name, label, select_box, options)    
  end

  def password_field(method, options = {})
    field_name, label, options = field_settings(method, options)
    wrapping("password", field_name, label, super, options)
  end

  def text_area(method, options = {})
    field_name, label, options = field_settings(method, options)
    wrapping("textarea", field_name, label, super, options)
  end

  def submit(method, options = {})
    field_name, label, options = field_settings(method, options.merge( :label => "&nbsp;"))
    wrapping("submit", field_name, label, super, options)
  end

  def submit_and_cancel(submit_name, cancel_name, options = {})
    submit_button = @template.submit_tag(submit_name, options)
    cancel_button = @template.submit_tag(cancel_name, options)
    wrapping("submit", nil, "", submit_button+cancel_button, options)
  end

  def radio_button_group(method, values, options = {})
    selections = []
    values.each do |value|
      if value.is_a?(Hash)
        tag_value = value[:value]
        label = value[:label]
        help = value.delete(:help)
      else
        tag_value = value
        value_text = value
      end
      radio_button = @template.radio_button(@object_name, method, tag_value, options.merge(:object => @object, :help => help))
      selections << boolean_field_wrapper(
                        radio_button, "#{@object_name}_#{method.to_s}",
                        tag_value, value_text)
    end
    selections    
    field_name, label, options = field_settings(method, options)
    semantic_group("radio", field_name, label, selections, options)    
  end

  def check_box_group(method, values, options = {})
    selections = []
    values.each do |value|
      if value.is_a?(Hash)
        checked_value = value[:checked_value]
        unchecked_value = value[:unchecked_value]
        value_text = value[:label]
        help = value.delete(:help)
      else
        checked_value = 1
        unchecked_value = 0
        value_text = value
      end
      check_box = @template.check_box(@object_name, method, options.merge(:object => @object), checked_value, unchecked_value)
      selections << boolean_field_wrapper(
                        check_box, "#{@object_name}_#{method.to_s}",
                        checked_value, value_text)
    end
    field_name, label, options = field_settings(method, options)
    semantic_group("check-box", field_name, label, selections, options)    
  end
end

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:

module SemanticFormHelper

  def wrapping(type, field_name, label, field, options = {})
    help = %Q{#{options[:help]}} if options[:help]
    to_return = []
    to_return << %Q{ 
} to_return << %Q{} unless ["radio","check", "submit"].include?(type) to_return << %Q{
} 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&nbsp;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.

UPDATE: It’s now a plugin, sexy and semantic forms are now available as a plugin

view comments

The complete guide to setting up Starling

Posted by glenn on Thursday, July 17, 2008

complete-guide-to-installing-starling

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:


script/plugin install http://svn.playtype.net/plugins/workling/
script/plugin install http://spawn.rubyforge.org/svn/spawn/

Now in your environment (environment.rb? an initializer? you decide) add the following:


Workling::Remote.dispatcher = Workling::Remote::Runners::SpawnRunner.new

Done!

Creating A Workling Background Worker

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:

ExampleWorker.async_create_new_records(:some_name => "This Person")

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):

sudo /usr/sbin/adduser -s /sbin/nologin starling
sudo mkdir -p /var/spool/starling
sudo mkdir -p /var/run/starling
sudo chown starling:starling /var/spool/starling
sudo chown starling:starling /var/run/starling

And now time to start up the services:

sudo -u starling starling -P /var/run/starling/starling.pid -d
script/workling_starling_client start

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:

Workling::Remote.dispatcher = Workling::Remote::Runners::StarlingRunner.new

And that’s it, business as usual!

Monitoring the sucker

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:

def configure_workling_client(w, rails_root,  app_name)
  w.uid = "mongrel" 
  w.gid = "webserver" 
  w.name = "#{app_name}-workling" 
  w.group = "#{app_name}" 
  w.interval = 30.seconds
  w.start = "RAILS_ENV=production #{rails_root}/script/workling_starling_client start" 
  w.stop = "#{rails_root}/script/workling_starling_client stop" 
  w.restart = "#{rails_root}/script/workling_starling_client restart" 
  w.pid_file = "#{rails_root}/log/workling.pid" 

  w.behavior(:clean_pid_file)
end

def monitor_app(w)
  start_if_not_running(w)
  restart_if_resource_hog(w)
  monitor_lifecycle(w)
end

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:

God.watch do |w|
  w.name = "starling" 
  w.group = "starlings" 
  w.uid = "starling" 
  w.gid = "starling" 
  w.interval = 30.seconds # default
  w.start = "/usr/bin/starling -P /var/run/starling/starling.pid -d" 
  w.start_grace = 20.seconds
  w.restart_grace = 20.seconds
  w.pid_file = "/var/run/starling/starling.pid" 
  w.behavior(:clean_pid_file)

  monitor_app(w)
end

Now restart god to take the new config, deploy you changes to your rails app, and cross your fingers ;)

view comments
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.
blog comments powered by Disqus