[英]Ruby templates: How to pass variables into inlined ERB?
我有一個內聯到 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)
我該如何解決?
對於簡單的解決方案,請使用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
上面的代碼很簡單,但(至少)有兩個問題: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
當然,如果您要經常使用它,請確保創建一個String#erb
擴展名,它允許您編寫類似"x=<%= x %>, y=<%= y %>".erb(x: 1, y: 2)
。
使用Binding 的簡單解決方案:
b = binding
b.local_variable_set(:a, 'a')
b.local_variable_set(:b, 'b')
ERB.new(template).result(b)
知道了!
我創建了一個綁定類
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 %>
在原始問題的代碼中,只需替換
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
參考資料: http : //stoneship.org/essays/erb-and-the-context-object/
我不能給你一個很好的答案,為什么會發生這種情況,因為我不是 100% 確定 ERB 是如何工作的,但只是看看ERB RDocs ,它說你需要一個binding
,它是“一個綁定或過程用於設置代碼評估上下文的對象”。
再次嘗試上面的代碼,只是替換
result = template.result
和
result = template.result(binding)
使它工作。
我確定/希望有人會跳到這里並提供有關正在發生的事情的更詳細的解釋。 干杯。
編輯:有關Binding
更多信息並使所有這些更清晰(至少對我而言),請查看Binding RDoc 。
也許最干凈的解決方案是將特定的current
局部變量傳遞給 erb 模板,而不是傳遞整個binding
。 可以使用ERB#result_with_hash方法(在 Ruby 2.5 中引入)
DATA.keys.each do |current|
result = template.result_with_hash(current: current)
...
編輯:這是一個骯臟的解決方法。 請看我的另一個回答。
這很奇怪,但添加
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
和不使用**
可以使用比 2.1 更舊的 ruby
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.