{"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 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 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. Our new resolver simply inherits from Now, assuming that all controllers inside the Admin namespace inherit from an And we are done! The 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}]}}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
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
\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
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
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
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