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