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 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.