2012-04-04 10 views

risposta

24

Utilizzare l'introspezione, 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 tornerà * tutto * variabili di istanza, piuttosto che solo quelle esposte da 'attr_accessor' – Jonah

+0

@Jonah: sì, quella era l'ipotesi di allora. Per un metodo più preciso, si può fare riferimento a [questa risposta] (http://stackoverflow.com/a/34440466/125816). –

1

quando si utilizza attr_accessor per definire gli attributi in una classe, Ruby utilizzando refexion, definire un paio di metodi, per ogni attributo dichiarato, uno per ottenere il valore e l'altro per impostare, una variabile con lo stesso nome istanza dell'attributo

si può vedere questo metodo utilizzando

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

quindi questo attributi sono accessibili, al di fuori della classe, con

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

o all'interno della classe attraverso la corrispondente variabile di istanza

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

Grazie per la risposta, ma ho chiesto un caso in cui non conosco i nomi degli attributi, perché altrimenti non sarebbe un approccio DRY.Ad esempio, cosa succede se decido di aggiungere ulteriori attributi '@ d',' @ e', '@ f'. Poi dovrò aggiungerli nel metodo 'attributes' come pure in' initialize', che porta alla duplicazione del codice. – user1179942

+0

In questa situazione, utilizzare la soluzione di Sergio – pgon

12

Mentre la risposta di Sergio aiuta, verrà restituito tutte le variabili di istanza, che se ho capito bene la domanda del PO, non è quello che viene chiesto.

Se si desidera restituire solo gli "attributi" che hanno ad es. mutatore, devi fare qualcosa di un po 'più complicato come ad esempio:

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 

Questo restituisce solo gli attributi dichiarati con attr_accessor (o con un mutatore creata manualmente), e mantenere le variabili di istanza interne nascoste. Puoi fare qualcosa di simile se vuoi quelli dichiarati con attr_reader.

+0

Penso che questa sia una risposta migliore data la domanda. Mi limiterò ad accenderlo. Ruby crea {Variable} = metodi ogni volta che un simbolo è stato aggiunto come accessor o writer. rispondere a? si accerta solo che esista un metodo valido che esiste con {Variable} = nome. La prima parte consiste nel troncare il primo "@" sulla variabile restituita. Nell'esempio OP, i metodi "x =" e "y =" verranno creati e li stai cercando con @x; x = e @y; y = –

+0

In alcuni casi, questo includerà ': attributi = '. : / – XtraSimplicity

3

Vedi questo altro Stack Overflow Question. Hanno priorità su 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 

Questo non può essere il DRY-est, ma se siete interessati solo con il fare questo per una classe (al contrario di una classe base che tutto eredita da), allora questo dovrebbe funzionare.

0

Se hai attr_writer s/attr_accessor s definita su attributi, che possono essere facilmente recuperati abbinando la =$ 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) } 
Problemi correlati