2012-01-14 3 views
1

Для генерации издевается для OmniAuth, я добавил этот метод config/environments/development.rbРуби/Rails: class_eval не хочет, чтобы оценить этот код

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) 
+0

Я не специалист по метапрограмме ruby, но, возможно, вы хотите ['instance_eval'] (http://ilikestuffblog.com/2009/01/09/fun-with-rubys-instance_eval-and-class_eval/) ? –

+3

Почему вы вообще используете '* _eval'? Просто позвоните своему методу без eval. Ваш текущий код даже выглядит так. –

+0

@Sergio Я получаю 'instance_eval: не могу преобразовать символ в строку (TypeError)' – Laura

ответ

2

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 относятся к числу самых неудачных) должны использоваться только в качестве последнего средства.