Ruby/Rails: class_eval не хочет оценивать этот код
Чтобы создать макеты для Omniauth, я добавил этот метод в config/environments/development.rb
def provides_mocks_for(*providers)
providers.each do |provider|
class_eval %Q{
OmniAuth.config.add_mock(provider, {
:uid => '123456',
:provider => provider,
:nickname => 'nickname',
:info => {
'email' => "#{provider}@webs.com",
'name' => 'full_name_' + provider
}
})
}
end
end
тогда я звоню в тот же файл:
provides_mocks_for :facebook, :twitter, :github, :meetup
Но я получаю:
3.1.3/lib/active_support/core_ext/kernel/singleton_class.rb:11:in `class_eval': can't create instance of singleton class (TypeError)
1 ответ
class_eval
а также module_eval
(которые эквивалентны) используются для оценки и немедленного выполнения строк в виде кода 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
,
Хотя это очень эффективный инструмент для выполнения динамически создаваемого кода, его следует использовать с большой осторожностью. Если вы допустите ошибки здесь, ПЛОХИЕ ВЕЩИ БУДУТ ПРОИЗОЙТИ. Например, если вы используете пользовательские значения при генерации кода, убедитесь, что переменные содержат только те значения, которые вы ожидаете. Функция на основе Eval, как правило, должна использоваться как последнее средство.
Более чистым и обычно предпочтительным вариантом является использование define_method
вот так:
class Foo
%w[foo bar].each do |name|
define_method name do
puts name
end
end
end
(Обратите внимание, что MRI немного быстрее с вариантом eval. Но это не имеет значения в большинстве случаев по сравнению с дополнительной безопасностью и ясностью.)
Теперь в вашем данном коде вы эффективно пишете код в строку, которую можно запустить напрямую. С помощью class_eval
здесь приводит к выполнению строки в контексте самого верхнего объекта (Kernel
в этом случае). Поскольку это специальный одноэлементный объект, который не может быть создан (подобно nil, true и false), вы получаете эту ошибку.
Однако, поскольку вы создаете непосредственно исполняемый код, вам не нужно использовать class_eval
(или любая форма eval) вообще. Просто запустите ваш код в цикле, как есть. И всегда помните: варианты метапрограммирования (из которых eval-методы являются одними из самых плохих) следует использовать только в качестве последнего средства.