Rubyコードにインライン化されたERBテンプレートがあります:
require 'erb'
DATA = {
:a => "HELLO",
:b => "WORLD",
}
template = ERB.new <<-EOF
current key is: <%= current %>
current value is: <%= DATA[current] %>
EOF
DATA.keys.each do |current|
result = template.result
outputFile = File.new(current.to_s,File::CREAT|File::TRUNC|File::RDWR)
outputFile.write(result)
outputFile.close
end
変数「current」をテンプレートに渡すことはできません。
エラーは次のとおりです。
(erb):1: undefined local variable or method `current' for main:Object (NameError)
どうすれば修正できますか?
とった!
バインディングクラスを作成します
class BindMe
def initialize(key,val)
@key=key
@val=val
end
def get_binding
return binding()
end
end
インスタンスをERBに渡す
dataHash.keys.each do |current|
key = current.to_s
val = dataHash[key]
# here, I pass the bindings instance to ERB
bindMe = BindMe.new(key,val)
result = template.result(bindMe.get_binding)
# unnecessary code goes here
end
.erbテンプレートファイルは次のようになります。
Key: <%= @key %>
簡単な解決策として、 OpenStruct を使用します。
_require 'erb'
require 'ostruct'
namespace = OpenStruct.new(name: 'Joan', last: 'Maragall')
template = 'Name: <%= name %> <%= last %>'
result = ERB.new(template).result(namespace.instance_eval { binding })
#=> Name: Joan Maragall
_
上記のコードは非常に単純ですが、(少なくとも)2つの問題があります。1)OpenStruct
に依存しているため、存在しない変数へのアクセスはnil
を返します。騒々しく失敗しました。 2)binding
はブロック内、つまりクロージャー内で呼び出されるため、スコープ内のすべてのローカル変数が含まれます(実際、これらの変数は構造体の属性を隠します!)。
そこで、より詳細ですが、これらの問題のない別のソリューションがあります:
_class Namespace
def initialize(hash)
hash.each do |key, value|
singleton_class.send(:define_method, key) { value }
end
end
def get_binding
binding
end
end
template = 'Name: <%= name %> <%= last %>'
ns = Namespace.new(name: 'Joan', last: 'Maragall')
ERB.new(template).result(ns.get_binding)
#=> Name: Joan Maragall
_
もちろん、これを頻繁に使用する場合は、"x=<%= x %>, y=<%= y %>".erb(x: 1, y: 2)
のようなものを記述できる_String#erb
_拡張を作成してください。
Binding を使用した簡単なソリューション:
b = binding
b.local_variable_set(:a, 'a')
b.local_variable_set(:b, 'b')
ERB.new(template).result(b)
元の質問のコードでは、単に置き換える
result = template.result
と
result = template.result(binding)
これは、トップレベルのコンテキストではなく、各ブロックのコンテキストを使用します。
(コメントが@sciurusによって抽出されたのは、最短で最も正確なコメントだからです。)
require 'erb'
class ERBContext
def initialize(hash)
hash.each_pair do |key, value|
instance_variable_set('@' + key.to_s, value)
end
end
def get_binding
binding
end
end
class String
def erb(assigns={})
ERB.new(self).result(ERBContext.new(assigns).get_binding)
end
end
REF: http://stoneship.org/essays/erb-and-the-context-object/
私はERBがどのように機能するかを100%確信していないので、なぜこれが起こっているのかについて非常に良い答えを与えることはできませんが、単に ERB RDocs を見て、binding
が必要だと言いますこれは、「コード評価のコンテキストを設定するために使用されるBindingまたはProcオブジェクト」です。
上記のコードをもう一度試して、単に置き換える
result = template.result
と
result = template.result(binding)
動作させた。
誰かがここに飛び込んで、何が起こっているかについてのより詳細な説明を提供してくれると確信しています。乾杯。
編集:Binding
の詳細と、これをすべて明確にするために(少なくとも私にとって)、 Binding RDoc をチェックしてください。
[〜#〜] edit [〜#〜]:これは汚い回避策です。私の他の答えをご覧ください。
それは全く奇妙ですが、追加します
current = ""
「for-each」ループが問題を修正する前。
神の祝福スクリプト言語とその「言語機能」...
他の人が言ったように、いくつかの変数セットでERBを評価するには、適切なバインディングが必要です。クラスとメソッドを定義するいくつかのソリューションがありますが、最も単純で、ほとんどの制御と安全性を与えるのは、クリーンなバインディングを生成し、それを使用してERBを解析することです。私の見解は次のとおりです(Ruby 2.2.x):
_module B
def self.clean_binding
binding
end
def self.binding_from_hash(**vars)
b = self.clean_binding
vars.each do |k, v|
b.local_variable_set k.to_sym, v
end
return b
end
end
my_Nice_binding = B.binding_from_hash(a: 5, **other_opts)
result = ERB.new(template).result(my_Nice_binding)
_
eval
を使用しても_**
_を使用しなくても、より古いRuby 2.1よりも