将 Ruby 对象的实例变量作为映射传递给另一个类的初始值设定项



我正在使用xml-mapping gem读取一些XML,并且我希望将映射对象提供给一些现有域类的初始化器。给定XML:

<foo bar="baz"><qux>quux</qux></foo>

我得到一个像

这样的对象
#<Foo @bar="baz", @qux="quux">

然后我想把它提供给一个域类,如:

class MyFoo
  def initialize(bar:, qux:)
    # ... etc.
  end
end

(注意,在MyFoo中,属性是只读的,并且在初始化器中有一些验证和转换,所以这不是简单地将实例变量从一个复制到另一个的问题。)

我尝试将实例变量转换为映射,因此:

foo.instance_variables.map { |name| [name, foo.instance_variable_get(name)] }.to_h

生产:

{ :@bar->"baz", :@qux->"quux" }

这个几乎是我需要的MyFoo初始化器,但不完全是——我需要的是

{ :bar->"baz", :qux->"quux" }

是否有一种方法可以将实例变量名称转换为符号而不需要 @ -符号?

或者,是否有一种更简单的方法来说"从这个对象的所有属性初始化自己"?

Andrey的注释工作得很好,但是我不喜欢直接依赖实例变量。我建议在您的Foo类中添加自定义to_h方法。您甚至可以将其与以下xml-mapping联系起来:

class Foo
  # ...
  def self.__fields__
    @__fields__ ||= all_xml_mapping_nodes.map { |r| r.instance_variable_get(:@attrname) }
  end
  def to_h
    self.class.__fields__.each_with_object({}) do |field, acc|
      acc[field] = send(field)
    end
  end  
end

那么你可以调用MyFoo.new(foo.to_h)

编辑

作为XML::Mapping的扩展:

module XmlMappingExtensions
  def self.included(base)
    base.extend(ClassMethods)
  end
  module ClassMethods
    def __fields__
      @__fields__ ||= all_xml_mapping_nodes.map { |r| r.instance_variable_get(:@attrname) }
    end
  end
  def to_h
    self.class.__fields__.each_with_object({}) do |field, acc|
      acc[field] = send(field)
    end
  end  
end

然后include XmlMappingExtensions在你的Foo类,或:

module XML::Mapping
  # Note: this may break XML::Mapping if it is using this method
  # and there is probably a more graceful way to do this
  # but I just tried it and it seems to work fine...
  def self.included(base)
    base.send(:include, XmlMappingExtensions)
  end
end

在加载XML::Mapping之后和加载Foo类之前

相关内容

  • 没有找到相关文章

最新更新