Ruby/Rails:class_evalはこのコードを評価したくない
-
28-10-2019 - |
質問
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)
解決
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
2つの方法が作成されます foo
と bar
それぞれの値を印刷します。ご覧のとおり、関数の実際のソースコードを含む文字列を作成し、 class_eval
.
これは動的に作成されたコードを実行する非常に有能な手段ですが、細心の注意を払って使用する必要があります。ここでエラーを犯した場合、Bad Things Dapen™。たとえば、コードを生成するときにユーザーサプリド値を使用する場合は、make 本当 確かに、変数に期待される値のみが含まれています。評価ベースの関数は、通常、最後の手段として使用する必要があります。
よりクリーンで一般的に好ましいバリアントを使用します define_method
そのようです:
class Foo
%w[foo bar].each do |name|
define_method name do
puts name
end
end
end
(MRIは評価バリアントを使用すると少し速いことに注意してください。しかし、それは、追加の安全性と明確さと比較して、ほとんどの場合は問題ではありません。)
指定されたコードでは、直接実行できる文字列に効果的にコードを書き込みます。使用 class_eval
ここでは、最上位オブジェクトのコンテキストで文字列が実行されることにつながります(Kernel
この場合)。これは、インスタンス化できない特別なシングルトンオブジェクトであるため(nil、true、falseに似ています)、このエラーが発生します。
ただし、実行可能なコードを直接作成すると、使用する必要はありません class_eval
(またはあらゆる形式の評価)。コードをそのままループで実行するだけです。そして、常に覚えておいてください:メタプログラミングバリアント(その評価方法は最も悪い態度の1つです)は、最後の手段としてのみ使用する必要があります。