The Beauty of reverse_merge

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!

The Comments Strike Back

It seems I'm well on my way to implementing my behaviors. A couple of revisions and some previews of what I'm thinking for tags:

  1. It seems we won't need a behavior for the comments themselves. I have the CommentBucket behavior escaping any HTML that occurs in the post. Obviously, an author of the site can bypass this, but then the security is less of an issue.
  2. I haven't figured out how to redirect the response to the parent page for the CommentBucket behavior. Any ideas?

Tags:

  <r:comments:each>
    <r:title />

    <r:body />
    <r:author />
    <r:date />
  </r:comments:each>

  <r:comments:form />

As always, your thoughts are welcome and encouraged.