ローカル変数とメソッドと eval

def x
  :method
end

def f
  if false
    x = :var
  end
  x
end

p f # => nil

f が nil を返すのは何ともかんとも。Python User としてはちょっと気持ち悪いかも知れない。まぁ、ruby はメソッドかローカル変数かをレキシカルな情報を使って判定しているんだろうけど。
で、まあ変なことを思いついたりするわけで。

require 'erb'

template = <<'EOD'
def x; :method; end

def f
  <%= eval_assign ? "eval #{assign.inspect}" : assign %>
  <%= eval_exp ? "eval #{exp.inspect}" : exp %>
end

EOD

erb = ERB.new(template, nil, '%<>')

assign = 'x = :var'
exp = 'x'
puts '--------------------'
[false, true].each do |eval_assign|
  [false, true].each do |eval_exp|
    code = erb.result(binding)
    puts code
    eval code
    puts "f # => #{f.inspect}"
    puts '--------------------'
  end
end

なんか、1.8.6-p36 と trunk で挙動が違うのは eval(もしくは Binding)の仕様変更ということでいいんだろうか?Mailing List アーカイブを適当に見てみたけどよくわからん。
結果は以下。
ruby 1.8.6 pathlevel 36 の場合。

--------------------
def x; :method; end

def f
  x = :var
  x
end

f # => :var
--------------------
def x; :method; end

def f
  x = :var
  eval "x"
end

f # => :var
--------------------
def x; :method; end

def f
  eval "x = :var"
  x
end

f # => :method
--------------------
def x; :method; end

def f
  eval "x = :var"
  eval "x"
end

f # => :var
--------------------

ruby 1.9.0 trunk の場合

--------------------
def x; :method; end

def f
  x = :var
  x
end

f # => :var
--------------------
def x; :method; end

def f
  x = :var
  eval "x"
end

f # => :var
--------------------
def x; :method; end

def f
  eval "x = :var"
  x
end

f # => :method
--------------------
def x; :method; end

def f
  eval "x = :var"
  eval "x"
end

f # => :method
--------------------