Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Declaring instance variables iterating over a hash!

i want to do the following:

I want to declare the instance variables of a class iterating over a dictionary.

Let's assume that i have this hash

hash = {"key1" => "value1","key2" => "value2","key3" => "value3"}

and i want to have each key as instance variable of a class. I want to know if i could declare the variables iterating over that hash. Something like this:

class MyClass
  def initialize()
    hash = {"key1" => "value1","key2" => "value2","key3" => "value3"}
    hash.each do |k,v|
      @k = v
    end
  end
end

I know this doesn't work! I only put this piece of code to see if you could understand what i want more clearly.

Thanks!

like image 522
flyer88 Avatar asked Oct 23 '09 18:10

flyer88


4 Answers

class MyClass
  def initialize()
    hash = {"key1" => "value1","key2" => "value2","key3" => "value3"}
    hash.each do |k,v|
      instance_variable_set("@#{k}",v)
      # if you want accessors:
      eigenclass = class<<self; self; end
      eigenclass.class_eval do
        attr_accessor k
      end
    end
  end
end

The eigenclass is a special class belonging just to a single object, so methods defined there will be instance methods of that object but not belong to other instances of the object's normal class.

like image 186
Chuck Avatar answered Nov 17 '22 22:11

Chuck


class MyClass
  def initialize
    # define a hash and then
    hash.each do |k,v|
      # attr_accessor k # optional
      instance_variable_set(:"@#{k}", v)
    end
  end
end
like image 34
Eimantas Avatar answered Nov 17 '22 22:11

Eimantas


Chuck's answer is better than my last two attempts. The eigenclass is not self.class like I had thought; it took a better test than I had written to realize this.

Using my old code, I tested in the following manner and found that the class was indeed manipulated and not the instance:

a = MyClass.new :my_attr => 3
b = MyClass.new :my_other_attr => 4

puts "Common methods between a & b:"
c = (a.public_methods | b.public_methods).select { |v| a.respond_to?(v) && b.respond_to?(v) && !Object.respond_to?(v) }
c.each { |v| puts "    #{v}" }

The output was:

Common methods between a & b:
    my_other_attr=
    my_attr
    my_attr=
    my_other_attr

This clearly disproves my presupposition. My apologies Chuck, you were right all along.

Older answer:

attr_accessor only works when evaluated in a class definition, not the initialization of an instance. Therefore, the only method to directly do what you want is to use instance_eval with a string:

class MyClass
  def initialize(params)
    #hash = {"key1" => "value1","key2" => "value2","key3" => "value3"}
    params.each do |k,v|
      instance_variable_set("@#{k}", v)
      instance_eval %{
        def #{k}
          instance_variable_get("@#{k}")
        end
        def #{k}= (new_val)
          instance_variable_set("@#{k}", new_val)
        end
      }
    end
  end
end

To test this try:

c = MyClass.new :my_var => 1
puts c.my_var
like image 4
Robert K Avatar answered Nov 17 '22 21:11

Robert K


http://facets.rubyforge.org/apidoc/api/more/classes/OpenStructable.html

OpensStructable is a mixin module which can provide OpenStruct behavior to any class or object. OpenStructable allows extention of data objects with arbitrary attributes.

like image 2
ykaganovich Avatar answered Nov 17 '22 23:11

ykaganovich