【问题标题】:Ruby - dynamically add property to class (at runtime) [duplicate]Ruby - 动态地将属性添加到类(在运行时)[重复]
【发布时间】:2011-03-08 11:45:06
【问题描述】:

我正在寻找一种在运行时将属性添加到我已经定义的类的方法,或者更好:

class Client
    attr_accessor :login, :password
    def initialize args = {}
        self.login    = args[:login]
        self.password = args[:password]
    end
end

但是,我有这个哈希

{:swift_bic=>"XXXX", :account_name=>"XXXX", :id=>"123", :iban=>"XXXX"} 

我希望这个哈希成为我的客户端实例的一部分,比如

client = Client.new :login => 'user', :password => 'xxxxx'

然后用神奇的魔法

client @@%$%PLIM!!! {:swift_bic=>"XXXX", :account_name=>"XXXX", :id=>"123", :iban=>"XXXX"} 

我可以访问

client.swift_bic => 'XXXX'
client.account_name => 'XXXX'
client.id => 123

而且我还想保持适当的对象结构,例如:

Client.new(:login => 'user', :password => 'xxxxx').inspect
#<Client:0x1033c4818 @password='xxxxx', @login='user'>

魔法之后

client.inspect
#<Client:0x1033c4818 @password='xxxxx', @login='user', @swift_bic='XXXX', @account_name='XXXX' @id => '123', @iban => 'XXXX'>

这会给我一个非常漂亮且格式正确的 json

有可能吗?

我从 web 服务获取这个哈希,所以我不知道那里是否有一个新属性,然后我必须在他们每次对其服务执行升级时更新我的​​应用程序。 所以,我有点想避免这种情况:/

谢谢各位。

:)

【问题讨论】:

标签: ruby class dynamic hash properties


【解决方案1】:

method_missing 方法可行,但如果您在添加访问器后要大量使用它们,不妨将它们添加为真正的方法,如下所示:

class Client
  def add_attrs(attrs)
    attrs.each do |var, value|
      class_eval { attr_accessor var }
      instance_variable_set "@#{var}", value
    end
  end
end

这将使它们像普通实例变量一样工作,但仅限于一个client

【讨论】:

  • 哈哈!!,神奇的魔法存在,非常感谢 mckeed,正是我想要的!欢呼
  • 默认情况下对我不起作用(我尝试了 1.8.7 和 1.9.1)我通过在 self 的单例类上调用 class_eval 让它工作。都是一样的,除了 line 3 变成:(class &lt;&lt; self ; self ; end).class_eval { attr_accessor var } 另一种写法是(class &lt;&lt; self ; self ; end).send :attr_accessor , var
  • 开枪,好球。这确实需要 ActiveSupport,就像我写的那样。
【解决方案2】:

我认为最好的解决方案是 mckeed 的

但这里还有一个需要考虑的想法。如果你愿意,你可以继承 OpenStruct:

require 'ostruct'

class Client < OpenStruct
  def initialize args = {}
    super
  end
  def add_methods( args = Hash.new )
    args.each do |name,initial_value|
      new_ostruct_member name
      send "#{name}=" , initial_value
    end
  end
end

client = Client.new :login => 'user', :password => 'xxxxx'
client.add_methods :swift_bic=>"XXXX", :account_name=>"XXXX", :iban=>"XXXX" , :to_s => 5
client # => #<Client login="user", password="xxxxx", swift_bic="XXXX", account_name="XXXX", iban="XXXX", to_s=5>

client.swift_bic      # => "XXXX"
client.account_name   # => "XXXX"

不过,此解决方案存在两个问题。 OpenStruct 使用method_missing,所以如果你定义一个像id 这样的方法,在1.8 上它会去寻找object_id 而不是找到你的方法。

第二个问题是它使用了一些关于如何实现 OpenStruct 的私有知识。所以将来可能会改变它,破坏这个代码(为了记录,我检查了 1.8.7 - 1.9.2 并且这是兼容的)

【讨论】:

    【解决方案3】:

    看看Object.method_missing。每当使用未定义的方法调用您的对象时,都会调用此方法。您可以定义此函数并使用它来检查未定义的方法与您的哈希值之一的名称。如果匹配,则返回哈希值。

    您还可以定义自己的inspect 函数并生成一个输出字符串,其中包含您希望它包含的任何内容。

    【讨论】:

      【解决方案4】:

      我将为此建议反对 method_missing - 这会创建许多“神奇”功能,如果不通过 method_missing 正文,就无法轻松记录或理解这些功能。相反,按照建议查看 OpenStruct - 您甚至可以创建自己的继承自它的类,例如:

      class Client < OpenStruct
        ...
      end
      

      您将能够使用收到的任何哈希值初始化客户端。

      【讨论】:

        猜你喜欢
        • 2017-04-22
        • 2011-01-15
        • 1970-01-01
        • 2017-10-14
        • 2019-04-28
        • 2013-03-27
        • 1970-01-01
        相关资源
        最近更新 更多