Sinatra erb :locals support added
Today in #sinatra, a question came from jshsu about using a the helper defined in the wiki, with ERB, using locals like you would in Rails.
It looked something like:
<%= partial(:_post, :locals => {:post => post} %>;In Rails, that passes a local variable called “post” into the template, with the value of whatever post is now. The problem is that ERB doesn’t support this nativlely. It’s sort of a hack to make it work.
The whole chunk of code:
def render_erb(content, options = {}) locals_opt = options.delete(:locals) || {} locals_code = "" locals_hash = {} locals_opt.each do |key, value| locals_code << "#{key} = locals_hash[:#{key}]\n" locals_hash[:"#{key}"] = value end body = ::ERB.new(content).src eval("#{locals_code}#{body}", binding) end
The initialization part - the locals variable contains key/value mappings containing all the values to pass into the template. locals_code and locals_hash are place holders for the code generation that happens in the next step. They need to be here because if we defined them in the each block below, they’d be block scoped and disappear at the end of that block. We don’t want that, so we need to pre-define them up here.
locals_opt = options.delete(:locals) || {} locals_code = "" locals_hash = {}
Code generation and storage of values - For each key/value pair in the locals hash, store it off in a locals_hash variable indexed by a symbol, and define the code to pull that value out a little later. The locals_code variable will be included in the rendered code in the next step.
locals_opt.each do |key, value| locals_code << "#{key} = locals_hash[:#{key}]\n" locals_hash[:"#{key}"] = value end
ERB rendering and eval() - The first statement here renders the erb template down to ruby code. Calling the src attribute on the rendered template gives back a string of ruby code matching that rendered template. Once we have that, we paste the locals_code to the top of the rendered template, and then calls eval with the current binding. The binding of course allows the locals_code’s references to locals_hash to resolve correctly.
body = ::ERB.new(content).src eval("#{locals_code}#{body}", binding)
May 27th, 2008 at 9:54 pm
Yay thanks so much for this!