bindingとeval
Rails1.2からstart_form_tagとかがdeprecatedになって、
<% form_tag do %> <%= text_field('some_object', 'some_attr') %> <% end %>
みたいに書けるようになったわけですが、<%= form_tag do %> じゃなくて <% form_tag do %>って何よ?って調べたら、evalとbindingという魔法が使われていました。
http://www.ruby-lang.org/ja/man/?cmd=view;name=%C1%C8%A4%DF%B9%FE%A4%DF%B4%D8%BF%F4;em=eval#eval より引用
eval(expr[, binding[, fname[, lineno=1]]]) 文字列 expr を Ruby プログラムとして評価してその結果を返します。 第2引数に Proc オブジェクトまたは Binding オブジェクトを与えた場合、 そのオブジェクトを生成したコンテキストで文字列を評価します。 binding も参照してください。
よくわからないのでBindingを見る
http://www.ruby-lang.org/ja/man/?cmd=view;name=Binding
Binding ローカル変数のテーブルと self、モジュールのネストなどの情報を 保持するオブジェクトのクラス。組み込み関数 binding によってのみ生成され、 eval の第 2 引数に使用します。
どうやらbindingオブジェクトは、あるメソッドあるいはブロックが呼び出された際のコンテキストを保持するオブジェクトらしい。これをevalに指定することでそのコンテキストで文字列を評価してくれるわけデス。内部的にはローカル変数のテーブルがスタックされているというのは想像できますが、それを簡単に扱えるっていうのは驚きデス。
というわけでサンプルを書いてみました。
class A def foo(&block) eval('self.baz', block.binding) count = eval('count', block.binding) count.times(&block) end end class B def bar(count, msg) A.new.foo do puts msg end end def baz puts 'baz' end end b1 = B.new b1.bar(3, 'abc')
結果
baz abc abc abc
コードに意味はありませんが、こんな感じのコードのアプリのレベルで書いてあったら悪意を感じずにはいられません。そんな人いないでしょうけど。