Posts tagged "resolvers"

It is common in Rails 3.0 applications that you want to provide default views for a group of controllers. Let’s say you have a bunch of controllers inside the Admin namespace and you would like each action to fallback to a default template. So if you are rendering the index action for Admin::PostsController and “app/views/admin/posts/index.html.*” is not available, it should then render “app/views/admin/defaults/index.html”.

There are several ways to implement this feature at the controller level. It mainly relies on trying to render the original template and then rescue ActionView::MissingTemplate. If this error is rescued, you then render the default one. However, there is a considerable performance overhead in this approach as it needs to pass through the rendering and template lookup stack twice.

Luckily, since Rails 3.0, we have a new abstraction called resolvers that holds the logic to find a template. I explain comprehensively how resolvers work and their API in my book Crafting Rails Applications. So here I would just show the basics to get this functionality working.
First, we need to define a DefaultResolver, it could be done inside the lib directory:

class MyResolver < ::ActionView::FileSystemResolver
  def initialize
    super("app/views")
  end
 
  def find_templates(name, prefix, partial, details)
    super(name, "admin/defaults", partial, details)
  end
end

Our new resolver simply inherits from ActionView::FileSystemResolver and does two changes: Overrides the initialize method so the view path defaults to “app/views” inside our application and overrides find_templates. The find_templates method receives the template name, a prefix (i.e. the controller path), a boolean marking if the template is a partial or not and a hash of details. In the example above, we simply ignore the prefix given and hardcode it to “admin/defaults”.

Now, assuming that all controllers inside the Admin namespace inherit from an Admin::ApplicationController, we can add default views to all of them by adding the following line:

class Admin::ApplicationController < ActionController::Base
  append_view_path MyResolver.new
end

And we are done! The view_paths holds a list of paths and/or resolvers that the controller will look for templates until one is found. If none is found, an ActionView::MissingTemplate is raised. Since we used append_view_paths, our resolver was added after the “app/views” path, used by default in all controllers.

As you may have guessed, resolvers are a powerful abstraction that allows you to retrieve templates from anywhere, including the database, which is the example given in Crafting Rails Applications.

Finally, template inheritance was a feature recently added to Rails master (upcoming Rails 3.1), so you won’t need to create your custom resolver as above. There is a good wrap up about this feature in Rails Edge.