/render_inheritable

A Rails 3 plugin that allows one to inherit or override single views and partials for controller hierarchies.

Primary LanguageRubyMIT LicenseMIT

RENDER INHERITABLE

A Rails 3 plugin that allows one to inherit or override single templates for controller hierarchies. Finally, also views and partials remain DRY!

As of Rails 3.1, the functionality of this plugin is built into the Rails core. So Render Inheritable is glad to go to retirement and enjoy life on its own.

In the default case, a template is searched in the current controller’s view folder. If it is not found there, the template with the same name in the view folder of the superclass controller is used. Have a look at the examples for more clarification.

Install the gem with

gem install render_inheritable

In your Rails application’s Gemfile, add

gem 'render_inheritable'

To use inheritable templates in a certain controller hierarchy, add this declaration to the respective base controller:

render_inheritable

That’s it, no more duplications in your views and partials.

To see render_inheritable in action, have a look at github.com/codez/dry_crud, which generates DRY and specifically extendable CRUD controller, views and helpers for Rails applications. Based on render_inheritable, you get overwritable, generic CRUD views for all the models you want.

Example

Suppose we have the following files in our Rails app:

# controllers/crud_controller.rb:

class CrudController < ApplicationController
  # use render inheritable for this controller hierarchy
  render_inheritable

  def index
    # set @entries in subclasses
  end
  ...
end

# controllers/messages_controller.rb:

class MessagesController < CrudController
  def index
    @entries = Message.all
  end
  ...
end

# views/crud/index.html.erb:

<h1>Listing</h1>

<%= render :partial => 'list' %>

<%= link_to 'New Entry', :url => {:action => 'new'} %>

# views/crud/_list.html.erb:

<ul>
  <% @entries.each do |entry| %>
 	 <li><%= render :partial => 'entry', :object => 'entry' %></li>
  <% end %>
</ul>

# views/crud/_entry.html.erb

<%= entry.to_s %>

Nothing sophisticated, but it will do to explain our little gem. With the call to render_inheritable in the CrudController, we tell all render calls in this controller, its subclasses and their corresponding views to perform a template lookup. What does this mean? When we call the index action of our MessagesController, no corresponding template is found in views/messages/index.html.erb. The lookup proceeds to the superclass controller, where it finds the template in views/crud/index.html.erb. The same holds for the two partials views/crud/_list.html.erb and views/crud/_entry.html.erb.

With this mechanism, we are able to define generic templates that are inherited to various subclass controllers. There is no need to duplicate templates when they basically look the same.

If we need to customize a certain part, it is possible to override a single template or partial. Say we would like to present a custom entry for messages. Just introduce the corresponding partial:

# views/messages/_entry.html.erb

<%= entry.title %>: <%= truncate(entry.body, :length => 20) %>

Now, the render :partial => 'entry' call in views/crud/_list.html.erb will find the partial in the views/messages folder and render it. The other two templates do not have to be touched and are still inherited form the views/crud folder. So it’s possible to customize certain snippets by specifically overriding partials. For this example, you could also create a partial views/messages/_list.html.erb to display a table instead of a list for messages.

Of course, the lookup not only works for HTML templates and partials, but for any format. Even page.replace 'element_id', :partial => 'element' calls in RJS templates benefit from this gem.

Wait! What if I want to render a specific partial, without getting it overriden somewhere else? No problem, just specifiy the complete path:

<%= render :partial => 'parent/show' %>

Custom Lookup Paths

To support more advanced lookups, it is possible to customize the used lookup path. You may override the method self.template_lookup_path(param = nil) in the controller where you defined render_inheritable. This method is expected to return an ordered list of controller_paths where the templates are searched in.

With the optional parameter param, definable in the controller instance method template_lookup_param, lookup paths may even be dynamic. This would allow you, for example, to perform a template lookup based on a type attribute from your model instance. Each type would get an own views subfolder, where specific views or partials might be overriden. These subfolders then would not need to match an existing controller.