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

我需要一个具有半自动’to_s’方法的Class(实际上生成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’方法’中是一个对象,我可以在其中进行进一步的reflection)。
  3. 我有一对多的关系我必须处理(’c’是其他对象类型的数组类型)。

这就是我想要做的:我想设计一个简单的Object,它有一个’to_s’,它将构建一个XML片段:例如。

  1   Title   Stuff     ....  

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

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

要从字符串中获取方法对象,可以使用方法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 

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

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

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

to_json实现基于这个答案

 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 }