OpenStruct instead of Hash
Tuesday, June 23rd, 2009Hash is a great way to represent the data structure. It is easily converted to xml, json and yaml. Activesupport extends core Hash class and enables these methods.
hash = Hash.from_xml(xml)
hash = Hash.from_json(xml)
Very convenient. But “[ ]” notation to access hash value is sometimes anoying. I want to use “.” operation instead of “[ ]”. One solution is to make a Class and use method_missing.
class Box
def initialize(data)
@data = data.symbolize_keys
end
def method_missing(method, *args)
return @data[method] if @data.has_key?(method)
super
end
end
data = {"box" => {"name" => "creative", "user" => {"name" => "Tom"}}}
box = Box.new(data["box"])
box.name
It works, but needs many codes like Box class. OpenStruct is an easier way to do the same thing.
require 'ostruct'
data = {'box' => {"name" => "creative", "user" => {"name" => "Tom"}}}
box = OpenStruct.new(data["box"])
box.name
Internally OpenStruct uses hash. And it creates methods of hash’s key. For example box has methods “name” and “name=”.
box.public_methods.include? "name" => true box.public_methods.include? "name=" => true
One thing insufficient of OpenStruct is it only converts 1 dimension of hash. So the box.user.name is not possible.
box.user
=> {"name"=>"Tom"}
In order to use box.user.name, nested converting is needed.
def to_openstruct(hash)
hash.inject(OpenStruct.new) { |result, (key, value)|
value = to_openstruct(value) if value.is_a? Hash
result.send("#{key}=", value)
result
}
end
data = {"box" => {"name" => "creative", "user" => {"name" => "Tom"}}}
box = to_openstruct(data["box"])
box.user.name