Ruby templates: How to pass variables into inlined ERB?
Solution 1
Got it!
I create a bindings class
class BindMe
def initialize(key,val)
@key=key
@val=val
end
def get_binding
return binding()
end
end
and pass an instance to 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:
Key: <%= @key %>
Solution 2
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)
.
Solution 3
Simple solution using Binding:
b = binding
b.local_variable_set(:a, 'a')
b.local_variable_set(:b, 'b')
ERB.new(template).result(b)
Solution 4
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.)
Solution 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/
ivan_ivanovich_ivanoff
!!! Netbeans 6.7 RELEASED !!! C is far better and easier than Java! Why? It is easier to use void pointers and to do pointer arithmetic, than explaining the basic concepts of OOP to a C programmer. Joke of the century: PHP is good, because it works... "PHP programming" is an oxymoron. There is no PHP programming. There is only PHP scriptkidding. There are two types of people who use PHP: - those who don't know other languages, - and those who HAVE TO use it Java is to JavaScript what Car is to Carpet. The LHC is the wrong answer to the technological singularity.
Updated on July 09, 2022Comments
-
ivan_ivanovich_ivanoff almost 2 years
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?