Я использую 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
Мое понимание class_eval
состоит в том, что он оценивает блок в контексте класса времени выполнения - в моем случае, под class Foo
. Я ожидаю, что приведенный выше код работает аналогично :
class Foo
attr_count :bar
@count = 0
def bar= (attr_name)
@attr_name = attr_name
@count += 1
end
def bar
@attr_name
end
end
Однако приведенный выше код привел к ошибке, говоря, что ошибка вызвана @count += 1
. Я не могу понять, почему @count
имеет nil:NilClass
в качестве супер?
(eval):5:in `bar=': undefined method `+' for nil:NilClass (NoMethodError)
С другой стороны, @selman дал решение поместить присваивание @count
в метод экземпляра, и оно работает.
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
Почему меняется область действия переменной? Как class_eval
выполняет следующую строку?