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.