eval族のスコープについてまとめた

ややこしかったので、まとめました。

eval(Bindingなし)

実行中のコンテキストに出現する変数に対しての操作が可能

foo = "foo"
eval('p foo') #=> foo

eval(Bindingあり)

以下のように、Bindingオブジェクトを使うことで、以下のようにコンテキストを指定できます。

class C
  def instance_binding
    foo = "foooo"
    binding
  end
end

foo = "foo"
binding_object =  C.new.instance_binding
eval('p foo', binding_object) #=> foooo

# このようにも書ける

binding_object.eval('p foo') #=> foooo

module_eval, class_eval

module_evalclass_eval の別名です。 文字列を渡した場合とブロックを渡した場合で、どのスコープで評価されるかが変わります。

文字列を引数とした場合は、レシーバーのスコープで評価されます。

class C; end

C.class_eval(<<-EOF)
Foo = "bar"
def hoge
  p Foo
end
EOF

Foo = "foo"

ブロックを引数とした場合は、そのコンテキストのスコープで評価されます。 つまり、以下ではトップレベルで定義したことになります。

class C; end

C.class_eval do
  Foo = "bar"
  def hoge
    p Foo
  end
end

Foo = "foo"

C.new.hoge
# => warning: already initialized constant Foo
# warning: previous definition of Foo was here
# "foo"

p Object.const_get(:Foo) # => "foo"

instance_eval

module_eval, class_eval と同じく、文字列で渡すときとブロックで渡すときで変わります。

module_exec, class_exec

module_execclass_exec の別名です。

ブロックで渡されたときのスコープは、module_eval と同じです。 文字列での評価はできません。

class C; end

C.class_exec do
  Foo = "bar"
  def hoge
    p Foo
  end
end

Foo = "foo"

C.new.hoge

また、引数をとりブロック引数として、評価する式に値を渡すことができます。

class C; end

C.class_exec(:foo) do |foo|
  define_method(foo) do
    p "foo"
  end
end

C.new.foo #=> "foo"

instance_exec

module_execclass_exec と同じく、引数で値を渡すことができます。

参考