要为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)
答案 0 :(得分:2)
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
(请注意,对于eval变体,MRI的速度要快一些。但与增加的安全性和清晰度相比,大多数情况下这并不重要。)
现在,在您给定的代码中,您可以有效地将代码写入可以直接运行的字符串中。在这里使用class_eval
会导致在最顶层对象(在本例中为Kernel
)的上下文中执行字符串。由于这是一个特殊的单例对象,无法实现(类似于nil,true和false),因此会出现此错误。
但是,当您直接创建可执行代码时,根本不需要使用class_eval
(或任何形式的eval)。只需按循环运行代码即可。并且永远记住:元编程变体(其中eval方法属于最坏的方法)应该只作为最后的手段使用。