Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create new binding and assign instance variables to it for availability in ERB?

Tags:

ruby

erb

I'm implementing HTML templating in a Ruby project (non-rails). To do this I'll be using ERB, but I have some concerns about the binding stuff.

First out, this is the method I got so far:

def self.template(template, data)
  template = File.read("#{ENV.root}/app/templates/#{template}.html.erb")

  template_binding = binding.clone

  data.each do |k, v|
    template_binding.local_variable_set(k, v)
  end

  ERB.new(template).result(template_binding)
end

To call it I'll just do

Email.template('email/hello', {
  name: 'Bill',
  age:  41
}

There are two issues with the current solution though.

First, I'm cloning the current binding. I want to create a new one. I tried Class.new.binding to create a new, but since binding is a private method it can't be obtained that way. The reason I want a new one is that I want to avoid the risk of instance variables leaking into or out from the ERB file (cloning only takes care of the latter case).

Second, I want the variables passed to the ERB file to be exposed as instance variables. Here I tried with template_binding.instance_variable_set, passing the plain hash key k which complained that it wasn't a valid instance variable name and "@#{k}", which did not complain but also didn't get available in the ERB code. The reason I want to use instance variables is that it's a convention that the people relying on this code is familiar with.

I have checked some topics here at Stack Overflow such as Render an ERB template with values from a hash, but the answers provided does not address the problems I'm discussing.

So in short, like the title: How to create new binding and assign instance variables to it for availability in ERB?

like image 945
matsve Avatar asked Oct 30 '25 15:10

matsve


1 Answers

1) No need to clone, new binding is created for you each time.

I have tested this in irb:

class A; def bind; binding; end; end
a = A.new
bind_1 = a.bind
bind_2 = a.bind

bind_1.local_variable_set(:x, 2)
=> 2
bind_1.local_variables
=> [:x]
bind_2.local_variables
=> []

2) Open the objects Eigenclass and add attr_accessor to it

class << template_binding  # this opens Eigenclass for object template_binding
  attr_accessor :x
end

So in ruby you can just open any class and add methods for it. Eigenclass means class of a single object - each object can have custom class definition. Coming from C# I couldn't imagine a situation where this would be used, until now. :)

Do this for each hash

data.each do |k, v|
  class << template_binding; attr_accessor k.to_sym; end
  template_binding.k = v
end
like image 115
Marko Avlijaš Avatar answered Nov 02 '25 12:11

Marko Avlijaš



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!