动态设置 Ruby 对象属性

53

我如何在Ruby中动态设置对象属性,例如:

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个回答

97

使用 send 方法:

def set_property(obj, prop_name, prop_value)
    obj.send("#{prop_name}=",prop_value)
end

2
如果您使用的是1.9.2版本,则应该调用public_send而不是send,因为send也可以调用对象的私有方法,请参见[http://www.joshstaiger.org/archives/2006/12/the_ruby_send_h.html]。 - Moiz Raja
20
对于动态地设置属性,这种方法不起作用。 如果没有定义foo=方法(通过属性访问器或显式方法定义),执行obj.send("foo=", "bar")将失败。 send只是使用指定的参数调用现有方法。请参见:http://ruby-doc.org/core-1.9.3/Object.html#method-i-send。 - wyattisimo
如果 prop_name 是嵌套键(nested key)怎么办?例如 foo.bar - user181452
在一对多的关系中,例如:帖子有很多评论,如何动态地将从属实例推送到独立实例?通常,在Rails上我们会这样做:@post.comments << @comment,如何使其变得动态?我知道ActiveModel在我将其设置为从属实例时会自动更新独立实例,但只是出于好奇 :) - João Ramires
“public_send”会更安全吗? - molexi

16
Object#instance_variable_set()是您正在寻找的内容,并且是您想要的更清晰的版本。
示例:
your_object = Object.new
your_object.instance_variable_set(:@attribute, 'value')
your_object
# => <Object:0x007fabda110408 @attribute="value">

Ruby文档关于Object#instance_variable_set的说明。


假设 OP 真的想设置一个实例变量而不是调用 mutator 方法。问题似乎混淆了这两个概念,但至少示例代码正在调用 mutator 方法。 - mu is too short
@mu-is-too-short,当然可以,但是instance_variable_set()实际上是在运行时为一个变量名设置实例变量。从这个意义上讲,它具有与问题中相同的效果,但是更清晰、更简洁的替代方法。 - Jochem Schulenklopper
1
不一定。直接设置实例变量和调用修改器方法并不总是相同的;该方法可以更改/检查传入的值,该方法可能在其他地方存储该值,甚至可能没有实例变量在修改器后面...这就是我想说的。 - mu is too short

9

如果情况允许使用实例方法,则以下内容不算过分:

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

使用方法:

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

5
这个答案 (https://stackoverflow.com/a/7466385/6094965) 对我很有帮助:
Object.send(attribute + '=', value)

attribute 必须是一个 String。所以如果你正在遍历一个 Symbol 数组(像我一样),你可以使用 to_s

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接