I have an ERB template inlined into Ruby code:
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.
The error is:
(erb):1: undefined local variable or method `current' for main:Object (NameError)
How do I fix this?
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):
I think with
eval
and without**
same can be made working with older ruby than 2.1For a simple solution, use OpenStruct:
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 returnsnil
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!).So here is another solution, more verbose but without any of these problems:
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)
.In the code from original question, just replace
with
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.)
EDIT: This is a dirty workaround. Please see my other answer.
It's totally strange, but adding
before the "for-each" loop fixes the problem.
God bless scripting languages and their "language features"...
Simple solution using Binding:
Got it!
I create a bindings class
and pass an instance to ERB
The .erb template file looks like this: