Ruby templates: How to pass variables into inlined ERB?
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 endendtemplate = '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)
.
Got it!
I create a bindings class
class BindMe def initialize(key,val) @key=key @val=val end def get_binding return binding() endend
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 hereend
The .erb template file looks like this:
Key: <%= @key %>