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