Yehuda Katz is a member of the Ruby on Rails core team, and lead developer of the Merb project. He is a member of the jQuery Core Team, and a core contributor to DataMapper. He contributes to many open source projects, like Rubinius and Johnson, and works on some he created himself, like Thor.

@nahi I believe he's using Clojure primitives

Archive for October, 2009

Inherited Templates With Rails

A nice feature of some template languages (for instance, Django’s) is the ability to create templates that can be “inherited” by other templates. In effect, the goal is to create a template that has some missing content, and let “inheritors” fill in that content downstream.

It’s not terribly difficult to add this feature to Rails; let’s first design the API. First, the parent template (_parent.html.erb):

Before in parent
<%= first_from_child %>
<%= second_from_child %>
After in parent

We just use normal partial locals for the parent API, because they’re a well-understood construct and pretty easy to work with. Next, the “subclass” (_child.html.erb):

Before in child
<% override_template("parent") do |template| -%>
  <% template.set_content(:first_from_child) do -%>first_from_child<% end -%>
  <% template.set_content(:second_from_child) do -%>second_from_child<% end -%>
<% end -%>
After in child

Here, we use Ruby’s block syntax to create a context for supplying the content for the template. We expect the following output:

Before in child
Before in parent
first_from_child
second_from_child
After in parent
After in child

There are two parts to this implementation. First, let’s create the implementation for the override_template method that will be exposed onto ActionView::Base:

module ActionView
  module InheritedTemplates
    class Collector
    end
 
    def override_template(name, &block)
      locals = Collector.collect(self, &block)
      concat(render :partial => name, :locals => locals)
    end
  end
 
  class Base
    include InheritedTemplates
  end
end

To start, we create the method, deferring the heavy lifting to the Collector.collect method. The API we’ve designed asks #collect to hand back a Hash of locals, which we can pass unmodified into the “super” template.

The collector is a pretty straight-forward implementation, but it deserves some explanation:

class Collector
  def self.collect(view)
    collector = new(view)
    yield collector
    collector.content
  end
 
  attr_reader :content
  def initialize(view)
    @view = view
    @content = {}
  end
 
  def set_content(name, &block)
    @content[name] = @view.capture(&block)
  end
end
 
def override_template(name, &block)
  locals = Collector.collect(self, &block)
  concat(render :partial => name, :locals => locals)
end

The first thing we do is define the collect method. It’s a basic DSL implementation, creating an instance of itself and evaluating the block in its context. In order to fully understand what’s going on here, you need to see what the block looks like, once it’s compiled from ERB into pure-Ruby:

override_template("parent") do |template|
  template.set_content(:first_from_child) do; @output_buffer << "first_from_child"; end
  template.set_content(:second_from_child) do; @output_buffer << "second_from_child"; end
end

We start by creating a new collector, yielding the collector to the block passed to override_template. Inside the block, template.set_content is called twice; each call sets a key and value in the @content Hash. When done, the Hash will look like {:first_from_child => "first_from_child", :second_from_child => "second_from_child"}. The collector then returns the content Hash.

When that’s done, override_template simply passes that locals Hash through to render :partial, and concats the output to the buffer.

Afterthought

Those following my blog might remember that I showed how we can improve block helpers by smarter compilation. If that fix was in place, here’s what _child.html.erb would look like:

Before in child
<%= override_template("parent") do |template| -%>
  <% template.set_content(:first_from_child) do -%>first_from_child<% end -%>
  <% template.set_content(:second_from_child) do -%>second_from_child<% end -%>
<% end -%>
After in child

More importantly, here’s what override_template would look like:

def override_template(name, &block)
  locals = Collector.collect(self, &block)
  render :partial => name, :locals => locals
end

We’re actually saved a bit of pain here in the unfixed case, because override_template can only be called from inside templates, so we don’t need to use block_called_from_erb? to disambiguate. Had this been a normal block helper, override_template would have looked like:

def override_template(name, &block)
  locals = Collector.collect(self, &block)
  response = render(:partial => name, :locals => locals)
 
  if block_called_from_erb?(&block)
    concat(response)
  else
    response
  end
end

Of course, realizing all of this is non-trivial, and the reason we’re going to be moving toward the simple block helper syntax above.

Share and Enjoy:
  • Digg
  • Reddit
  • HackerNews
  • Twitter

Emulating Smalltalk’s Conditionals in Ruby

If you follow my blog, you know that I enjoy emulating language features from other languages (like Python) in pure-Ruby. On the flight back from London, I read through Smalltalk Best Practice Patterns, and was reminded that Smalltalk doesn’t have built-in conditionals.

Instead, they use method calls (aka message sends) to do the heavy lifting:

anObject isNil
  ifTrue:  [ Transcript show: 'true'. ]
  ifFalse: [ Transcript show: 'false'. ].

In contrast, the typical way to do that in Ruby is:

if an_object.nil?
  puts "true"
else
  puts "false"
end

However, the Smalltalk approach is fully compatible with Ruby’s pure-OO approach (which is, in fact, inherited from Smalltalk in the first place). So why not be able to do:

(an_object.nil?).
  if_true  { puts "true" }.
  if_false { puts "false" }

In Ruby, unlike Smalltalk, message sends use a “.“, so we need to use periods between the message sends to link them together. With that exception, the semantics of the two languages are roughly identical. And, as usual, the implementation in Ruby is pretty straight-forward:

class Object
  def if_true
    yield
    self
  end
 
  def if_false
    self
  end
end
 
class NilClass
  def if_true
    self
  end
 
  def if_false
    yield
    self
  end
end
 
class FalseClass
  def if_true
    self
  end
 
  def if_false
    yield
    self
  end
end

In Ruby, false and nil are “falsy”, while all other values are “truthy”. Since everything in Ruby inherits from Object, we can simply define if_true and if_false on Object, FalseClass, and NilClass. If the block should be called (if_true on truthy values, or if_false on falsy values), we yield. In all cases, we return “self” so that we can chain if_true to if_false. And that’s it.

Of course, this means extending core classes, which some people are shy about doing, but it does mirror the semantics of Smalltalk.

Share and Enjoy:
  • Digg
  • Reddit
  • HackerNews
  • Twitter