将hash转化为对象

在处理嵌套了几层的hash时,总是感觉很混乱,读取、修改时都很麻烦。因此想到把hash转换为对象,直接生成key的get/set方法,代码如下:

class HashObj
  class << self
    def load_from_hash(hash)
      if hash.instance_of? Hash
        obj = HashObj.new
        hash.each{|k,v| obj.send :def_sget_method,k,HashObj.load_from_hash(v)}
        obj
      elsif hash.instance_of? Array
        hash.map{|m| HashObj.load_from_hash(m) }
      else
        hash
      end
    end
  end

  def attributes
    hash = {}
    @@reg ||= /=/
    self.singleton_methods.reject{|x| @@reg =~ x.to_s}.each do |m|
      v = self.send(m)
      if v.instance_of? HashObj
        real_v = v.attributes
      elsif v.instance_of? Array
        real_v = []
        v.each do |l|
          if l.instance_of? HashObj
            real_v << l.attributes
          else
            real_v << l
          end
        end
      else
        real_v = v
      end
      hash[m] = real_v
    end
    hash
  end

  protected
  def def_sget_method(name,val)
    self.instance_variable_set "@#{name}",val

    self.define_singleton_method "#{name}=" do |n_val|
      instance_variable_set "@#{name}",n_val
    end

    self.define_singleton_method name do
      instance_variable_get "@#{name}"
    end
  end
end

使用demo

hash = {name:'jack',age:22,phone:['61900871','8787876'],
                basic_info:{country:'USA',city:'New York'}}
obj = HashObj.load_from_hash hash
obj.name    #'jack'
obj.age     #22
obj.phone   #['61900871','8787876']
obj.basic_info   #
obj.basic_info.country   #'USA'
obj.attributes == hash    #true
obj.age = 30
obj.attributes #{:name=>"jack", :age=>30, :phone=>["61900871", "8787876"],
# :basic_info=>{:country=>"USA", :city=>"New York"}} 

你可能感兴趣的:(将hash转化为对象)