123456789_123456789_123456789_123456789_123456789_

Module: ActionView::Layouts

Relationships & Source Files
Namespace Children
Modules:
Extension / Inclusion / Inheritance Descendants
Included In:
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
Instance Chain:
Defined in: actionview/lib/action_view/layouts.rb

Overview

Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in repeated setups. The inclusion pattern has pages that look like this:

<%= render "application/header" %>
Hello World
<%= render "application/footer" %>

This approach is a decent way of keeping common structures isolated from the changing content, but it’s verbose and if you ever want to change the structure of these two includes, you’ll have to change all the templates.

With layouts, you can flip it around and have the common structure know where to insert changing content. This means that the header and footer are only mentioned in one place, like this:

// The header part of this layout
<%= yield %>
// The footer part of this layout

And then you have content pages that look like this:

hello world

At rendering time, the content page is computed and then inserted in the layout, like this:

// The header part of this layout
hello world
// The footer part of this layout

Accessing shared variables

Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with references that won’t materialize before rendering time:

<h1><%= @page_title %></h1>
<%= yield %>

…and content pages that fulfill these references at rendering time:

<% @page_title = "Welcome" %>
Off-world colonies offers you a chance to start a new life

The result after rendering is:

<h1>Welcome</h1>
Off-world colonies offers you a chance to start a new life

Layout assignment

You can either specify a layout declaratively (using the #layout class method) or give it the same name as your controller, and place it in app/views/layouts. If a subclass does not have a layout specified, it inherits its layout using normal Ruby inheritance.

For instance, if you have PostsController and a template named app/views/layouts/posts.html.erb, that template will be used for all actions in PostsController and controllers inheriting from PostsController.

If you use a module, for instance Weblog::PostsController, you will need a template named app/views/layouts/weblog/posts.html.erb.

Since all your controllers inherit from ApplicationController, they will use app/views/layouts/application.html.erb if no other layout is specified or provided.

Inheritance Examples

class BankController < ActionController::Base
  # bank.html.erb exists

class ExchangeController < BankController
  # exchange.html.erb exists

class CurrencyController < BankController

class InformationController < BankController
  layout "information"

class TellerController < InformationController
  # teller.html.erb exists

class EmployeeController < InformationController
  # employee.html.erb exists
  layout nil

class VaultController < BankController
  layout :access_level_layout

class TillController < BankController
  layout false

In these examples, we have three implicit lookup scenarios:

  • The BankController uses the “bank” layout.

  • The ExchangeController uses the “exchange” layout.

  • The CurrencyController inherits the layout from BankController.

However, when a layout is explicitly set, the explicitly set layout wins:

  • The InformationController uses the “information” layout, explicitly set.

  • The TellerController also uses the “information” layout, because the parent explicitly set it.

  • The EmployeeController uses the “employee” layout, because it set the layout to nil, resetting the parent configuration.

  • The VaultController chooses a layout dynamically by calling the access_level_layout method.

  • The TillController does not use a layout at all.

Types of layouts

Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can be done either by specifying a method reference as a symbol or using an inline method (as a proc).

The method reference is the preferred approach to variable layouts and is used like this:

class WeblogController < ActionController::Base
  layout :writers_and_readers

  def index
    # fetching posts
  end

  private
    def writers_and_readers
      logged_in? ? "writer_layout" : "reader_layout"
    end
end

Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing is logged in or not.

If you want to use an inline method, such as a proc, do something like this:

class WeblogController < ActionController::Base
  layout proc { |controller| controller.logged_in? ? "writer_layout" : "reader_layout" }
end

If an argument isn’t given to the proc, it’s evaluated in the context of the current controller anyway.

class WeblogController < ActionController::Base
  layout proc { logged_in? ? "writer_layout" : "reader_layout" }
end

Of course, the most common way of specifying a layout is still just as a plain template name:

class WeblogController < ActionController::Base
  layout "weblog_standard"
end

The template will be looked always in app/views/layouts/ folder. But you can point layouts folder direct also. layout "layouts/demo" is the same as layout "demo".

Setting the layout to nil forces it to be looked up in the filesystem and falls back to the parent behavior if none exists. Setting it to nil is useful to re-enable template lookup overriding a previous configuration set in the parent:

class ApplicationController < ActionController::Base
  layout "application"
end

class PostsController < ApplicationController
  # Will use "application" layout
end

class CommentsController < ApplicationController
  # Will search for "comments" layout and fall back to "application" layout
  layout nil
end

Conditional layouts

If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The :only and :except options can be passed to the layout call. For example:

class WeblogController < ActionController::Base
  layout "weblog_standard", except: :rss

  # ...

end

This will assign “weblog_standard” as the WeblogController’s layout for all actions except for the rss action, which will be rendered directly, without wrapping a layout around the rendered view.

Both the :only and :except condition can accept an arbitrary number of method references, so except: [ :rss, :text_only ] is valid, as is except: :rss.

Using a different layout in the action render call

If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above. Sometimes you’ll have exceptions where one action wants to use a different layout than the rest of the controller. You can do this by passing a :layout option to the render call. For example:

class WeblogController < ActionController::Base
  layout "weblog_standard"

  def help
    render action: "help", layout: "help"
  end
end

This will override the controller-wide “weblog_standard” layout, and will render the help action with the “help” layout instead.

Class Method Summary

::ActiveSupport::Concern - Extended

class_methods

Define class methods from given block.

included

Evaluate given block in context of base class, so that you can write class macros here.

prepended

Evaluate given block in context of base class, so that you can write class macros here.

append_features, prepend_features

Instance Attribute Summary

Rendering - Included

ViewPaths - Included

Instance Method Summary

Rendering - Included

#initialize, #render_to_body,
#view_context

An instance of a view class.

#view_context_class,
#_normalize_args

Normalize args by converting render “foo” to render action: “foo” and render “foo/bar” to render template: “foo/bar”.

#_normalize_options

Normalize options.

#_process_format

Assign the rendered format to look up context.

#_render_template

Find and render a template based on the options given.

#process

Override process to set up ::I18n proxy.

#view_renderer

Returns an object that is able to render templates.

ViewPaths - Included

#any_templates?,
#append_view_path

Append a path to the list of view paths for the current LookupContext.

#details_for_lookup,
#lookup_context

LookupContext is the object responsible for holding all information required for looking up templates, i.e. view paths and details.

#prepend_view_path

Prepend a path to the list of view paths for the current LookupContext.

#template_exists?,
#_prefixes

The prefixes used in render “foo” shortcuts.

DSL Calls

included

[ GitHub ]


210
211
212
213
214
215
# File 'actionview/lib/action_view/layouts.rb', line 210

included do
  class_attribute :_layout, instance_accessor: false
  class_attribute :_layout_conditions, instance_accessor: false, instance_reader: true, default: {}

  _write_layout_method
end

Instance Attribute Details

#_conditional_layout?Boolean (readonly, private)

[ GitHub ]

  
# File 'actionview/lib/action_view/layouts.rb', line 377

def _conditional_layout?
  true
end

#action_has_layout=(value) (rw)

[ GitHub ]

  
# File 'actionview/lib/action_view/layouts.rb', line 359

attr_internal_writer :action_has_layout

#action_has_layout?Boolean (rw)

Controls whether an action should be rendered using a layout. If you want to disable any layout settings for the current action so that it is rendered without a layout then either override this method in your controller to return false for that action or set the action_has_layout attribute to false before rendering.

[ GitHub ]

  
# File 'actionview/lib/action_view/layouts.rb', line 372

def action_has_layout?
  @_action_has_layout
end

Instance Method Details

#_default_layout(lookup_context, formats, require_layout = false) (private)

Returns the default layout for this controller. Optionally raises an exception if the layout could not be found.

Parameters

  • formats - The formats accepted to this layout

  • require_layout - If set to true and layout is not found, an ArgumentError exception is raised (defaults to false)

Returns

  • template - The template object for the default layout (or nil)

[ GitHub ]

  
# File 'actionview/lib/action_view/layouts.rb', line 415

def _default_layout(lookup_context, formats, require_layout = false)
  begin
    value = _layout(lookup_context, formats) if action_has_layout?
  rescue NameError => e
    raise e, "Could not render layout: #{e.message}"
  end

  if require_layout && action_has_layout? && !value
    raise ArgumentError,
      "There was no default layout for #{self.class} in #{view_paths.inspect}"
  end

  _normalize_layout(value)
end

#_include_layout?(options) ⇒ Boolean (private)

[ GitHub ]

  
# File 'actionview/lib/action_view/layouts.rb', line 430

def _include_layout?(options)
  !options.keys.intersect?([:body, :plain, :html, :inline, :partial]) || options.key?(:layout)
end

#_layout (private)

This will be overwritten by _write_layout_method

[ GitHub ]

  
# File 'actionview/lib/action_view/layouts.rb', line 382

def _layout(*); end

#_layout_for_option(name) (private)

Determine the layout for a given name, taking into account the name type.

Parameters

  • name - The name of the template

[ GitHub ]

  
# File 'actionview/lib/action_view/layouts.rb', line 388

def _layout_for_option(name)
  case name
  when String     then _normalize_layout(name)
  when Proc       then name
  when true       then Proc.new { |lookup_context, formats| _default_layout(lookup_context, formats, true)  }
  when :default   then Proc.new { |lookup_context, formats| _default_layout(lookup_context, formats, false) }
  when false, nil then nil
  else
    raise ArgumentError,
      "String, Proc, :default, true, or false, expected for `layout'; you passed #{name.inspect}"
  end
end

#_normalize_layout(value) (private)

[ GitHub ]

  
# File 'actionview/lib/action_view/layouts.rb', line 401

def _normalize_layout(value)
  value.is_a?(String) && !value.match?(/\blayouts/) ? "layouts/#{value}" : value
end

#_normalize_options(options)

This method is for internal use only.
[ GitHub ]

  
# File 'actionview/lib/action_view/layouts.rb', line 350

def _normalize_options(options) # :nodoc:
  super

  if _include_layout?(options)
    layout = options.delete(:layout) { :default }
    options[:layout] = _layout_for_option(layout)
  end
end

#initialize

This method is for internal use only.
[ GitHub ]

  
# File 'actionview/lib/action_view/layouts.rb', line 361

def initialize(*) # :nodoc:
  @_action_has_layout = true
  super
end