Ruby - 动态地向类添加属性(在运行时)

时间:2010-06-25 21:40:33

标签: ruby class dynamic hash properties

我正在寻找一种方法在运行时或更好的时候为我已定义的类添加属性:

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

有可能吗?

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

谢谢你。

:)

4 个答案:

答案 0 :(得分:15)

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

答案 1 :(得分: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,这是兼容的)

答案 2 :(得分:0)

看看Object.method_missing。只要使用未定义的方法调用对象,就会调用此方法。您可以定义此函数并使用它来根据其中一个哈希值的名称检查未定义的方法。如果匹配,则返回哈希值。

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

答案 3 :(得分:0)

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

class Client < OpenStruct
  ...
end

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