web-dev-qa-db-fra.com

Comment obtenir des attributs définis via attr_reader ou attr_accessor

Supposons que j'ai une classe A

class A
  attr_accessor :x, :y

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

Comment obtenir les attributs x et y sans savoir exactement comment ils ont été nommés. 

Par exemple.

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

Utilisez l'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]
30
Sergio Tulentsev

Bien que la réponse de Sergio soit utile, elle renverra toutes les variables d'instance, ce qui, si j'ai bien compris la question du PO, n'est pas ce qui est demandé.

Si vous souhaitez renvoyer uniquement les "attributs" qui ont, par exemple, un mutateur, vous devez faire quelque chose de légèrement plus compliqué, tel que:

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

Cela retourne uniquement les attributs déclarés avec attr_accessor (ou avec un mutateur créé manuellement), et garde les variables d'instance internes masquées. Vous pouvez faire quelque chose de similaire si vous voulez ceux déclarés avec attr_reader.

13
Nicolas Bonnefon
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

Ce n'est peut-être pas le type DRY-est, mais si vous ne l'utilisez que pour une classe (par opposition à une classe de base dont tout hérite), cela devrait fonctionner.

6
marksiemers

Voir cet autre Stack Overflow Question . Ils surchargent 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
3
reto

lorsque vous utilisez attr_accessor pour définir des attributs dans une classe, Ruby en utilisant refexion, définissez deux méthodes, pour chaque attribut déclaré, une pour obtenir la valeur et l'autre pour la définir, une variable d'instance du même nom de l'attribut

vous pouvez voir ces méthodes en utilisant

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

Donc, ces attributs sont accessibles, en dehors de la classe, avec

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

ou à l'intérieur de la classe à travers la variable d'instance correspondante

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

Si vous avez défini attr_writers/attr_accessors sur vos attributs, ils peuvent être facilement récupérés en faisant correspondre l'expression =$:

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

OR

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