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!