Rails Template Inheritance
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.