class_eval
и module_eval
(которые equivalen t) используются для оценки и немедленного выполнения строк в виде кода Ruby. Таким образом, они могут использоваться как средство метапрограммирования для динамического создания методов. Примером может служить
class Foo
%w[foo bar].each do |name|
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{name}
puts '#{name}'
end
RUBY
end
end
Это создаст два метода foo
и bar
, которые печатают свои соответствующие значения. Как вы можете видеть, я создаю строку, содержащую фактический исходный код функции и передаю ее в class_eval
.
Хотя это очень удобный инструмент для выполнения динамически созданного кода, его следует использовать с большой осторожностью. Если вы делаете ошибки здесь, BAD THINGS WAP HAPPEN ™. Например. если вы используете предоставленные пользователем значения при генерации кода, сделайте действительно уверены, что переменные содержат только значения, которые вы ожидаете. Функция на основе Eval обычно должна использоваться в качестве последнего средства.
Очиститель и обычно предпочтительным вариантом является использование define_method
следующим образом:.
class Foo
%w[foo bar].each do |name|
define_method name do
puts name
end
end
end
(Обратите внимание, что МРТ является чуть-чуть быстрее с вариантом Eval Но это не имеет значения, большую часть времени по сравнению к дополнительной безопасности и ясности.)
Теперь в вашем коде вы эффективно записываете код в строку, которая может быть запущена напрямую. Использование class_eval
приводит к тому, что строка выполняется в контексте самого верхнего объекта (Kernel
в этом случае). Поскольку это специальный одноэлементный объект, который не может быть инстанцирован (аналогично nil, true и false), вы получаете эту ошибку.
Однако при создании непосредственно исполняемого кода вам не нужно использовать class_eval
(или любую форму eval) вообще. Просто запустите свой код в цикле, как есть. И всегда помните: варианты метапрограмм (из которых методы eval относятся к числу самых неудачных) должны использоваться только в качестве последнего средства.
Я не специалист по метапрограмме ruby, но, возможно, вы хотите ['instance_eval'] (http://ilikestuffblog.com/2009/01/09/fun-with-rubys-instance_eval-and-class_eval/) ? –
Почему вы вообще используете '* _eval'? Просто позвоните своему методу без eval. Ваш текущий код даже выглядит так. –
@Sergio Я получаю 'instance_eval: не могу преобразовать символ в строку (TypeError)' – Laura