113

In general, how can I get a reference to an object whose name I have in a string?

More specifically, I have a list of the parameter names (the member variables - built dynamically so I can't refer to them directly).

Each parameter is an object that also has an from_s method.

I want to do something like the following (which of course doesn't work...):

define_method(:from_s) do | arg |
    @ordered_parameter_names.each do | param |
        instance_eval "field_ref = @#{param}"
        field_ref.from_s(param)
    end
end
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
LK__
  • 6,515
  • 5
  • 34
  • 53

2 Answers2

204

The most idiomatic way to achieve this is:

some_object.instance_variable_get("@#{name}")

There is no need to use + or intern; Ruby will handle this just fine. However, if you find yourself reaching into another object and pulling out its ivar, there's a reasonably good chance that you have broken encapsulation.

If you explicitly want to access an ivar, the right thing to do is to make it an accessor. Consider the following:

class Computer
  def new(cpus)
    @cpus = cpus
  end
end

In this case, if you did Computer.new, you would be forced to use instance_variable_get to get at @cpus. But if you're doing this, you probably mean for @cpus to be public. What you should do is:

class Computer
  attr_reader :cpus
end

Now you can do Computer.new(4).cpus.

Note that you can reopen any existing class and make a private ivar into a reader. Since an accessor is just a method, you can do Computer.new(4).send(var_that_evaluates_to_cpus)

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Yehuda Katz
  • 28,535
  • 12
  • 89
  • 91
  • Doesn't instance_variable_get("@#{name}") return the value of the variable? I need a reference to the actual object. I ended up rewriting so instead of having many variables + an array with the names in the order I wanted, I put the parameters themselves into the array (the design decision was whether to access variables each time by searching the array or optimize by having an additional array with their names that would be used only when needed) – LK__ Jul 05 '09 at 06:07
  • Nope: instance_variable_get("@#{name}") returns the actual object. – Yehuda Katz Jul 05 '09 at 06:37
  • Uncovering the dead thread for a little bit of clarity. The complete example would be: class Computer attr_read :cpus def new(cpus) @cpus = cpus end end ? – Nicolas de Fontenay Aug 05 '13 at 16:50
  • "However, if you find yourself reaching into another object and pulling out its ivar, , there's a reasonably good chance that you have broken encapsulation.". How do we break into another object's ivar ?. – RubyMiner Nov 19 '15 at 18:32
10

To get an instance variable from the name of an instance variable do:

name = "paramName"
instance_variable_get(("@" + name).intern)

This will return the value of the instance variable @paramName

Daniel Lucraft
  • 7,196
  • 6
  • 34
  • 35
  • While creating a class dynamically, I have an array of the instance variable names (I need this list because I need to handle them in a certain order). By using this name, I want to get a reference to the variable. – LK__ Jul 02 '09 at 14:44
  • 1
    I have a string "paramName" and need a reference to @paramName – LK__ Jul 02 '09 at 14:45
  • 1
    OK. I recommend you edit your question to say exactly those two comments. – Daniel Lucraft Jul 02 '09 at 14:53
  • 2
    Also, this has got nothing to do with deserialization. A better title might be "Get the value of an instance variable given its name" or something. – Daniel Lucraft Jul 02 '09 at 14:54
  • You can always do a Module and add `:attr_reader varname` so you can then access the variables in a cleaner, less verbose manner. – ocodo Dec 18 '12 at 08:18