ERB で Binding を使うとローカル変数が勝手に変更されるという問題

通常、ERB#result() には Binding を渡すけど、個人的にこれが大っ嫌い。なんでかというと、eRubyスクリプトを実行することで、メインプログラム内のローカル変数が勝手に変更されてしまうから。
つまりだな、

s = <<END
<% for item in list %>
<p><%= item %></p>
<% end %>
END

list = ['AAA', 'BBB', 'CCC']
require 'erb'
item = 'foo'   # ローカル変数を設定
print ERB.new(s).result(binding)
p item         #=> "CCC" に変更されている!

というのがすごく嫌いというわけ。
#んなもん当たり前じゃないか! と思った人とはたぶん仲良くはなれない。

これが嫌いだから、

print ERB.new(s).result(:list=>list)

のようにできるべきだと思ってるし、Erubis::Eruby#result()はそうなっている。

Erubisにはもうひとつ、Erubis::Eruby#evaluate()というのがある。使い方はresult()と変わらないんだけど、メインプログラムから渡される変数にはローカル変数ではなくインスタンス変数を使う点が違う。

## ローカル変数ではなくインスタンス変数を使うのがポイント
input = <<END
<h1><%= @title %></h1>
<% for item in @list %>
<b><%= item %></b>
<% end %>
END

## result() のかわりに evaluate() を使う
title = 'Example'
list = [10, 20, 30]
require 'erubis'
item = 'foo'   # ローカル変数を設定
eruby = Erubis::Eruby.new(input)
print eruby.evaluate(:title=>title, :items=>items) # Hash
p item         #=> "foo" で変わっていない!

Erubis::Eruby#evaluate() は内部で Object#instance_eval を使っている。eval() と違って、instance_eval() は Proc オブジェクトを引数にとれるため、Erubis::Eruby#evaluate() は内部でコードをProcオブジェクトに変換してから instance_eval() で実行している。こうすることで、文字列を毎回 eval() する方法と比べて構文解析にかかるコストをなくしている。また ERB#def_method と比べても速度に遜色ないし、柔軟性もより高い。
もっと大事なことは、Bindingを使わないので、eRubyスクリプト内でのローカル変数の操作がメインプログラムに影響することがない。素晴らしい。ただしselfが変わることには注意。