Skip to content

Rails Template Inheritance

Mario Alberto Chavez edited this page Jul 16, 2013 · 1 revision

A Rails functionality that is somehow neglected is template inheritance. Is something that I see developers not use it very often and end up with a lot of view code duplication.

This functionality was introduced with Rails 3.0, it adds the functionality for a Rails controller to navigate controller's heirarchy to find a template to use, maybe this sounds quite vague, but let's use an example to make sense of this.

class PostsController < ApplicationController
  def index; #....; end
  def show; #...; end
end

class EventsController < ApplicationController
  def index; #....; end
  def show; #...; end
end

# We have index.html.erb and show.html.erb templates but only for 
# ApplicationController at
# app/views/application/index.html.erb
# app/views/application/show.html.erb

If we issue a get request on /posts, since we do not have an index template for PostsController, Rails is going to use PostController heirarchy to try to find an index template, in our example it will be app/views/application/index.html.erb, the same will happen is we issue a get request on /events.

To actually make this example works we will need a couple of helper methods that we can define as a part of a controller protocol. These methods will be:

# A collection method get a reference to the instance variable
# that represents a collection of objects
def colletion
  @posts
end

# A method to get a refrence to the instance variable that
# represents a single object
def resource
  @post
end

# An attributes method which returns an array of attributes 
# to be rendered on the views
def attributes
   %w(title body)
end

# A resource class method to access class definition of elements 
# in a collection
def resource_class
  Post
end

helper_method :resource_class, :collection, :resource, :attributes
protected :resource_class, :collection, :resource, :attributes

With these methods in place then we can define the index.html.erb template as follow:

<h1><%= resource_class.model_name.human.pluralize %></h1>

<table>
  <thead>
    <tr>
    <% attributes.each do |attr| %>
      <th><%= resource_class.human_attribute_name(attr) %></th>
    <% end %>
    </tr>
    <tr>
      <th></th>
    </tr>
  </thead>
  <tbody>
  <% collection.each do |resource| %>
    <tr>
      <% attributes.each do |attr| %>
        <td><%= resource[attr] %></td>
      <% end %>
      <td>
        <%= link_to 'Show', resource %>
      </td>
    </tr>
  <% end %>
  </tbody>
</table>

I will omit the code for show template, because I belive that by now you get the idea on having generic templates to work with template inheritance.

What about if you want to provide a different template for index action on EventsController? This is quite easy, just drop an index.html.erb template and Rails will use that template.

Template inheritance also works for partials, if you refear to partial that is not available at controllers' view folder, then Rails will look at controller's heirarchy for it.

What is next?

So far we have learn about template inheritance and how to create generic views for it, this will without doubt DRY your templates code.

A normal go from here is to try to DRY our controllers actions that are pretty much standard and repetitive. Inherited Resources gem helps you to do this and also take care of very edge cases.

Another option is to use my Restful gem which have an API based on Inherited Resources but is moving forward to change the API to be based on Template pattern with a set of useful hooks. Also there is a sample project that shows how to use Restful and how to use Template Inheritance with it.

Reference

  • Template Inheritance from EdgeRails.info link
  • #269 Template Inheritance from Railscasts.com link
Clone this wiki locally
You can’t perform that action at this time.