2012-04-04 6 views
18
durch attr_reader oder attr_accessor definiert wurden

Angenommen, ich habe eine Klasse AWie Attribute zu erhalten, die

class A 
    attr_accessor :x, :y 

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

Wie kann ich x und y Attribute erhalten, ohne zu wissen, wie genau sie wurden benannt.

z.

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

Antwort

24

Verwenden Sie Introspektion, 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

Hinweis: attrs wird * alle zurückkehren * Instanzvariablen, und nicht nur die, die von 'attr_accessor' offengelegt werden. – Jonah

+0

@Jonah: Ja, das war die Annahme damals. Für eine genauere Methode kann man auf [diese Antwort] verweisen (http://stackoverflow.com/a/34440466/125816). –

1

wenn Sie attr_accessor verwenden Attribute in einer Klasse zu definieren, Rubin refexion verwenden, ein paar Methoden definieren, für jedes Attribut erklärt, man den Wert zu erhalten und andere zu setzen, eine Instanzvariable mit dem gleichen Namen mit

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

so dass diese Attribute sind erreichbar, außerhalb der Klasse, mit

des Attributs

können Sie diese Methoden sehen

oder innerhalb der Klasse durch die entsprechende Instanz-Variable

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

Vielen Dank für Ihre Antwort, aber ich fragte nach einem Fall, wenn ich die Namen der Attribute nicht kenne, weil es sonst nicht DRY Ansatz wäre.Zum Beispiel, was passiert, wenn ich beschließe, zusätzliche Attribute '@ d',' @ e', '@ f' hinzuzufügen. Dann muss ich sie sowohl in der 'attributes'-Methode als auch in' initialize' hinzufügen, was zur Duplizierung von Code führt. – user1179942

+0

In dieser Situation verwenden Sie die Sergio-Lösung – pgon

12

Während Sergio Antwort hilft, wird es alle Instanzvariablen zurückgeben, die, wenn ich mich richtig die Frage des OP zu verstehen, ist nicht das, was gefragt wird.

Wenn Sie nur die Attribute zurückgeben möchten, die z. ein Mutator, haben Sie etwas etwas komplizierter, wie zu tun:

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 

Diese gibt nur die Attribute deklariert mit attr_accessor (oder mit einem manuell erstellt Mutator), und halten Sie die internen Instanzvariablen versteckt. Sie können etwas ähnliches tun, wenn Sie die mit attr_reader deklarierten wollen.

+0

Ich denke, das ist eine bessere Antwort auf die Frage gegeben. Ich werde nur daran anhängen. Ruby erstellt {Variable} = Methoden, wenn ein Symbol als Accessor oder Writer hinzugefügt wurde. Antworten auf? stellt nur sicher, dass eine gültige Methode mit dem Namen {Variable} = existiert. Der erste Teil besteht darin, das erste "@" für die zurückgegebene Variable zu entfernen. Im OP-Beispiel werden die Methoden "x =" und "y =" erstellt, und Sie suchen nach ihnen mit @x; x = und @y; y = –

+0

In einigen Fällen enthält dies ': Attribute = '. : / – XtraSimplicity

3

Siehe dieses andere Stack Overflow Question. Sie überschreiben 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 

Dies kann nicht der DRY-est sein, aber wenn Sie besorgt sind sich darin eine Klasse für (zu einer Basisklasse, im Gegensatz, dass alles von erbt), dann sollte diese Arbeit.

0

Wenn Sie attr_writer s haben/attr_accessor s auf Ihre Attribute definiert, als können sie leicht durch Anpassung der =$ regexp abgerufen werden:

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

ODER

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