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?
For a simple solution, use 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!).
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)
.