instance_evalとスコープ

これはfooがすでにローカル変数として宣言されているからそっちが優先された話でしょう。

foo.instance_eval { foo } # => #
foo.instance_eval{ defined? foo } # => "local-variable"
foo.instance_eval{ defined? bar } # => "method"


最初見たとき理解不能だった。
え、instance_evalってレシーバのコンテキストで評価されるんじゃないの?( ゚д゚)
なんで、外側のスコープにあるfooが見れるの???


で、調べてみると。

オブジェクトのコンテキストで評価するとは self をそのオブジェクトにして実行するということです。また、文字列/ブロック中でメソッドを定義すれば self の特異メソッドが定義されます。
ただし、ローカル変数だけは instance_eval の外側のスコープと共有します。


なるほど。ローカル変数はアクセスできるのね。
こういうことか。

S = Struct.new(:s)
hoge = 1
S.new.instance_eval{hoge}  #=> 1
S.new.instance_eval{fuga = 1}
puts fuga

NameError: undefined local variable or method `fuga' for main:Object
        from (irb):5

3行目のinstance_eval内からは、現在(Objectインスタンスのコンテキスト)のローカル変数(hoge)にアクセス可能。まぁ、じゃないと、使いづらいしね。何かを引数で受けて、それをinstance_eval内で使いたいってことは沢山あるし。


こういうのは当然無理。

z = 1

class Hoge
  def hoge
    instance_eval do
      z
    end
  end
end

Hoge.new.hoge
NameError: undefined local variable or method `z' for #<Hoge:0x2e47ef4>
        from (irb):5:in `hoge'
        from (irb):4:in `instance_eval'
        from (irb):4:in `hoge'
        from (irb):9


今(Objectインスタンス)のコンテキストのローカル変数にアクセスするには、Objectインスタンス下でinstance_evalしないといけないから。