Les Hill github twitter facebook linked in archives
Posted June 10, 2009

Ruby and the argument collecting hash syntax

Unlike other languages, ruby does not support keyword parameters. As an alternative, we use ruby’s syntactic shorthand to pass a hash as the final argument to a method using the key => value syntax:

1 some_method(first_arg, :key1 => value1, :key2 => value2)

some_method will receive a hash as its second argument with the specified key/value pairs. Unlike regular arguments, there is no way to provide default values for the individual key/value pairs (you can provide a default for the hash argument though):

1 def some_method(thing, options = {})
2   #...
3 end

There are a few ruby idioms for providing default options to an option hash, one of the most common is available in rails, using the core extension Hash#reverse_merge from ActiveSupport1:

1 def some_method(thing, options = {})
2   options.reverse_merge!({:key1 => 'default'})
3   #...
4 end

If you do not have access to ActiveSupport, you can use ruby’s Hash#merge2 as an alternative:

1 def some_method(thing, options = {})
2   options = {:key1 => 'default'}.merge(options)
3   #...
4 end

If that seems like too much code to be typing all the time, you can open up Hash and add a method. Careful! This is simple enough to get wrong. Here is some code3 from a very useful rails plugin:

1 def default!(defaults = {})
2   defaults.each do |key, value|
3     self[key] = value if self[key].nil?
4   end
5   self
6 end

This code works until you want to set a default value to nil. This can be fixed by replacing the conditional on line three with unless self.has_key?(key). Or you can simply rewrite this in terms of Hash#merge4:

1 def default!(defaults = {})
2   replace(defaults.merge(self))
3 end

1 Which builds on ruby’s Hash#merge.

2 Or merge-bang (merge!).

3 Slightly modified for readability.

4 ActiveSupport does something similar.

Thanks to Tom Preston-Werner for the CSS layout, Webby for the blog renderer, and GitHub Pages for the blog hosting.