54

How can I set an object attribute dynamically in Ruby e.g.

def set_property(obj, prop_name, prop_value)
    #need to do something like > obj.prop_name = prop_value 

    #we can use eval but I'll prefer a faster/cleaner alternative:
    eval "obj.#{prop_name} = #{prop_value}"
end

4 Answers 4

98

Use send:

def set_property(obj, prop_name, prop_value)
    obj.send("#{prop_name}=",prop_value)
end
Sign up to request clarification or add additional context in comments.

5 Comments

If you are use 1.9.2 you should call public_send instead of send as send can call private methods of an object as well see [joshstaiger.org/archives/2006/12/the_ruby_send_h.html]
This does not work for setting a property dynamically. Doing obj.send("foo=", "bar") will break unless the foo= method is already defined (by an attribute accessor or explicit method definition). All send does is invoke an existing method using the specified arguments. See: ruby-doc.org/core-1.9.3/Object.html#method-i-send
what if the prop_name is nested key ? foo.bar
In a One to Many relationship, eg: Posts have many Comments, how to dynamically push the depedent instance to the independent? Normally, we do like this on rails: @post.comments << @comment, how to turn it dynamic? I know that ActiveModel automatically updates the independent when I set it on the dependent, but just for curiosity :)
wouldn't public_send be safer?
17

Object#instance_variable_set() is what you are looking for, and is the cleaner version of what you wanted.

Example:

your_object = Object.new
your_object.instance_variable_set(:@attribute, 'value')
your_object
# => <Object:0x007fabda110408 @attribute="value">

Ruby documentation about Object#instance_variable_set

3 Comments

Assuming that the OP really wants to set an instance variable rather than calling the mutator method. The question appears to be conflating the two but the example code at least is calling the mutator method.
@mu-is-too-short, Sure, but instance_variable_set() really sets an instance variable for a variable name that is determined at run-time. In that sense, it has the same effect as in the question, but a cleaner / clearer alternative.
Not necessarily. Directly setting an instance variable and calling a mutator method aren't always the same thing; the method can alter/check the incoming value, the method may store the value somewhere else, there might not even be an instance variable behind the mutator, ... That's all I'm saying.
9

If circumstances allow for an instance method, the following is not overly offensive:

class P00t
  attr_reader :w00t

  def set_property(name, value)
    prop_name = "@#{name}".to_sym # you need the property name, prefixed with a '@', as a symbol
    self.instance_variable_set(prop_name, value)
  end
end

Usage:

p = P00t.new
p.set_property('w00t', 'jeremy')

Comments

5

This answer (https://stackoverflow.com/a/7466385/6094965) worked for me:

Object.send(attribute + '=', value)

attribute has to be a String. So if you are iterating trough an array of Symbols (like me), you can use to_s.

Object.send(attribute.to_s + '=', value) 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.