The Beauty of reverse_merge

by Sean Cribbs

There are frequent times in development when you want to use "keyword arguments" or in Rails-ish, an options hash. I had looked at some of the Hash extensions that are in the Rails framework in the past, but I had rarely used anything more than stringify_keys and symbolize_keys. Now, reverse_merge is my friend.

Let's take a simple example: say you're making a helper that displays your Person in a default way, where Person has the fields :name, :location, and :birthday. In this horribly contrived example, we'll allow the user to specify the CSS style of each field, like so:

<%= show_person @person, :name => "font-size: 30pt;" %>

Part of the convention-over-configuration principle is providing defaults to reduce the amount of code and to create consistency. This is where the "options hash" comes in. Before when I wanted to do something like this, my code would look thusly:

# in the helper
def show_person(person, options = {})
  options.symbolize_keys!
  options = {:name => "font-size: 36px; color: blue", 
             :location => "font-size: 10px; color: gray;", 
             :birthday => "font-family:  Georgia;" }.update(options)

  # do the rest of the helper stuff...
end

Now let's turn it on its head with reverse_merge.

def show_person(person, options = {})
  options.symbolize_keys!
  options.reverse_merge! :name => "font-size: 36px; color: blue", 
             :location => "font-size: 10px; color: gray;", 
             :birthday => "font-family:  Georgia;"

  # ...
end

Much better! (IMHO, YMMV) reverse_merge works by inserting only those keys that are not in the receiving Hash. Let's clean it up a little more.

def show_person(person, options = {})
  options.to_options!.reverse_merge!  :name => "font-size: 36px; color: blue", 
             :location => "font-size: 10px; color: gray;", 
             :birthday => "font-family:  Georgia;"

  # ...
end

There you have it! Quick, simple and fool-proof "keyword arguments". One enhancement that could simplify it even further is to add our own little extension...

class Hash
  def options_merge!(options)
    self.to_options!.reverse_merge! options
  end
end

Happy Ruby-ing!

© 2006-present Sean CribbsGithub PagesTufte CSS