2012-04-04 5 views

Respuesta

24

Use introspection, Luke!

class A 
    attr_accessor :x, :y 

    def initialize(*args) 
    @x, @y = args 
    end 

    def attrs 
    instance_variables.map{|ivar| instance_variable_get ivar} 
    end 
end 

a = A.new(5,10) 
a.x # => 5 
a.y # => 10 
a.attrs # => [5, 10] 
+2

Nota: attrs volverá * todos * variables de instancia, en lugar de solo las expuestas por 'attr_accessor' – Jonah

+0

@Jonah: sí, esa era la suposición de entonces. Para un método más preciso, uno puede referirse a [esta respuesta] (http://stackoverflow.com/a/34440466/125816). –

1

cuando se utiliza attr_accessor para definir los atributos de una clase, Ruby utilizando refexión, definir un par de métodos, para cada atributo declarado, uno para obtener el valor y la otra para establecer, una variable de instancia del mismo nombre del atributo

esto se puede ver utilizando métodos

p A.instance_methods 

[:x, :x=, :y, :y=, :nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?,.. 

por lo que este atributos son accesibles, fuera de la clase, con

p "#{a.x},#{a.y}" 

o dentro de la clase a través de la instancia correspondiente variable de

class A 
    ... 
    def attributes 
    [@x,@y] 
    end 
    ... 
end 
p a.attributes #=> [5,10] 
+3

Gracias por su respuesta, pero pregunté sobre un caso cuando no sé los nombres de los atributos, porque de lo contrario no sería un enfoque SECO.Por ejemplo, ¿qué sucede si decido agregar atributos adicionales '@ d',' @ e', '@ f'. Luego tendré que agregarlos en el método 'attributes' a la vez que en' initialize', lo que conduce a la duplicación del código. – user1179942

+0

En esta situación, use la solución de Sergio – pgon

12

Si bien la respuesta de Sergio ayuda, devolverá todas las variables de instancia, que si he entendido bien la pregunta del PO, no es lo que se pide.

Si desea devolver solo los 'atributos' que tienen, p. un mutador, usted tiene que hacer algo un poco más complicado, tales como:

attrs = Hash.new 
instance_variables.each do |var| 
    str = var.to_s.gsub /^@/, '' 
    if respond_to? "#{str}=" 
    attrs[str.to_sym] = instance_variable_get var 
    end 
end 
attrs 

Esto devuelve sólo los atributos declarados con attr_accessor (o con un mutador creado manualmente), y mantener las variables de instancia internos ocultos. Puede hacer algo similar si quiere los declarados con attr_reader.

+0

Creo que esta es una mejor respuesta dada la pregunta. Voy a anexarme a eso. Ruby crea {Variable} = métodos siempre que se haya agregado un símbolo como un descriptor de acceso o escritor. ¿responder a? solo asegura que hay un método válido que existe con el {Variable} = nombre. La primera parte es quitar el primer "@" en la variable devuelta. En el ejemplo de OP, los métodos "x =" y "y =" se crearán, y usted los está buscando con @x; x = y @y; y = –

+0

En algunos casos, esto incluirá ': atributos = '. : / – XtraSimplicity

3

Ver este otro Stack Overflow Question. Anulan attr_accessor.

def self.attr_accessor(*vars) 
    @attributes ||= [] 
    @attributes.concat vars 
    super(*vars) 
    end 

    def self.attributes 
    @attributes 
    end 

    def attributes 
    self.class.attributes 
    end 
1
class A 
    ATTRIBUTES = [:x, :y] 
    attr_accessor *ATTRIBUTES 

    def initialize(x,y) 
    @x, @y = x, y 
    end 

    def attributes 
    ATTRIBUTES.map{|attribute| self.send(attribute) } 
    end 
end 

esto puede no ser el DRY-est, pero si usted es sólo se ocupa de hacer esto para una clase (en contraposición a una clase base que todo lo que se hereda de), entonces esto debería funcionar.

0

Si tiene attr_writer s/attr_accessor s definida en sus atributos, que pueden ser fácilmente recuperados, haciendo coincidir la =$ expresión regular:

A.instance_methods.each_with_object([]) { |key, acc| acc << key.to_s.gsub(/=$/, '') if key.match(/\w=$/) } 

O

A.instance_methods.each_with_object([]) { |key, acc| acc << key if key = key.to_s.match(/^(.*\w)=$/)&.[](1) } 
Cuestiones relacionadas