views:

308

answers:

2

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
+2  A: 

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
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
I have a string "paramName" and need a reference to @paramName
lk
OK. I recommend you edit your question to say exactly those two comments.
Daniel Lucraft
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
+2  A: 

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)

Yehuda Katz
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
Nope: instance_variable_get("@#{name}") returns the actual object.
Yehuda Katz