Posts Tagged ‘ostruct’

OpenStruct instead of Hash

Tuesday, June 23rd, 2009

Hash 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