ruby metaprogramming class-eval

ruby - ¿Cuál es el alcance de la variable dentro de la cadena `class_eval`?



metaprogramming class-eval (1)

Estoy usando class_eval para escribir código para ser ejecutado en el contexto de la clase actual. En el siguiente código, quiero agregar un contador para los cambios de los valores de los atributos.

class Class def attr_count(attr_name) attr_name = attr_name.to_s attr_reader attr_name # create the attribute''s getter class_eval %Q{ @count = 0 def #{attr_name}= (attr_name) @attr_name = attr_name @count += 1 end def #{attr_name} @attr_name end } end end class Foo attr_count :bar end f = Foo.new f.bar = 1

Mi comprensión de class_eval es que evalúa el bloque en el contexto de la clase de tiempo de ejecución , en mi caso, bajo la class Foo . Espero que el código anterior se ejecute de manera similar a :

class Foo attr_count :bar @count = 0 def bar= (attr_name) @attr_name = attr_name @count += 1 end def bar @attr_name end end

Sin embargo, el código anterior generó un error al decir que el error es causado por @count += 1 . No puedo entender por qué @count tiene nil:NilClass como su super?

(eval):5:in `bar='': undefined method `+'' for nil:NilClass (NoMethodError)

Por otro lado, @selman ha dado una solución para poner la asignación de @count dentro del método de instancia y funciona.

class Class def attr_count(attr_name) #... class_eval %Q{ def #{attr_name}= (attr_name) @attr_name = attr_name if @count @count += 1 else @count = 1 end end #... } end end

¿Por qué cambia el ámbito de aplicación variable? ¿Cómo ejecuta class_eval su siguiente cadena?


no se trata de class_eval se trata de @count . Si define esta variable a nivel de clase, será una class instance variable no una instance variable .

class Class def attr_count(attr_name) attr_name = attr_name.to_s attr_reader attr_name # create the attribute''s getter class_eval %Q{ def #{attr_name}= (attr_name) @attr_name = attr_name if @count @count += 1 else @count = 1 end end def #{attr_name} @attr_name end } end end class Foo attr_count :bar end f = Foo.new f.bar = 1