{"id":1959,"date":"2011-04-04T15:00:26","date_gmt":"2011-04-04T18:00:26","guid":{"rendered":"http:\/\/blog.plataformatec.com.br\/?p=1959"},"modified":"2011-04-04T17:39:16","modified_gmt":"2011-04-04T20:39:16","slug":"default-views-in-rails-3-0-with-custom-resolvers","status":"publish","type":"post","link":"https:\/\/blog.plataformatec.com.br\/2011\/04\/default-views-in-rails-3-0-with-custom-resolvers\/","title":{"rendered":"Default views in Rails 3.0 with custom resolvers"},"content":{"rendered":"

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<\/code> namespace and you would like each action to fallback to a default template. So if you are rendering the index action for Admin::PostsController<\/code> and “app\/views\/admin\/posts\/index.html.*” is not available, it should then render “app\/views\/admin\/defaults\/index.html”.<\/p>\n

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<\/code>. 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.<\/p>\n

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

\r\nclass MyResolver < ::ActionView::FileSystemResolver\r\n  def initialize\r\n    super(\"app\/views\")\r\n  end\r\n\r\n  def find_templates(name, prefix, partial, details)\r\n    super(name, \"admin\/defaults\", partial, details)\r\n  end\r\nend\r\n<\/pre>\n

Our new resolver simply inherits from ActionView::FileSystemResolver<\/code> and does two changes: Overrides the initialize<\/code> method so the view path defaults to \"app\/views\" inside our application and overrides find_templates<\/code>. The find_templates<\/code> 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\".<\/p>\n

Now, assuming that all controllers inside the Admin namespace inherit from an Admin::ApplicationController<\/code>, we can add default views to all of them by adding the following line:<\/p>\n

\r\nclass Admin::ApplicationController < ActionController::Base\r\n  append_view_path MyResolver.new\r\nend\r\n<\/pre>\n

And we are done! The view_paths<\/code> 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<\/code> is raised. Since we used append_view_paths<\/code>, our resolver was added after the \"app\/views\" path, used by default in all controllers.<\/p>\n

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<\/a>.<\/p>\n

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<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"

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.*” … \u00bb<\/a><\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[1],"tags":[126,115,116,145,146],"aioseo_notices":[],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/1959"}],"collection":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/comments?post=1959"}],"version-history":[{"count":6,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/1959\/revisions"}],"predecessor-version":[{"id":1981,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/1959\/revisions\/1981"}],"wp:attachment":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/media?parent=1959"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/categories?post=1959"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/tags?post=1959"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}