Neat Ruby Tricks: Automatically Raising Exceptions With Hash

Hashes are a fundamental and useful data type in Ruby. In their default state if a key is not found in the hash the hash returns nil:

h = { :a => 1, :b => 2 }
h[ :a ] # => 1
h[ :c ] # => nil

Hashes have also becoming a common idiom for passing dynamic named arguments to methods:
def foo( parameters )
  puts parameters[ :bar ] if parameters[ :bar ]
  puts paramsters[ :baz ] if paramsters[ :baz ]
end
# outputs 'hello'
foo( :bar => 'hello' ) 
# outpus 'hello' and 'world'
foo( :bar => 'hello', :baz => 'world' ) 

The need to check whether a parameter is present as we did above is one of the drawbacks of using a hash. Using the often overlooked Hash.new initialisation method for hashes allows us to define a block that is called whenever a key has no value defined:
h = Hash.new do |hash,key| 
  "No value defined for key: #{ key }"
end
hash[ :a ] = 1
# Returns 1
h[ :a ]
# Returns "No value defined for key: b"
h[ :b ]

If we raise an exception in that block then that exception will only be raised if a key is not found:
h = Hash.new do |hash,key|
  raise( "Key #{ key } is not valid" )
end
h[ :a ] = 1
# Returns 1
h[ :a ]
# Raises a RuntimeError with "Key b is not valid" 
h[ :b ]

Instead of forcing your users to pass through a specially constructed hash you can just do it transparently in your method (or in a utility method called from your method) using Hash#update. This provides a convenient way of checking that all the keys in use in a method or block of code are defined without having to manually check each key or forcing hte user to do it for you. You can even ‘segregate’ your keys into keys which are required and keys which are not by creating a hash with a raise block and only using it for the required keys. Putting everything together with some exception handling and you’d end up with
def create_book( parameters )
  required = Hash.new do |h,k|
    raise( "Attribute #{k} is required" ) 
  end
  required.update( parameters )
  begin
  Book.new( 
      required[ :title ],          # Title required
      required[ :author ],         # Author required
      required[ :isbn ],           # ISBN required
      parameters[ :num_pages ],    # Optional
      parameters[ :cover_artist ]) # Optional
  rescue RuntimeError => error
    nil # Return nil if required parameter missing
  end
end

which doesn’t look half bad. No ‘if parameter…’ expressions all over the place checking if values actually exist in the hash.

Farrel Lifson is a lead developer at Aimred.

About Aimred

Aimred is a specialist Ruby and Ruby on Rails development house and consultancy based in Cape Town, South Africa.

We provide Ruby and Ruby on Rails development, consulting and training services to businesses and organisations of all sizes. If you want to find out how we can help you, contact us at info@aimred.com.

Recent Posts

Yearly Archives