2012-02-27 19 views
16

Estoy usando class_eval para escribir el código que se ejecutará 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.¿Cuál es el alcance variable dentro de la cadena `class_eval`?

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 se evalúa el bloque en el contexto de la clase en tiempo de ejecución - en mi caso, bajo class Foo. Espero que el código anterior se ejecuta 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 se tradujo en error diciendo, 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 @count asignación 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 alcance variable? ¿Cómo ejecuta class_eval su siguiente cadena?

+0

¿Qué le pareció su clase CS169? :) –

Respuesta

12

no se trata de class_eval es sobre @count. si define esta variable en el nivel de clase, será un class instance variable, no un 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 
+0

Funciona. Todavía estoy confundido por la diferencia entre '@ count' y' @@ count' en el método 'class_eval'. Mi comprensión es 1. La cadena que pasa a 'class_eval' se evaluará en tiempo de ejecución, cuando se llame a la' clase Foo'. 2. '@ count' debe ser una variable de instancia para todas las instancias de' Foo'. ¿Por qué usar la variable de clase '@@ count' hace que se comporte como ** variable de instancia **? Sé que el tema es complicado, ¿tiene alguna referencia que pueda leer? – steveyang

+0

esta [publicación de blog] (http://martinfowler.com/bliki/ClassInstanceVariable.html) –

+0

Hola, @selmen. Encontré '@@ count' en realidad no se comporta como una variable de instancia. Se comporta como una variable de clase. Eso no es lo que esperaba – steveyang

Cuestiones relacionadas