1

为了为 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)
4

1 回答 1

2

class_evalmodule_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

它将创建两个方法foobar打印它们各自的值。如您所见,我创建了一个包含函数实际源代码的字符串,并将其传递到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 方法是最糟糕的方法之一)只能作为最后的手段使用。

于 2012-01-14T18:07:28.000 回答