2009-10-23 16 views
14

voglio fare quanto segue:Dichiarazione di variabili di istanza che iterano su un hash!

Voglio dichiarare le variabili di istanza di una classe che itera su un dizionario.

Supponiamo che ho questo hash

hash = {"key1" => "value1","key2" => "value2","key3" => "value3"} 

e voglio avere ogni tasto come variabile di istanza di una classe. Voglio sapere se potrei dichiarare le variabili che iterano su quell'hash. Qualcosa di simile a questo:

class MyClass 
    def initialize() 
    hash = {"key1" => "value1","key2" => "value2","key3" => "value3"} 
    hash.each do |k,v| 
     @k = v 
    end 
    end 
end 

So che questo non funziona! Metto solo questo pezzo di codice per vedere se riesci a capire cosa voglio più chiaramente.

Grazie!

risposta

27
class MyClass 
    def initialize() 
    hash = {"key1" => "value1","key2" => "value2","key3" => "value3"} 
    hash.each do |k,v| 
     instance_variable_set("@#{k}",v) 
     # if you want accessors: 
     eigenclass = class<<self; self; end 
     eigenclass.class_eval do 
     attr_accessor k 
     end 
    end 
    end 
end 

L'eigenclass è una classe speciale appartenente solo ad un singolo oggetto, quindi i metodi definiti ci saranno metodi di tale oggetto istanza ma non appartengono ad altre istanze di classe normale dell'oggetto.

+5

Chuck, hai dimenticato il prefisso @ -sign ... –

+3

E ora non l'ho fatto. – Chuck

4

La risposta di Chuck è migliore dei miei ultimi due tentativi. L'eigenclass non è self.class come avevo pensato; ci è voluto un test migliore di quello che avevo scritto per rendermene conto.

Utilizzando il mio vecchio codice, ho provato nel seguente modo e ha scoperto che la classe è stato effettivamente manipolato e non l'istanza:

a = MyClass.new :my_attr => 3 
b = MyClass.new :my_other_attr => 4 

puts "Common methods between a & b:" 
c = (a.public_methods | b.public_methods).select { |v| a.respond_to?(v) && b.respond_to?(v) && !Object.respond_to?(v) } 
c.each { |v| puts " #{v}" } 

L'uscita era:

Common methods between a & b: 
    my_other_attr= 
    my_attr 
    my_attr= 
    my_other_attr 

Questo smentisce chiaramente la mia presupposto. Le mie scuse Chuck, voi erano giusto tutto il tempo. risposta

Older:

attr_accessor funziona solo quando valutato in una definizione di classe, non è l'inizializzazione di un'istanza. Pertanto, l'unico metodo per fare direttamente ciò che si vuole è quella di utilizzare instance_eval con una stringa:

class MyClass 
    def initialize(params) 
    #hash = {"key1" => "value1","key2" => "value2","key3" => "value3"} 
    params.each do |k,v| 
     instance_variable_set("@#{k}", v) 
     instance_eval %{ 
     def #{k} 
      instance_variable_get("@#{k}") 
     end 
     def #{k}= (new_val) 
      instance_variable_set("@#{k}", new_val) 
     end 
     } 
    end 
    end 
end 

Per verificare questa prova:

c = MyClass.new :my_var => 1 
puts c.my_var 
+1

'self.class' è ** non ** l'eigenclass. La modifica di 'self.class' influenzerà ogni oggetto appartenente alla classe, mentre la modifica di una eigenclass di un oggetto avrà effetto solo su quell'oggetto.In alcuni casi sono intercambiabili, ma non in generale. – Chuck

+0

A partire dal 1.8.7, l'ho provato e ho trovato la tua affermazione falsa. Tuttavia, questa è l'unica versione che ho provato. –

+0

Quindi il tuo test era difettoso. Esegui: '" Hello ".class.class_eval {def size() 9000 end}; mette "Bye" .size' e vedrai che sovrascrivendo il metodo in "Hello" .class ha influenzato anche il comportamento della stringa completamente indipendente "Bye". Questo è vero in qualsiasi versione di Ruby che potresti incontrare. – Chuck

4
class MyClass 
    def initialize 
    # define a hash and then 
    hash.each do |k,v| 
     # attr_accessor k # optional 
     instance_variable_set(:"@#{k}", v) 
    end 
    end 
end 
+0

grazie per quell'osservazione facoltativa! ma l'attr_accessor nel BLOCK iteratore hash non funziona ... Come posso farlo? (crea attr_accessors iterating) – flyer88

+0

Per fare un 'attr_accessor', devi essere in un contesto di classe. Il modo per ottenere un contesto di classe per un'istanza è utilizzare l'eigenclass dell'oggetto come nella mia risposta. – Chuck

Problemi correlati