【问题标题】:Set Attribute Dynamically of Ruby Object动态设置 Ruby 对象的属性
【发布时间】:2011-10-21 12:41:03
【问题描述】:

如何在 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

【问题讨论】:

    标签: ruby dynamic


    【解决方案1】:

    使用send:

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

    【讨论】:

    • 如果你使用的是 1.9.2,你应该调用 public_send 而不是 send 因为 send 可以调用对象的私有方法,参见 [joshstaiger.org/archives/2006/12/the_ruby_send_h.html]
    • 这不适用于动态设置属性。除非已经定义了 foo= 方法(通过属性访问器或显式方法定义),否则执行 obj.send("foo=", "bar") 将中断。 send 所做的只是使用指定的参数调用现有方法。见:ruby-doc.org/core-1.9.3/Object.html#method-i-send
    • 如果prop_name 是嵌套键怎么办? foo.bar
    • 一对多关系中,例如:帖子有很多评论,如何将依赖实例动态推送到独立实例?通常,我们在 Rails 上这样做:@post.comments << @comment,如何将其变为动态?我知道当我在依赖项上设置 ActiveModel 时,它会自动更新独立项,但只是出于好奇:)
    • public_send 会不会更安全?
    【解决方案2】:

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

    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')
    

    【讨论】:

      【解决方案3】:

      Object#instance_variable_set() 是您正在寻找的,并且是您想要的更简洁的版本。

      例子:

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

      关于 Object#instance_variable_set 的 Ruby 文档

      【讨论】:

      • 假设 OP 真的想设置一个实例变量而不是调用 mutator 方法。这个问题似乎将两者混为一谈,但示例代码至少调用了 mutator 方法。
      • @mu-is-too-short,当然,但instance_variable_set() 确实为在运行时确定的变量名称设置了一个实例变量。从这个意义上说,它与问题中的效果相同,但更清晰/更清晰。
      • 不一定。直接设置实例变量和调用 mutator 方法并不总是一回事。该方法可以更改/检查传入的值,该方法可以将值存储在其他地方,甚至可能在 mutator 后面没有实例变量,......这就是我要说的。
      【解决方案4】:

      这个答案 (https://*.com/a/7466385/6094965) 对我有用:

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

      attribute 必须是 String。因此,如果您要遍历 Symbols 数组(像我一样),您可以使用 to_s

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

      【讨论】: