Written on June 27th, 2007.

With Erubis, it is trivial to use a context object that makes variables accessible as instance variables in templates. It is not obvious, but you can do exactly the same with ERB as well, with a bit of work. Imagine being able to do this:

assigns = { :adjective => 'awesome' }
puts 'This is <%= @adjective %>!'.eruby(assigns)

The first step is to create a class which will contain the instance variables that should be accessible inside the template. The class whose source is shown below builds its instance variables based off a hash argument.

class ERBContext
  def initialize(hash)
    hash.each_pair do |key, value|
      instance_variable_set('@' + key.to_s, value)
    end
  end
end

The next part involves the magic of Binding. A binding basically lets you evalutate code (using eval() or something) at some point in your program, and pretend the code was evaluated at the point where you got the binding.

In our ERB code, we’d like to evaluate the ERB template at some point in the program, but still pretend we’re evaluating the template inside the context object, so that we have access to its instance variables. To do so, add a method that returns a binding for that class:

class ERBContext
  def get_binding
    binding
  end
end

And we can finally use the context object:

assigns = { :adjective => 'awesome' }
ERB.new('This is <%= @adjective %>!')
erb_context = ERBContext.new(assigns)
erb_binding = erb_context.get_binding
puts erb.result(erb_binding)

That is not very pretty, and it takes a lot of code to evaluate a single template, so let’s put this in a new String method, like this:

class String
  def eruby(assigns={})
    ERB.new(self).result(ERBContext.new(assigns).get_binding)
  end
end

So now we can indeed do (as mentioned above):

assigns = { :adjective => 'awesome' }
puts 'This is <%= @adjective %>!'.eruby(assigns)

For completeness, here’s the full source code for this example:

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 eruby(assigns={})
    ERB.new(self).result(ERBContext.new(assigns).get_binding)
  end
end

Enjoy!