2010-01-07 4 views
8

Necesito una clase que tenga un método "to_s" semiautomático (para generar XML de hecho). quisiera iterar a través de todos los métodos automáticos establecidos en mi línea 'attr_accessor':Ruby: métodos generados attr_accessor - cómo iterarlos (en to_s - formato personalizado)?

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

c=MyClass.new 

Hasta el momento estoy haciendo un básica:

c.methods - Object.methods 

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

estoy frente a algunos desafíos:

  1. 'id' puede causar un ligero dolor de cabeza, ya que Object parece tener un 'id'.
  2. La llamada 'c.methods' anterior, devuelve cadenas - ¿No obtengo ningún otro metadato? (En Java 'método' es un objeto, donde podría realizar más reflexión).
  3. Tengo relaciones de uno a muchos con los que tengo que lidiar ('c' es un tipo de matriz de otros tipos de objetos).

Esto es lo que trato de hacer: quiero diseñar un Objeto simple que tenga un 'to_s' que construirá un fragmento de XML: por ejemplo.

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

Y luego heredarán mis datos clases de ese objeto simple: para que (con suerte) me siento un mechansim para construir todo un documento XML.

Estoy seguro de que estoy volviendo a inventar la rueda aquí también ... por lo que otros enfoques probados son bienvenidos.

Respuesta

13

Para conseguir los objetos de método de una cadena, puede utilizar los métodos method o instance_method (donde method se llamaría en un objeto y instance_method en una clase). La única información interesante que le da es arity, sin embargo (a diferencia de Java, donde también le daría los tipos del valor de retorno y los argumentos, que por supuesto no es posible en ruby). Su título sugiere que solo desea iterar sobre los métodos creados por attr_accessor, pero su código repetirá todos los métodos definidos en su clase, lo que podría convertirse en un problema .

para deshacerse de ese problema y el problema con id, podría utilizar su propia envoltura alrededor de attr_accessor que almacena las variables que se crea descriptores de acceso para, de este modo:

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 

Para el problema 3 sólo se puede hacer

if item.is_a? Array 
    do_something 
else 
    do_something_else 
end 
+0

Interesante Simon - necesidad de pasar por esto antes de que pueda votar! Gracias! – monojohnny

+0

Esto definitivamente ha ayudado a +1 - pero el problema principal aquí, es que realmente quiero retomar las cosas definidas por attr_accessor realmente. – monojohnny

+0

Han aclarado la descripción un poco también para indicar esto. – monojohnny

0

Utilizo esta técnica para convertir objetos personalizados a JSON. Puede ser el fragmento a continuación será útil ya que la pregunta era para la implementación to_xml.

Aquí hay un poco de magia usando self.included en un módulo. Aquí hay un artículo muy bueno de 2006 sobre el módulo que tiene métodos de instancia y clase http://blog.jayfields.com/2006/12/ruby-instance-and-class-methods-from.html

El módulo está diseñado para incluirse en cualquier clase para proporcionar la funcionalidad to_json. Intercepta el método attr_accessor en lugar de usar el suyo para requerir cambios mínimos para las clases existentes.

to_json aplicación se basa en este 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 
} 
Cuestiones relacionadas