Ruby:attr_accessor生成的方法 - 如何迭代它们(在to_s中 - 自定义格式)?

时间:2010-01-07 12:24:18

标签: xml ruby reflection

我需要一个具有半自动'to_s'方法的类(实际上生成XML)。 我想遍历在'attr_accessor'行中设置的所有自动方法:

class MyClass
    attr_accessor :id,:a,:b,:c
end

c=MyClass.new

到目前为止,我正在做一个基本的事情:

c.methods - Object.methods

=> ["b", "b=", "c", "c=", "id=", "a", "a="]

我面临一些挑战:

  1. 'id'可能会引起轻微的头痛 - 因为Object似乎已经有'id'。
  2. 上面的'c.methods'调用,返回字符串 - 我没有得到任何其他元数据? (在Java'方法'中是一个对象,我可以在其中进行进一步的反射。)
  3. 我必须处理一对多关系('c'是其他对象类型的数组类型)。
  4. 这就是我想要做的:我想设计一个简单的Object,它有一个'to_s',它将构建一个XML片段:例如。

    <id> 1 </id>
    <a> Title </a>
    <b> Stuff </b>
    <c>
        <x-from-other-object>
        <x-from-other-object>
        ....
    </c>
    

    然后从这个简单的对象继承我的数据类:这样(希望)我得到一个mechansim来构建一个完整的XML文档。

    我确信我也在这里重新发明轮子......所以欢迎其他久经考验的方法。

2 个答案:

答案 0 :(得分:13)

要从字符串中获取方法对象,可以使用方法methodinstance_method(在对象上调用method,在类上调用instance_method) 。它给你的唯一有趣的信息是arity(与java相反,它也会给你返回值和参数的类型,当然这在ruby中是不可能的)。

您的标题表明您只想迭代attr_accessor创建的方法,但您的代码将迭代您的类中定义的每个方法,如果您想添加其他非访问方法,这可能会成为一个问题到你的班级。

要解决这个问题以及id的问题,你可以使用你自己的attr_accessor包装器来存储它为其创建访问器的变量,如下所示:

module MyAccessor
  def my_attr_accessor *attrs
    @attrs ||= []
    @attrs << attrs
    attr_accessor *attrs
  end

  def attrs
    @attrs
  end
end

class MyClass
  extend MyAccessor
  my_attr_accessor :id,:a,:b,:c

  def to_s
    MyClass.attrs.each do |attr|
      do_something_with(attr, send(attr))
    end
  end
end

问题3你可以做到

if item.is_a? Array
  do_something
else
  do_something_else
end

答案 1 :(得分:0)

我使用此技术将自定义对象转换为JSON。可能是下面的代码段有用,因为问题是to_xml实施。

在模块中使用self.included有一点魔力。这是2006年关于模块同时具有实例和类方法http://blog.jayfields.com/2006/12/ruby-instance-and-class-methods-from.html

的非常好的文章

该模块旨在包含在任何类中以提供to_json功能。它拦截attr_accessor方法而不是使用它自己的方法,以便对现有类进行最小的更改。

to_json实施基于此answer

module JSONable
  module ClassMethods
    attr_accessor :attributes

    def attr_accessor *attrs
      self.attributes = Array attrs
      super
    end
  end

  def self.included(base)
    base.extend(ClassMethods)
  end

  def as_json options = {}
    self.class.attributes.inject({}) do |hash, attribute|
      hash[attribute] = self.send(attribute)
      hash
    end
  end

  def to_json *a
    as_json.to_json *a
  end
end


class CustomClass
  include JSONable
  attr_accessor :b, :c 

  def initialize b: nil, c: nil
    self.b, self.c = b, c
  end
end

a = CustomClass.new(b: "q", c: 23)
puts JSON.pretty_generate a

{
  "b": "q",
  "c": 23
}