Reputation: 17444
I've got a class, like this one:
class A
attr_accessor(:field2)
attr_accessor(:field1)
end
What's the best way to produce a Hash out of it with keys and values taken from the class instance?
And what's the best way to populate the instance of class A with values from that Hash?
=====
I'm probably looking for something similar to JavaBeans introspection that would give me the names of the data object fields, then execute logic based on this info. Ruby is a very modern flexible and dynamic language and I refuse to admit that it will not let me do things that I can easily do with Java ;-)
=====
In the end I found out that Struct is the best option:
a = {:a => 'qwe', :b => 'asd'}
s = Struct.new(*a.keys).new(*a.values) # Struct from Hash
h = Hash[*s.members.zip(s.values).flatten] # Hash from Struct
Upvotes: 0
Views: 135
Reputation: 67850
Something to start playing with:
a = A.new
a.field1 = 1
a.field2 = 2
methods = a.public_methods(false).select { |s| s.end_with?("=") }
attributes = Hash[methods.map { |m| [m, a.send(m)] }]
=> {"field1"=>1, "field2"=>2}
If you want a more fine-grained detection of pairs getter/setter:
methods = a.public_methods(false).group_by { |s| s.split("=")[0] }.
map { |k, vs| k if vs.size == 2 }.compact
Regarding the second question:
attributes = {"field1"=>1, "field2"=>2}
a = A.new
a.each { |k, v| a.send(k+"=", v) }
=> #<A:0x7f9d7cad7bd8 @field1=1, @field2=2>
However, it would appear you want to use something like Struct or OpenStruct.
Upvotes: 1
Reputation: 160191
f.instance_variables.inject({}) { |m, v| m[v] = f.instance_variable_get v; m }
Although that gives you the @
in the attribute symbols; you could strip it off in the assignment if it's important. The reverse is just the opposite; iterate over the keys and use instance_variable_set
.
You could also interrogate for methods ending in =
, which would be more robust if you've added logic to any of them instead of relying on those created by attr_accessor
.
Upvotes: 1
Reputation: 1712
Class to hash. Could write this as a method in A, of course, if desired.
foo = A.new
foo.field1 = "foo"
foo.field2 = "bar"
hash = {}
foo.instance_variables.each {|var| hash[var.to_s.delete("@")] = foo.instance_variable_get(var) }
p hash
=> {"field1"=>"foo", "field2"=>"bar"}
Hash to class: extend A's initialize
. Borrowed from http://pullmonkey.com/2008/01/06/convert-a-ruby-hash-into-a-class-object/ .
class A
def initialize(hash)
hash.each do |k,v|
self.instance_variable_set("@#{k}", v)
end
end
end
Then you can:
hash = { :field1 => "hi" }
foo = A.new(hash)
=> #<A:0x00000002188c40 @field1="hi">
Upvotes: 1