Ruby模板:如何将variables传递到内联的ERB?
我有一个ERB模板内嵌到Ruby代码中:
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
我无法将variables“current”传递给模板。
错误是:
(erb):1: undefined local variable or method `current' for main:Object (NameError)
我该如何解决?
对于一个简单的解决scheme,使用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
上面的代码很简单,但是(至less)有两个问题:1)由于它依赖于OpenStruct
,访问一个不存在的variables返回nil
而你可能更喜欢它失败。 2)在块中调用binding
,就是这样,在一个闭包中,所以它包含了范围中的所有局部variables(实际上,这些variables将会影响结构的属性!)。
所以这里是另一个解决scheme,更详细但没有任何这些问题:
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
当然,如果你打算经常使用它,请确保你创build了一个String#erb
扩展名,它允许你写如"x=<%= x %>, y=<%= y %>".erb(x: 1, y: 2)
。
简单的解决scheme使用绑定 :
b = binding b.local_variable_set(:a, 'a') b.local_variable_set(:b, 'b') ERB.new(template).result(b)
得到它了!
我创build一个绑定类
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 %>
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/
在原始问题的代码中,只需replace
result = template.result
同
result = template.result(binding)
这将使用每个块的上下文而不是顶级上下文。
(刚刚提取@sciurus的评论作为答案,因为它是最短,最正确的。)
我不能给你一个很好的答案,为什么发生这种情况,因为我不是100%确定ERB是如何工作的,而只是看着ERB RDocs ,它说你需要a Binding or Proc object which is used to set the context of code evaluation.
再次尝试你的上面的代码,只是取代result = template.result
result = template.result(binding)
使它的工作。
我敢肯定/希望有人会跳进来,提供更详细的解释。 干杯。
编辑:有关Binding
更多信息,使所有这一点更清楚一点(至less对我来说),检查了绑定RDoc 。
编辑 :这是一个肮脏的解决方法。 请看我的其他答案。
这很奇怪,但增加
current = ""
在“for-each”循环修复问题之前。
上帝保佑脚本语言和他们的“语言function”…
正如其他人所说,要用一些variables来评估ERB,你需要一个适当的绑定。 有一些定义类和方法的解决scheme,但我认为最简单,最有把握和最安全的方法就是生成一个干净的绑定,并用它来parsingERB。 这是我的意见(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