2009-11-14 3 views
12

Sto provando a fare un instance_eval seguito da uno attr_accessor all'interno di initialize, e continuo a ricevere questo: `` initialize ': metodo non definito' attr_accessor'`. Perché non funziona?Perché non posso usare attr_accessor all'interno di initialize?

Il codice sembra un po 'come questo:

class MyClass 
    def initialize(*args) 
     instance_eval "attr_accessor :#{sym}" 
    end 
end 

risposta

20

Non è possibile chiamare attr_accessor sull'istanza, perché attr_accessor non è definito come un metodo di MyClass esempio. È disponibile solo su moduli e classi. Ho il sospetto che si desidera chiamare attr_accessor sul metaclasse dell'istanza, in questo modo:

class MyClass 
    def initialize(varname) 
    class <<self 
     self 
    end.class_eval do 
     attr_accessor varname 
    end 
    end 
end 

o1 = MyClass.new(:foo) 
o2 = MyClass.new(:bar) 
o1.foo = "foo" # works 
o2.bar = "bar" # works 
o2.foo = "baz" # does not work 
+0

class_eval è lo stesso di metterlo dove hai scritto te stesso – johannes

+4

No, non lo è. 'classe << auto; ...; end' non è una chiusura. Non si è in grado di accedere a 'varname' al suo interno, ma è possibile accedervi nel blocco' class_eval'. – sepp2k

+0

[risposta di semiomant] (http://stackoverflow.com/a/14978624/403664) è buono. controlla. –

4

un'implementazione più pulito (NB: Questo aggiungerà la funzione di accesso a tutte le istanze della classe, non solo la singola istanza, vedi commenti qui sotto):

class MyClass 
    def initialize(varname) 
    self.class.send(:attr_accessor, varname) 
    end 
end 
+6

Definisce l'accessor come metodo di istanza di 'MyClass'. Quindi se avete 'o1 = MyClass.new (: foo)' e 'o2 = MyClass.new (: bar)' sia 'o1' che' o2' hanno accessor sia per 'foo' che' bar', che non è il comportamento previsto. – sepp2k

+0

Giusto! Qui ho i miei upboats. Lascerò questa risposta qui (con il tuo commento) per chiunque possa incontrarlo. –

+0

La soluzione di Rob sembra molto più pulita. Ma resterò fedele alla soluzione di sepp2k, avendo gli accessor solo per una istanza. – JAkk

4

Rob d'Apice ha quasi destra. Hai solo bisogno di scrivere:

self.singleton_class.send(:attr_accessor, varname) 

o

self.singleton_class.class_eval "attr_accessor :#{varname}" 

o il mio variante preferita

self.singleton_class.class_exec do attr_accessor varname end 

assumendo il valore di varname è un simbolo