Dan Chak’s Blog

Nested layouts in Rails

Posted in Uncategorized by Dan Chak on December 22, 2009

I’m working on a new pet project, and I want to keep my layouts nice and DRY. That means I need to nest my layouts. Unfortunately, googling around for how folks were implementing nested layouts put me on the right path, but nothing felt generic enough. I did get put on the right path, though. This post describes my current solution.

The problem scenario is two-fold:

First, we assume that we’re trying to insert a controller-specific layout layer between the most generic layout and the content from the current action’s template.

Generic Header
– Controller-specific header
—- Action-specific content
– Controller-specific footer
Generic footer

Second, the controller may have some inheritance hierarchy that’s more complicated that just inhering from ApplicationController, and I want to render the most specific layout available. For example, my hierarchy might look like this:

  • UsersController (defaults for all user-accessible controllers)
    • AccountController
    • PuppiesController
  • AdminController (defaults for all admin-accessible controllers
    • SomeAdminThingController

If I’m rendering an action in PuppiesController, I want to first try to render layouts/_puppies.rhtml. If that’s not available, then I’ll try layouts/_users.rhtml. And if there’s no middle-layer layout available, I just want to render the content.

Here’s the solution:

First, set your default layout in application_controller.rb:

layout 'default'

Ensure you’ve got a matching default.rhtml in app/views/layouts/.

Then, you would place the following method in helpers/application_helper.rb:

module ApplicationHelper
  def render_best_inner_layout
    layouts = []
    c = controller.class
    while c != ApplicationController do
      layouts << c.controller_name
      c = c.superclass
    end
    while (_layout = layouts.shift)
      begin
        return render :partial => "/layouts/#{_layout}"
      rescue ActionView::MissingTemplate
      end
    end
    return false
  end
end

Finally, in your default layout, you call that method where you want to render the nested layout. Notice how if false is returned by the helper method, meaning no mid-tier layout was found, we simply yield.

<%= render_best_inner_layout || yield %>

The limitation is that this doesn’t nest infinitely. E.g., if you have a layout nested layout for ‘users’ and‘puppies’, only puppies will be used, rather than chaining users followed by puppies. This suits my current needs, so I’ll let others improve on it as needed.

One Response

Subscribe to comments with RSS.

  1. Pratik said, on December 22, 2009 at 11:41 am

    You might wanna checkout my take as well : http://m.onkey.org/2009/7/7/nested-layouts


Leave a Reply