Ruby Idioms, Part 7

Written on 6:48:00 PM by S. Potter

This idiom I have seen a little more in Rails than I have seen in Ruby, but I am putting it in this Ruby Idioms series anyway. First off, most of you will know by know that you can "transparently" provide a method in Ruby a Hash. What do I mean by "transparently"? Well have a look at the code example below:


user = User.find(:first, :include => :preferences)
The last argument is actually a Hash even though you do not see the curly braces at all. In my opinion it makes for more readable code. So let us look behind the scenes at what the User.find method might do if it wasn't a magic method from ActiveRecord:

class User
  # ... some stuff here
  class << self
    def find(type, params = {})
      options = @@DEFAULTS.merge(params)
      case type
      when :first
        options[:limit] = 1
      when :all
        # do stuff with options and query database
        # bla bla bla
      end
      # rest of implementation
    end
  end
  # ... some stuff here
end
This is quite useful, but one of the idioms in the same vain that I really appreciate is the following usage:

  validates_length_of :name, :title, :company, :in => 2..128
This provides another example of supplying a transparent Hash arguments at the end of a list of arguments, the length of which is unknown. For example we may decide that the code needs to actually be more like:

  validates_length_of :name, :company, :in => 2..64
  validates_length_of :title, :in => 2..128, :allow_nil => true # to cater for those ridiculously long job titles out there or those that do not have a job title at all
Now you will notice that the validates_length_of method accepts a list of variable size for the list of attribute symbols to check and then at the end an option hash to specify length checking specifics. How does the method know there is an option Hash at the end to use for these things and/or how long the list of attributes is? The secret is in the sauce. If we were to look in the source code (though I am not, because I have seen it so many times before), we would see something like:

def validates_length_of(*attrs)
  options = @@DEFAULT_OPTIONS.merge(attrs.pop.symbolize_keys) if attrs.last.is_a?(Hash)
  # then continue with rest of implementation.
end
All it is doing here is in the signature, specifying that there is a list of unknown length to be supplied to the method. Then it pops the last item off that list (Array) if and only if the last element of the list is a Hash for use as the options that the implementation will use. Until the next time enjoy Ruby's "transparent" Hash idioms.

If you enjoyed this post Subscribe to our feed

No Comment

Post a Comment