Dynamically reference instance vars

If I need to dynamically reference instance vars, is this the only way
to do it (var set example)?

my_object.send(:instance_variable_set, “@#{iname}”, ivalue)

I expected something more elegant, but this is the only way I can get it
to work. No biggie, just curious.

More complete example below.

– gw

class Shape
attr_accessor :size, :fill_color, :line_color, :line_width
def initialize
@size = “”
@fill_color = “”
@line_color = “”
@line_width = “”
end
end

my_shape = Shape.new

shape_details = {
:size => ‘small’,
:fill_color => ‘red’,
:line_color => ‘black’,
:line_width => ‘2’}

shape_details.each do |iname, ivalue|
my_shape.send(:instance_variable_set, “@#{iname}”, ivalue)
end

On 10/29/2010 09:14 PM, Greg W. wrote:

– gw
end
my_shape.send(:instance_variable_set, “@#{iname}”, ivalue)
end

How about backing the accessors with a hash that also has an accessor?
That way you could merge in a hash of settings or set/get them neatly by
name:

class Shape
attr_reader :details

def initialize
@details = {
:size => “”,
:fill_color => “”,
:line_color => “”,
:line_width => “”
}
end

def size
@details[:size]
end

def size=(size)
@details[:size] = size
end

def fill_color
@details[:fill_color]
end

def fill_color=(fill_color)
@details[:fill_color] = fill_color
end

def line_color
@details[:line_color]
end

def line_color=(line_color)
@details[:line_color] = line_color
end

def line_width
@details[:line_width]
end

def line_width=(line_width)
@details[:line_width] = line_width
end
end

my_shape = Shape.new

shape_details = {
:size => ‘small’,
:fill_color => ‘red’,
:line_color => ‘black’,
:line_width => ‘2’
}

my_shape.details.merge!(shape_details)

-Jeremy

C’mon, this is ruby! Revel in it!

class Shape
attr_reader :details
def self.keys
[:size, :fill_color, :line_color, :line_color]
end

def initialize
@details = {}
self.class.keys.each {|key| @details[key] = “” }
end

keys.each do |key|
define_method key do
@details[key]
end

define_method "#{key}=" do |input|
  @details[key] = input
end

end
end

my_shape = Shape.new

shape_details = {
:size => ‘small’,
:fill_color => ‘red’,
:line_color => ‘black’,
:line_width => ‘2’
}

my_shape.details.merge!(shape_details)

On 10/30/2010 07:28 AM, Andrew W. wrote:

self.class.keys.each {|key| @details[key] = "" }

end

my_shape.details.merge!(shape_details)

Excellent point! Thanks!

-Jeremy

On Sat, Oct 30, 2010 at 9:27 PM, Greg W. [email protected]
wrote:

myobject.send(iname, ivalue)

but that doesn’t work. I could maybe force it to work by manually
creating setter methods, but that’s not an elegant solution either, I’d
rather have the occassional use of instance_variable_set than craft
setters.

Anyway, not a biggie, just a curiosity.

Crafting setters is pretty simple, though:

class A
attr_writer :a
end

a = A.new
a.a = 3

Jesus.

Appreciate the effort, but the point has been lost. Don’t focus on the
hash or mass assignment, that was just a device to create an example.

The question is whether there’s a shorter way to do this:

my_object.send(:instance_variable_set, “@#{iname}”, ivalue)

In another language I used, I could simply do the equivalent of

myobject.iname = ivalue

that’s because the syntax was like this
#myobject->#iname = ivalue

Where # denotes a local var, so it was obvious to the parser that #iname
was not a method name, whereas with Ruby it’s not obvious.

I was hoping in Ruby that at least this was possible:

myobject.send(iname) = ivalue
–or–
myobject.send(iname, ivalue)

but that doesn’t work. I could maybe force it to work by manually
creating setter methods, but that’s not an elegant solution either, I’d
rather have the occassional use of instance_variable_set than craft
setters.

Anyway, not a biggie, just a curiosity.

On Sat, Oct 30, 2010 at 1:27 PM, Greg W. [email protected]
wrote:

The question is whether there’s a shorter way to do this:

my_object.send(:instance_variable_set, “@#{iname}”, ivalue)

In another language I used, I could simply do the equivalent of

myobject.iname = ivalue

This breaks the encapsulation that objects are supposed to provide for
their
hidden state. I have to say I like the fact that
instance_variable_get/instance_variable_set are long and unwieldy. By
using
them you hopefully remind yourself that you’re doing something naughty
by
touching an object’s private parts.

I was hoping in Ruby that at least this was possible:

myobject.send(iname) = ivalue
–or–
myobject.send(iname, ivalue)

but that doesn’t work. I could maybe force it to work by manually
creating setter methods, but that’s not an elegant solution either, I’d
rather have the occassional use of instance_variable_set than craft
setters.

It’s Ruby, a lot is possible! You could add some method_missing magic
so:

myobj.iv_foobar = 42

thunks to:

instance_variable_set(:@foobar, 42)

I would strongly recommend against that sort of thing though.

On Oct 30, 2010, at 12:27 , Greg W. wrote:

I was hoping in Ruby that at least this was possible:

myobject.send(iname) = ivalue
–or–
myobject.send(iname, ivalue)

You’re confusing methods and variables. They’re not the same, but you’re
on the right track with the code above, but you’d be calling the getter,
not the setter. Check it:

class X
attr_accessor :x # creates x and x= methods
end

o = X.new
o.send(“x=”, 42)
p x

Ryan D. wrote in post #958245:

On Oct 30, 2010, at 12:27 , Greg W. wrote:

I was hoping in Ruby that at least this was possible:

myobject.send(iname) = ivalue
–or–
myobject.send(iname, ivalue)

You’re confusing methods and variables. They’re not the same, but you’re
on the right track with the code above, but you’d be calling the getter,
not the setter. Check it:

class X
attr_accessor :x # creates x and x= methods
end

o = X.new
o.send(“x=”, 42)
p x

I wasn’t confusing them, I was hoping Ruby’s send would be flexible
enough to work with both of them (in conjunction with the accessors
being defined). What I didn’t think of in this context was Ruby’s x=
being considered the setter method and not just x, which I knew, and
should have recognized – so yep, that was the ticket. You win! :slight_smile:
Sorry, no prizes :frowning:

Thanks.

– gw

On 30.10.2010 22:11, Greg W. wrote:

on the right track with the code above, but you’d be calling the getter,
I wasn’t confusing them, I was hoping Ruby’s send would be flexible
enough to work with both of them (in conjunction with the accessors
being defined). What I didn’t think of in this context was Ruby’s x=
being considered the setter method and not just x, which I knew, and
should have recognized – so yep, that was the ticket. You win! :slight_smile:
Sorry, no prizes :frowning:

Here are two other approaches using widely underused class Struct and
that differ in the way they deal with values not present in the Hash:

Shape = Struct.new :size, :fill_color, :line_color, :line_width do
def self.from_hash_1(h)
sh = new
h.each {|k,v| sh[k] = v}
sh
end

def self.from_hash_2(h)
sh = new
members.each {|m| sh[m] = h[m]}
sh
end
end

irb(main):015:0* s1 = Shape.from_hash_1(
irb(main):016:1* :size => ‘small’,
irb(main):017:1* :fill_color => ‘red’,
irb(main):018:1* :line_color => ‘black’,
irb(main):019:1* :line_width => ‘2’
irb(main):020:1> )
=> #<struct Shape size=“small”, fill_color=“red”, line_color=“black”,
line_width=“2”>
irb(main):021:0> s2 = Shape.from_hash_2(
irb(main):022:1* :size => ‘small’,
irb(main):023:1* :fill_color => ‘red’,
irb(main):024:1* :line_color => ‘black’,
irb(main):025:1* :line_width => ‘2’
irb(main):026:1> )
=> #<struct Shape size=“small”, fill_color=“red”, line_color=“black”,
line_width=“2”>

If you like that better you can as well define

def Shape(h)
sh = Shape.new

logic from above

sh
end

Then you can do

s1 = Shape(
:size => ‘small’,
:fill_color => ‘red’,
:line_color => ‘black’,
:line_width => ‘2’
)

Kind regards

robert

@Greg W.,

You realize that instance_variable_set isn’t private right?

So you can simply go:

my_object.instance_variable_set("@#{iname}", ivalue)

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