2011-03-31 21 views
11

espero que el siguiente código funcione como se esperaba, pero me da un NoMethodError (método `foo privada', solicitado # < MiClase ...)¿Cómo agregar dinámicamente un attr_reader

class MyClass 
end 

my_object = MyClass.new 

my_object.instance_variable_set(:@foo, "bar") 
MyClass.send("attr_reader", :foo) 

puts my_object.foo 

El problema es Estoy usando un código literalmente idéntico en una aplicación más grande y funciona exactamente como esperaba, pero cuando lo simplifico a este ejemplo más básico, falla.

(entiendo que hay muchas otras maneras de hacer lo que estoy haciendo en Rubí)

+1

el ejemplo funciona en el IRB pero no rubí ... –

+0

@jleedev - me encontré con lo mismo (tanto en ruby ​​y ruby ​​1.8.6 Empresa 1.8.7). irb es genial, pero si ejecuta exactamente los mismos comandos de un archivo ... ¿Crees que es un error en la implementación de Ruby (REE principalmente rehizo el recolector de basura)? –

Respuesta

0

Sólo forzar el método a ser pública:

MyClass.send("public", :foo) 

no tengo idea de por qué es privado en algunos casos.

+0

Esto supone que ya se ha definido un método 'foo', que puede no ser el caso para algunas variables de instancia. – Phrogz

+0

Esta es la mejor respuesta hasta el momento, pero todavía tengo curiosidad de por qué es privada en algunos casos y no en otros. – Leroy22

3

Problema interesante, he encontrado esta solución funciona bien:

MyClass.class_eval("attr_reader :foo") 
+0

Recomiendo usar el formulario de bloque (por respuesta de @MikeLewis) en lugar del formulario de cadena para que no se necesite analizar/leer en tiempo de ejecución. – Phrogz

+0

Sí, tienes razón, acabo de encontrar esta solución de trabajo rápidamente sin buscar mejores formas. –

5

Uso Module#class_eval:

Al hacer esto, el bloque se pone en el contexto de MiClase, lo que le permite llamar a attr_reader

ruby-1.9.2-p136 :028 > my_object.instance_variable_set(:@foo, "bar") 
=> "bar" 
ruby-1.9.2-p136 :029 > MyClass.class_eval{attr_reader :foo} 
=> nil 
ruby-1.9.2-p136 :030 > my_object.foo 
=> "bar" 
+0

Me pregunto si uno puede hacerlo para una instancia particular de una clase. –

1

La respuesta de @MikeLewis es agradable, pero ¿por qué no volver a abrir la clase?

irb(main):001:0> class MyClass; end 
#=> nil 
irb(main):002:0> m = MyClass.new 
#=> #<MyClass:0x2d43ae0> 
irb(main):003:0> class MyClass; attr_accessor :foo; end 
#=> nil 
irb(main):004:0> m.foo = 42 
#=> 42 
+0

Si es una joya que está retocando: 'class ModuleName; end' causas: TypeError: ModuleName no es una clase \t a partir de (IRB): 15 \t desde/usr/bin/IRB: 12: en '

'' – Crisfole

+0

@Crisfole tal vez porque es un 'module' en lugar de una clase. – Phrogz

+0

Debo haber tenido una confusión de gemas. Pensé que estaba intentando volver a abrir esto -> https://github.com/dcparker/ruby-gmail/blob/master/lib/gmail.rb#L3-L6. Tal vez me olvidé de ejecutar bundler ... – Crisfole

1

Supongamos por un instante que desea crear un descriptor de acceso para esa instancia particular de la clase (que supongo es el caso, ya que se está operando en la instancia.

Usted sólo tiene que abrir clase de singleton de la instancia y utilizar instance_eval añadir el descriptor de acceso

class MyClass 
end 
my_instance = MyClass.new 
my_instance.singleton_class.instance_eval { attr_accessor :foo } 
my_instance.foo = :bar 
my_instance.foo # => :bar 
Cuestiones relacionadas