サンプルでいきなりbindingという変数みたいなのが出てきてとまどう。

require 'erb'
str = "hoge"
erb = ERB.new("value = <%= str %>")
puts erb.result(binding)

Rubyist Magazine - 標準添付ライブラリ紹介 【第 10 回】 ERB

どうやらbindingは変数ではなく、組み込み関数らしい。

■ binding について
これは変数ではなくRubyに最初から組み込まれているメソッド。実行すると、このメソッドを呼んだスコープの変数と値の一覧を収めたオブジェクト(=bindingと呼ばれるもの)が返ってくる。

お題目うぉっち:ERBの実行時に出てくるbindingって何者?
ローカル変数のテーブルと self、モジュールのネストなどの情報を保持するオブジェクトのクラスです。

組み込み関数 Kernel.#binding によってのみ生成され、Kernel.#eval の第 2 引数に使用します。またトップレベルの Binding オブジェクトとして組み込み定数 Kernel::TOPLEVEL_BINDING が用意されています。

class Binding
変数・メソッドなどの環境情報を含んだ Binding オブジェクトを生成して返します。通常、Kernel.#eval の第二引数として使います。

module function Kernel.#binding
eval(expr) -> object
eval(expr, bind, fname = __FILE__, lineno = 1) -> object

文字列 expr を Ruby プログラムとして評価してその結果を返します。第2引数に Proc オブジェクトまたは Binding オブジェクトを与えた場合、そのオブジェクトを生成したコンテキストで文字列を評価します。

expr の中のローカル変数の扱いはブロックの場合と同じです。すなわち、eval 実行前に補足されていた変数は eval 実行後にブロック外に持ち出せます。

fname と lineno が与えられた場合には、ファイル fname の行番号 lineno から文字列 expr が書かれているかのようにコンパイルされます。スタックトレースの表示などを差し替えることができます。

bind によらずに特定のオブジェクトのコンテキストで expr を評価したい場合、 Module#module_eval, Object#instance_eval が使えます。

module function Kernel.#eval

へー。
わかったようなわからないような。

とりあえずサンプルを書いてみる。

環境。


$ uname -s -r -o -m
Linux 2.6.26-2-amd64 x86_64 GNU/Linux
 
$ ruby -v
ruby 1.8.7 (2008-08-11 patchlevel 72) [x86_64-linux]

binding関数についてのサンプルコード。


$ cat ./bindtest.rb
#!/usr/bin/env ruby
$KCODE='u'
 
def foo
  aaa = "hoge"
  bbb = { :c => 'd', 'e' => 'f' }
  b = binding()
  ccc = [ 0, 1, 2, 3, 4 ]
  ddd = [ aaa, bbb, ccc ]
  return b
end
 
def bar
  aaa = "HAGE"
  bbb = { :C => 'D', 'E' => 'F' }
  b = binding()
  ccc = [ 9, 8, 7, 6, 5 ]
  ddd = [ aaa, bbb, ccc ]
  return b
end
 
b1 = foo()
p b1
eval("p aaa", b1)
eval("p bbb", b1)
eval("p ccc", b1)
eval("p ddd", b1)
 
b2 = bar()
p b2
eval("p aaa", b2)
eval("p bbb", b2)
eval("p ccc", b2)
eval("p ddd", b2)
 
b3 = binding()
p b3
eval("p b1", b3)
eval("p b2", b3)

実行結果。


$ ruby ./bindtest.rb
#<Binding:0x7f56e727a188>
"hoge"
{:c=>"d", "e"=>"f"}
[0, 1, 2, 3, 4]
["hoge", {:c=>"d", "e"=>"f"}, [0, 1, 2, 3, 4]]
#<Binding:0x7f56e7279800>
"HAGE"
{:C=>"D", "E"=>"F"}
[9, 8, 7, 6, 5]
["HAGE", {:C=>"D", "E"=>"F"}, [9, 8, 7, 6, 5]]
#<Binding:0x7f56e7278fb8>
#<Binding:0x7f56e727a188>
#<Binding:0x7f56e7279800>

・「binding関数を呼んだ瞬間」と「生成されるBindingオブジェクトの状態」は関係なさそう。binding関数をコールしたあとにセットした変数もちゃんと見れる。

・Binding#inspect の内容がオブジェクトIDっぽい表現でしかない。特殊な組み込み関数だからかな。

さて、テンプレートファイルにERBで変数を埋め込んでみる。

サンプルコード。


$ cat ./erbtest.rb
#!/usr/bin/env ruby
$KCODE='u'
 
require 'erb'
 
def foo
  aaa = "hoge"
  bbb = { :c => 'd', 'e' => 'f' }
  b = binding()
  ccc = [ 0, 1, 2, 3, 4 ]
  ddd = [ aaa, bbb, ccc ]
  return b
end
 
def bar
  aaa = "HAGE"
  bbb = { :C => 'D', 'E' => 'F' }
  b = binding()
  ccc = [ 9, 8, 7, 6, 5 ]
  ddd = [ aaa, bbb, ccc ]
  return b
end
 
b1 = foo()
b2 = bar()
 
erb = ERB.new(IO.read('erbtest.template'))
str1 = erb.result(b1)
str2 = erb.result(b2)
puts str1
puts str2

ERBクラスにはファイルを直接読み込む機能があるのかと思ってたけどなかった。
なので、IO.read にファイルパスを指定して読み込む。

テンプレートになるファイル。


$ cat ./erbtest.template
<%= aaa %>
<%= bbb %>
<%= ccc %>
<%= ddd %>
 

実行結果はこんな感じ。


$ ruby ./erbtest.rb
hoge
cdef
01234
hogecdef01234
 
HAGE
CDEF
98765
HAGECDEF98765
 

Ref. class ERB

tags: Ruby zurazure

Posted by NI-Lab. (@nilab)