I have an ERB template inlined into Ruby code:
我有一个嵌入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
I can't pass the variable "current" into the template.
我不能将变量“current”传递到模板中。
The error is:
错误的是:
(erb):1: undefined local variable or method `current' for main:Object (NameError)
How do I fix this?
我怎么修复这个?
9 个解决方案
#1
57
For a simple solution, use OpenStruct:
对于简单的解决方案,使用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
The code above is simple enough but has (at least) two problems: 1) Since it relies on OpenStruct
, an access to a non-existing variable returns nil
while you'd probably prefer that it failed noisily. 2) binding
is called within a block, that's it, in a closure, so it includes all the local variables in the scope (in fact, these variables will shadow the attributes of the struct!).
上面的代码非常简单,但是(至少)有两个问题:1)因为它依赖于OpenStruct,对一个不存在的变量的访问返回nil,而您可能更希望它失败得很大声。2)绑定是在块中调用的,也就是在闭包中调用的,因此它包含作用域中的所有局部变量(实际上,这些变量将隐藏结构的属性!)
So here is another solution, more verbose but without any of these problems:
这是另一个解决方案,更详细但没有这些问题
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
Of course, if you are going to use this often, make sure you create a String#erb
extension that allows you to write something like "x=<%= x %>, y=<%= y %>".erb(x: 1, y: 2)
.
当然,如果您打算经常使用它,请确保创建了一个字符串#erb扩展,允许您编写“x=<%= x %>, y=<%= y %>”之类的内容。erb(x:1,y:2)。
#2
20
Simple solution using Binding:
简单的解决方案使用绑定:
b = binding
b.local_variable_set(:a, 'a')
b.local_variable_set(:b, 'b')
ERB.new(template).result(b)
#3
10
Got it!
得到它!
I create a bindings class
我创建了一个bindings类
class BindMe
def initialize(key,val)
@key=key
@val=val
end
def get_binding
return binding()
end
end
and pass an instance to ERB
并将实例传递给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
The .erb template file looks like this:
.erb模板文件如下:
Key: <%= @key %>
#4
5
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/
裁判:http://stoneship.org/essays/erb-and-the-context-object/
#5
5
In the code from original question, just replace
在原始问题的代码中,只需替换
result = template.result
with
与
result = template.result(binding)
That will use the each block's context rather than the top-level context.
这将使用每个块的上下文,而不是*上下文。
(Just extracted the comment by @sciurus as answer because it's the shortest and most correct one.)
(只是提取了@sciurus的评论作为答案,因为它是最简短、最正确的。)
#6
4
I can't give you a very good answer as to why this is happening because I'm not 100% sure how ERB works, but just looking at the ERB RDocs, it says that you need a binding
which is a Binding or Proc object which is used to set the context of code evaluation.
Trying your above code again and just replacing result = template.result
with result = template.result(binding)
made it work.
我不能给你一个很好的回答,这是为什么,因为我不是100%肯定ERB是如何工作的,只是看着ERB出来时,它说,你需要一个绑定是一个绑定或Proc对象用于设置上下文代码评估。再次尝试上面的代码并替换result = template。result = template.result(绑定)使其工作。
I'm sure/hope someone will jump in here and provide a more detailed explanation of what's going on. Cheers.
我肯定/希望有人会加入进来,对正在发生的事情提供更详细的解释。欢呼。
EDIT: For some more information on Binding
and making all of this a little clearer (at least for me), check out the Binding RDoc.
编辑:要了解更多关于绑定的信息,并使所有这些信息更清晰(至少对我而言),请查看绑定RDoc。
#7
0
EDIT: This is a dirty workaround. Please see my other answer.
编辑:这是一个肮脏的变通方案。请看我的另一个答案。
It's totally strange, but adding
这完全是奇怪的,但是增加
current = ""
before the "for-each" loop fixes the problem.
在“for-each”循环修复问题之前。
God bless scripting languages and their "language features"...
上帝保佑脚本语言及其“语言特性”……
#8
0
This article explains this nicely.
本文很好地解释了这一点。
http://www.garethrees.co.uk/2014/01/12/create-a-template-rendering-class-with-erb/
http://www.garethrees.co.uk/2014/01/12/create-a-template-rendering-class-with-erb/
#9
0
As others said, to evaluate ERB with some set of variables, you need a proper binding. There are some solutions with defining classes and methods but I think simplest and giving most control and safest is to generate a clean binding and use it to parse the ERB. Here's my take on it (ruby 2.2.x):
正如其他人所说,要用一些变量来评估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)
I think with eval
and without **
same can be made working with older ruby than 2.1
我认为使用eval和不使用** *的方法都可以使用比2.1更老的ruby
#1
57
For a simple solution, use OpenStruct:
对于简单的解决方案,使用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
The code above is simple enough but has (at least) two problems: 1) Since it relies on OpenStruct
, an access to a non-existing variable returns nil
while you'd probably prefer that it failed noisily. 2) binding
is called within a block, that's it, in a closure, so it includes all the local variables in the scope (in fact, these variables will shadow the attributes of the struct!).
上面的代码非常简单,但是(至少)有两个问题:1)因为它依赖于OpenStruct,对一个不存在的变量的访问返回nil,而您可能更希望它失败得很大声。2)绑定是在块中调用的,也就是在闭包中调用的,因此它包含作用域中的所有局部变量(实际上,这些变量将隐藏结构的属性!)
So here is another solution, more verbose but without any of these problems:
这是另一个解决方案,更详细但没有这些问题
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
Of course, if you are going to use this often, make sure you create a String#erb
extension that allows you to write something like "x=<%= x %>, y=<%= y %>".erb(x: 1, y: 2)
.
当然,如果您打算经常使用它,请确保创建了一个字符串#erb扩展,允许您编写“x=<%= x %>, y=<%= y %>”之类的内容。erb(x:1,y:2)。
#2
20
Simple solution using Binding:
简单的解决方案使用绑定:
b = binding
b.local_variable_set(:a, 'a')
b.local_variable_set(:b, 'b')
ERB.new(template).result(b)
#3
10
Got it!
得到它!
I create a bindings class
我创建了一个bindings类
class BindMe
def initialize(key,val)
@key=key
@val=val
end
def get_binding
return binding()
end
end
and pass an instance to ERB
并将实例传递给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
The .erb template file looks like this:
.erb模板文件如下:
Key: <%= @key %>
#4
5
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/
裁判:http://stoneship.org/essays/erb-and-the-context-object/
#5
5
In the code from original question, just replace
在原始问题的代码中,只需替换
result = template.result
with
与
result = template.result(binding)
That will use the each block's context rather than the top-level context.
这将使用每个块的上下文,而不是*上下文。
(Just extracted the comment by @sciurus as answer because it's the shortest and most correct one.)
(只是提取了@sciurus的评论作为答案,因为它是最简短、最正确的。)
#6
4
I can't give you a very good answer as to why this is happening because I'm not 100% sure how ERB works, but just looking at the ERB RDocs, it says that you need a binding
which is a Binding or Proc object which is used to set the context of code evaluation.
Trying your above code again and just replacing result = template.result
with result = template.result(binding)
made it work.
我不能给你一个很好的回答,这是为什么,因为我不是100%肯定ERB是如何工作的,只是看着ERB出来时,它说,你需要一个绑定是一个绑定或Proc对象用于设置上下文代码评估。再次尝试上面的代码并替换result = template。result = template.result(绑定)使其工作。
I'm sure/hope someone will jump in here and provide a more detailed explanation of what's going on. Cheers.
我肯定/希望有人会加入进来,对正在发生的事情提供更详细的解释。欢呼。
EDIT: For some more information on Binding
and making all of this a little clearer (at least for me), check out the Binding RDoc.
编辑:要了解更多关于绑定的信息,并使所有这些信息更清晰(至少对我而言),请查看绑定RDoc。
#7
0
EDIT: This is a dirty workaround. Please see my other answer.
编辑:这是一个肮脏的变通方案。请看我的另一个答案。
It's totally strange, but adding
这完全是奇怪的,但是增加
current = ""
before the "for-each" loop fixes the problem.
在“for-each”循环修复问题之前。
God bless scripting languages and their "language features"...
上帝保佑脚本语言及其“语言特性”……
#8
0
This article explains this nicely.
本文很好地解释了这一点。
http://www.garethrees.co.uk/2014/01/12/create-a-template-rendering-class-with-erb/
http://www.garethrees.co.uk/2014/01/12/create-a-template-rendering-class-with-erb/
#9
0
As others said, to evaluate ERB with some set of variables, you need a proper binding. There are some solutions with defining classes and methods but I think simplest and giving most control and safest is to generate a clean binding and use it to parse the ERB. Here's my take on it (ruby 2.2.x):
正如其他人所说,要用一些变量来评估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)
I think with eval
and without **
same can be made working with older ruby than 2.1
我认为使用eval和不使用** *的方法都可以使用比2.1更老的ruby