Yehuda Katz is a member of the Ember.js, Ruby on Rails and jQuery Core Teams; he spends his daytime hours at the startup he founded, Tilde Inc.. Yehuda is co-author of best-selling jQuery in Action and Rails 3 in Action. He spends most of his time hacking on open source—his main projects, along with others, like Thor, Handlebars and Janus—or traveling the world doing evangelism work. He can be found on Twitter as @wycats.
Inherited Templates With Rails
October 4th, 2009
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.

Jeremy McAnally, Posted October 4, 2009, 10:58 pm
Cool, this looks useful.
What’s the difference between this and something like content_for, though? I don’t see a whole ton of difference in the behavior, unless it’s way too late to try to be absorbing tech blog entries and I’m missing something. :)
DEkart, Posted October 5, 2009, 12:10 am
This should be really helpful if will allow to override templates and partials from engines.
Loren Norman, Posted October 5, 2009, 12:11 am
So (what do you think about | how does this affect) the techniques suggested in the “Using Nested Layouts” section of the current Rails Guides?
http://guides.rubyonrails.org/layouts_and_rendering.html#using-nested-layouts
I’ve used this technique a bit and always felt that it left something to be desired. I’m not sure if your approach has benefits or drawbacks to it?
Thanks!
wycats, Posted October 5, 2009, 12:18 am
@DEkart You’d absolutely be able to do that :-D
@jeremy the main difference is that content_for is global, so you’d have to be careful about not using a name that was already in use somewhere else. Probably not a huge problem for smallish uses of this technique, but I could imagine it getting out of hand in cases like what @DEkart is talking about (gem’ified templates that are designed to be inherited from).
Also, the syntax for using content_for is a bit uglier and less obviously connected.
In the parent:
In the child:
So again, the biggest issue is in the use of “global” names, but I personally find the aesthetic of keeping it all together to be an improvement as well.
wycats, Posted October 5, 2009, 12:23 am
@loren I’d say the biggest difference is that that’s “official”, in a Rails guide and vetted, while mine is a quick hack I worked up for a blog post. That said, as I said to @jeremy, I like the aesthetic of having this stuff self-contained. I could also trivially add a feature that would allow default content. The right thing is probably for me to release a plugin with this technique and let the bugs shake out.
Dru, Posted October 5, 2009, 7:08 am
What about layout for partial?
Isn’t this is the same thing?
‘header’, :layout => ‘boxed’, :locals => {:post => @post} %> ?
wycats, Posted October 5, 2009, 7:27 am
@dru the main difference is that you can only pass objects that already exist via
locals, while the code I showed allows you to use ERB to create the contents of the locals, which is more in line with the idea of “inherited templates”.David R, Posted October 5, 2009, 10:41 am
Ack! I’m hoping now that you’ve had your coffee you realize you wrote this backwards: “the syntax for using content_for is a bit uglier”…
What that means is this:
Content
Is uglier than this:
Content
…?! I get that the functionality is not identical, but… Maybe I’m misreading something? Or, maybe you’ve been spending too much time looking at Django templates ;)
Kyle, Posted November 20, 2010, 9:50 pm
Hey Yehuda, do you think you could shape this into a Rails 3 plugin? I’ve been trying to implement this, as a learning experience and because I think inherited templates are superior, but am running up against a bunch of walls.
Also, I figure this is a good time to say thanks for all the work you’ve done/are doing for Rails/Ruby. It is really really appreciated.
Aidan Feldman, Posted January 14, 2011, 10:43 am
I came across another solution for nested templates here:
http://m.onkey.org/nested-layouts
It is the cleanest of the solutions I’ve seen, though I am having trouble getting it working in Rails 3.0.3.