Blog Redesign and some undocumented ERB

(Update: Some minor clarification regarding the performance cost of eval())

Visitors to the site might notice that there was a drastic (and long called for) redesign of the site. It is not, I assure you, the sudden manifestation of artistic ability on my part; rather, it was some great work done by Erik Goens, which has made the website look like something a little better than "derelict".

I've got lots to say about Text Tumble, and maybe even a little bit to contribute about Objective C (pending some exploratory work on my own part), but I'm afraid I don't have enough ready yet to go in depth. So, in the short term, allow me to share a little bit of undocumented ERB capability that came to me by way of Eric Hodel's Cached ERB Template class.

Background: ERB is the library used to drive most Rails templates. When you write a view template and use this form:

This is my html file, <%= @some_ruby_variable %>

it will be processed by the ERB library (which is part of your Ruby Standard Library) and turned into Ruby code which may be executed. You can see it for yourself in irb:

require "erb"
doc = ERB.new("this is my template <%= @var >")
puts doc.src.inspect

The "src" attribute of the doc variable is the Ruby code that will be executed when the template is rendered. You can evaluate it yourself, like so:

eval(doc.src)

or use the .result method to obtain the evaluated template result, like so:

doc.result

Each time you do this, you will evaluate the generated Ruby code anew. The cost of evaluating Ruby code programmatically is not trivial - it requires parsing and interpreting the string of Ruby code as if it were a separate file that had been loaded into the specified context.

You can bypass this by using some undocumented functions in the ERB library. The important method to consider in this case is the method named: "def_method". def_method is a method that allows you to evaulate the ruby code and "compiles" it into a method, allowing you to call the method later on without re-evaluating the string.

For instance:

doc = ERB.new("foo bar <%= @baz %>")
class Test; end
doc.def_method(Test, "call_dynamic_method")
t = Test.new
puts t.call_dynamic_method

def_method will take the generated Ruby source and evaluate it in a manner similar to this:

doc = ERB.new("foo bar <%= @baz %>")
method_name = "call_dynamic_method"
method_source = "def #{dynamic_method}; #{doc.src}; end"
Test.module_eval(method_source)

You can see a more elaborate version of this technique in the Rails source in ActionView::Renderable#compile!. That means that, if you are using Rails, that the performance gains from this are already applied to you (and have been since Rails 1.0). However, if you are using ERB on its own, it's a good technique to have at hand.

comments powered by Disqus