ERB で Binding のかわりに Hash を使う

class ERB
  def result_hash(hash)
    b = binding
    hash.each {|k,v| eval "#{k} = %Q[#{v}]", b}
    result b
  end
end

puts ERB.new('<%= example %>').result_hash(:example => 'foo')
ERB で引数の hash を展開して欲しい - #生存戦略 、それは - subtech

これだと、値がArrayとかだったらうまくいかない。また繰り返しごとにevalするのは効率が悪い。こう書き換えたほうがいいだろう。

class ERB
  def result_hash(hash)
    b = binding
    eval hash.collect {|k,v| "#{k} = hash[#{k.inspect}];" }.join
    result b
  end
end
ERB.new('<%= var.inspect %>').result_hash(:var=>[1,2,3])  #=> [1, 2, 3]

ちなみにErubis::Eruby#result()はBindingもHashも受け付けるようになっている。

input = <<END
<h1><%= title %></h1>
<% for item in items %>
<b><%= item %></b>
<% end %>
END

require 'erubis'
title = 'Example'
items = [10, 20, 30]
eruby = Erubis::Eruby.new(input)
print eruby.result(binding)   # Binding
print eruby.result(:title=>title, :items=>items) # Hash

ただし、Ruby1.9からはeval中で定義されたローカル変数はeval中でのみ有効であり、evalを抜けると使えなくなるという仕様に変更されたため、これらの方法は使えない同じBindingを使えばいいだけでした(詳細はコメントをどうぞ)。こんな仕様変更するんなら、Bindingに値を設定できるようにするか、PHPのextract()関数のようにローカル変数を設定できるメソッドを追加してくれよと思うけど、今は Erubis::Eruby#evaluate() を使って我慢することにする。

Erubis::Eruby#evaluate() については次のエントリで。