Being a Ruby noob (and having a background in Groovy), I was a little surprised that you can not access hash objects using the dot notation. I am writing an application that relies heavily on XML and JSON data. This data will need to be displayed and I would rather use book.author.first_name over book[‘author’][‘first_name’]. A quick search on google yielded this post on the subject.
So, taking the DRYOO (Don’t Repeat Yourself Or Others) concept. I came up with this:
class ::Hash
# add keys to hash
def to_obj
self.each do |k,v|
if v.kind_of? Hash
v.to_obj
end
k=k.gsub(/\.|\s|-|\/|\'/, '_').downcase.to_sym
## create and initialize an instance variable for this key/value pair
self.instance_variable_set("@#{k}", v)
## create the getter that returns the instance variable
self.class.send(:define_method, k, proc{self.instance_variable_get("@#{k}")})
## create the setter that sets the instance variable
self.class.send(:define_method, "#{k}=", proc{|v| self.instance_variable_set("@#{k}", v)})
end
return self
end
end
This works pretty well. It converts each of your keys to properties of the Hash.
However, it doesn’t sit very well with me because I probably will not use 90% of the properties most of the time. Why should I go through the performance overhead of creating instance variables for all of the unused ones?
Enter the ‘magic method’ #missing_method:
class ::Hash
def method_missing(name)
return self[name] if key? name
self.each { |k,v| return v if k.to_s.to_sym == name }
super.method_missing name
end
end
This is a much cleaner method for my purposes. Quite simply, it checks to see if there is a key with the given symbol, and if not, loop through the keys and attempt to find one.
I am a Ruby noob, so if there is something I am overlooking, please let me know.
s


#1 by Chad O at April 24th, 2010
| Quote
Thanks!
I’m a Ruby (and programming in general) n00b, so this seems fantastic to me! I’ll have to ask my coworkers what they think and get some more experienced feedback.
But I went to a presentation on REST with Groovy, and was started to discover that Ruby didn’t match up. So good work!
#2 by kerry at April 26th, 2010
| Quote
yea, Groovy has some nice features. I have been using JRuby lately at work though.
#3 by Ajasja Ljubetič at May 5th, 2010
| Quote
Why not just use http://ruby-doc.org/stdlib/libdoc/ostruct/rdoc/classes/OpenStruct.html ?
#4 by kerry at May 5th, 2010
| Quote
Thanks for the tip!
I did not know of that class.
Sometimes though, you may not have the ability to wrap an object to access the properties or you don’t want to do it over and over again.
#5 by kerry at May 5th, 2010
| Quote
When dealing with json feeds (which is what I am using this for) OpenStruct is insufficient as there are nested hashes. So you would have to call it for element. ie.
Given:
my_hash = { :a => :b, :c => {:d => :e, :f => :g} }
using openstruct you would have to say openstruct.c[:d] to get :e
with my solution you can simply say:
my_hash.c.d
#6 by Kenan at May 7th, 2010
| Quote
Hey, thanks so much. I was not looking forward to figuring out how to do this (OpenStruct isn’t recursive and wouldn’t apply to nested stuff). But this did the trick for me. Thanks for saving me some time!
#7 by zhenya1001 at October 29th, 2010
| Quote
Great idea, but
why do you need this line?
self.each { |k,v| return v if k.to_s.to_sym == name }
if you have
return self[name] if key? name
#8 by kerry at October 30th, 2010
| Quote
Hashes can contain any type as the key, that line allows it to work with hashes that have other types as keys (strings, for instance).
#9 by Jesse Wolgamott at March 31st, 2011
| Quote
Hey one suggestion: also implement to respond_to? if you’re doing method_missing. I saw this in this stack-overflow question and think it makes a lot of sense: http://stackoverflow.com/questions/291132/method-missing-gotchas-in-ruby
#10 by Dan at August 16th, 2011
| Quote
hash_extension is a gem that does this. It also provides “setter” methods as well as ActiveRecord finders: http://enterpriserails.rubyforge.org/hash_extension/