Dynamically assigning instance variables (redux)

I need a modern answer to Daniel B.'s 2002 post,

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/39534

Is there a way to dynamically assign instance variables?

class SomeClass
def initialize(hash)
hash.each do |key,val|
@#{key} = val # Doesn’t work
end
end
end

Actually, I’d like the assignment and make them attr_readers too.

Nearly meta-programming clueless,

On 10/16/06, Bil K. [email protected] wrote:

  end
end

end

Here’s one way:
http://rubyquiz.com/quiz67.html

If you just want to set instance variables, you can do:
def initialize(has)
hash.each do |key,val|
instance_variable_set “@#{key}”, val
end
end

On 06-10-16, at 22:35, Bil K. wrote:

Is there a way to dynamically assign instance variables?

class SomeClass
def initialize(hash)
hash.each do |key,val|
@#{key} = val # Doesn’t work
end
end
end

Replace:

@#{key} = val

with:

self.instance_variable_set(:@"#{key}", val)

Actually, I’d like the assignment and make them attr_readers too.

Nearly meta-programming clueless,

Not sure on this one, havn’t gone too deep into metaprogramming in ruby.

Bil K. wrote:

Is there a way to dynamically assign instance variables?
[…snip…]
Actually, I’d like the assignment and make them attr_readers too.

class Foo
def initialize( hash={} )
hash.each{ |key,value|
var_name = “@#{key}”
instance_variable_set( var_name, value )
singleton_class = class << self; self; end
singleton_class.class_eval{
define_method( key ){
instance_variable_get( var_name )
}
define_method( “#{key}=” ){ |new_value|
instance_variable_set( var_name, new_value )
}
}
}
end
end

f = Foo.new :name=>‘whee’, :age=>12
p f, f.age
#=> #<Foo:0x32b900 @age=12, @name=“whee”>
#=> 12
f.age=32
p f
#=> #<Foo:0x32b900 @age=32, @name=“whee”>

Phrogz [email protected] wrote:

  singleton_class = class << self; self; end

end
Wow, that was way cool. I think we can make it shorter (esp since he
only wants attr_readers) and faster (no point generating the singleton
class every time thru the loop):

class Foo
def initialize( hash={} )
singleton_class = class << self; self; end
hash.each do |key,value|
instance_variable_set( “@” + key, value )
singleton_class.class_eval { attr_reader key }
end
end
end

f = Foo.new “name”=>‘whee’, “age”=>12
p f.age, f.name # => works
f.name = “no, it’s a reader” # => error, as desired

But pls note that I’m just imitating; the self-returning singleton class
was totally beyond me! m.

On Oct 16, 2006, at 8:45 PM, Phrogz wrote:

  singleton_class = class << self; self; end

end
I find this much too complex with no real need for using
singleton_classes (ever, really). Try:

class Foo
def initialize( hash={} )
hash.each do |ivar, val|
self.class.send :attr_accessor, ivar unless respond_to? ivar
send “#{ivar}=”, val
end
end
end

The primary benefit of writing code like this is that you can know
what it does in half a glance with no extra thinking. I should also
point out that calling a define_method method is about 2x slower than
an attr_* method. (not a big deal in this case but take a look at
rails’ primary_key and think about that impact)

Phrogz wrote:

    define_method( "#{key}=" ){ |new_value|
      instance_variable_set( var_name, new_value )
    }
  }
}

end
end

Note that the above defines distinct readers/writers for each instance:

f1 = Foo.new :name=>‘whee’, :age=>12
f2 = Foo.new :chicken=>‘gorkbo’
p f1, f2
#=> #<Foo:0x32b900 @age=12, @name=“whee”>
#=> #<Foo:0x32b360 @chicken=“gorkbo”>

p f1.name
#=> “whee”

p f2.name
#=> NoMethodError: undefined method `name’ for #<Foo:0x32b34c
@chicken=“gorkbo”>
#=> at top level in tmp.rb at line 26

You can also open up the main class (instead of the singleton class) to
define the methods shared by all instances, or store the hash as an
instance variable and use method_missing to dynamically catch and
report values as appropriate.

Here’s an example of that for Hash objects in general:

class Hash

Causes all Hash objects to behave similar to Object literals in

JS,

with their keys accessible via dot notation.

foo = { ‘name’=>‘Gavin’, ‘age’=>33 }

foo.weight = 180

p foo.name, foo.weight

#=> “Gavin”

#=> 180

p foo.zzzz

#=> NoMethodError: undefined method `zzzz’ for

#=> {“name”=>“Gavin”, “weight”=>180, “age”=>33}:Hash

alias_method :mMm, :method_missing
def method_missing(meth,*args)
if /=$/ =~ (meth=meth.id2name)
# Name ends with an equals sign; shove value to the hash
self[ meth[0…-1] ] = (args.length<2 ? args[0] : args)
else
# Retrieve the value
if self.key?( meth )
self[ meth ]
else
# Let the real method_missing handle/pass on up
mMm( meth.intern, *args )
end
end
end
end

TIMTOWTDI.

Ryan D. wrote:

class Foo
def initialize( hash={} )
hash.each do |ivar, val|
self.class.send :attr_accessor, ivar unless respond_to? ivar
send “#{ivar}=”, val
end
end
end

This is distinctly different, however, in that it adds the accessor
methods to the Foo class, not the instances. I have no idea what Mr.
Kleb wanted, but given that he’s adding accessors based on hashes
passed to each instance, my guess was that he would want a unique set
per instance.

f1 = Foo.new :name=>‘whee’
f2 = Foo.new :age=>12
p f1.age
#=> nil

Is the above result desirable, Bill?

matt neuburg wrote:

end

end
end

/slaps forehead
Of course, very nice :slight_smile:

On Tue, 17 Oct 2006, Phrogz wrote:

  singleton_class.class_eval { attr_reader key }
end

end
end

/slaps forehead
Of course, very nice :slight_smile:

or, with attributes.rb:

 harp:~ > cat a.rb
 require 'rubygems'
 require 'attributes'

 class Foo
   def initialize(h={}) h.each{|k,v| attributes k => v} end
 end

 foo = Foo.new 'a' => 4, :k => 2
 p foo.a
 p foo.k


 harp:~ > ruby a.rb
 4
 2

:wink:

-a

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs