rails attr_reader attr_accessor ruby metaprogramming

ruby - rails - Cómo obtener atributos que se definieron a través de attr_reader o attr_accessor



attr_reader rails 5 (6)

Supongamos que tengo una clase A

class A attr_accessor :x, :y def initialize(x,y) @x, @y = x, y end end

¿Cómo puedo obtener los atributos x e y sin saber exactamente cómo fueron nombrados?

P.ej

a = A.new(5,10) a.attributes # => [5, 10]


¡Usa la introspección, 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]


Cuando usa attr_accessor para definir atributos en una clase, Ruby usa refexion, define un par de métodos, para cada atributo declarado, uno para obtener el valor y otro para establecer, una variable de instancia con el mismo nombre del atributo

Puedes ver estos métodos usando

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?,..

Así que estos atributos son accesibles, fuera de la clase, con

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

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

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


Si bien la respuesta de Sergio ayuda, devolverá todas las variables de instancia, que si entiendo correctamente la pregunta del OP, no es lo que se pregunta.

Si desea devolver solo los ''atributos'' que tienen, por ejemplo, un mutador, debe hacer algo un poco más complicado, 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 solo los atributos declarados con attr_accessor (o con un mutador creado manualmente) y mantiene las variables de instancia internas ocultas. Puedes hacer algo similar si quieres los declarados con attr_reader.


Si tiene attr_writer s / attr_accessor s definido en sus atributos, puede recuperarlos fácilmente haciendo coincidir el =$ regexp:

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) }


Ver esta otra pregunta de desbordamiento de pila . 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


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

Puede que este no sea el DRY-est, pero si solo está preocupado por hacer esto para una clase (a diferencia de una clase base de la que todo hereda), esto debería funcionar.