aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/abstract_controller
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/abstract_controller')
-rw-r--r--actionpack/lib/abstract_controller/asset_paths.rb2
-rw-r--r--actionpack/lib/abstract_controller/layouts.rb166
-rw-r--r--actionpack/lib/abstract_controller/logger.rb2
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb7
-rw-r--r--actionpack/lib/abstract_controller/view_paths.rb2
5 files changed, 120 insertions, 59 deletions
diff --git a/actionpack/lib/abstract_controller/asset_paths.rb b/actionpack/lib/abstract_controller/asset_paths.rb
index c2a6809f58..dd5f9a1942 100644
--- a/actionpack/lib/abstract_controller/asset_paths.rb
+++ b/actionpack/lib/abstract_controller/asset_paths.rb
@@ -4,7 +4,7 @@ module AbstractController
included do
config_accessor :asset_host, :asset_path, :assets_dir, :javascripts_dir,
- :stylesheets_dir, :default_asset_host_protocol
+ :stylesheets_dir, :default_asset_host_protocol, :relative_url_root
end
end
end
diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb
index bbf5efe565..8b6136d6ba 100644
--- a/actionpack/lib/abstract_controller/layouts.rb
+++ b/actionpack/lib/abstract_controller/layouts.rb
@@ -66,27 +66,40 @@ module AbstractController
# == Inheritance Examples
#
# class BankController < ActionController::Base
- # layout "bank_standard"
+ # # bank.html.erb exists
+ #
+ # class ExchangeController < BankController
+ # # exchange.html.erb exists
+ #
+ # class CurrencyController < BankController
#
# class InformationController < BankController
+ # layout "information"
#
- # class TellerController < BankController
+ # class TellerController < InformationController
# # teller.html.erb exists
#
- # class TillController < TellerController
+ # class EmployeeController < InformationController
+ # # employee.html.erb exists
+ # layout nil
#
# class VaultController < BankController
# layout :access_level_layout
#
- # class EmployeeController < BankController
- # layout nil
+ # class TillController < BankController
+ # layout false
+ #
+ # In these examples, we have three implicit lookup scenrios:
+ # * The BankController uses the "bank" layout.
+ # * The ExchangeController uses the "exchange" layout.
+ # * The CurrencyController inherits the layout from BankController.
#
- # In these examples:
- # * The InformationController uses the "bank_standard" layout, inherited from BankController.
- # * The TellerController follows convention and uses +app/views/layouts/teller.html.erb+.
- # * The TillController inherits the layout from TellerController and uses +teller.html.erb+ as well.
+ # 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 <tt>access_level_layout</tt> method.
- # * The EmployeeController does not use a layout at all.
+ # * The TillController does not use a layout at all.
#
# == Types of layouts
#
@@ -126,6 +139,22 @@ module AbstractController
# If no directory is specified for the template name, the template will by default be looked for in <tt>app/views/layouts/</tt>.
# Otherwise, it will be looked up relative to the template root.
#
+ # Setting the layout to nil forces it to be looked up in the filesystem and fallbacks 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 fallback "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
@@ -168,11 +197,12 @@ module AbstractController
included do
class_attribute :_layout_conditions
remove_possible_method :_layout_conditions
- delegate :_layout_conditions, :to => :'self.class'
self._layout_conditions = {}
_write_layout_method
end
+ delegate :_layout_conditions, :to => "self.class"
+
module ClassMethods
def inherited(klass)
super
@@ -188,7 +218,7 @@ module AbstractController
#
# ==== Returns
# * <tt> Boolean</tt> - True if the action has a layout, false otherwise.
- def action_has_layout?
+ def conditional_layout?
return unless super
conditions = _layout_conditions
@@ -244,43 +274,71 @@ module AbstractController
def _write_layout_method
remove_possible_method(:_layout)
- case defined?(@_layout) ? @_layout : nil
- when String
- self.class_eval %{def _layout; #{@_layout.inspect} end}, __FILE__, __LINE__
- when Symbol
- self.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
- def _layout
- #{@_layout}.tap do |layout|
- unless layout.is_a?(String) || !layout
- raise ArgumentError, "Your layout method :#{@_layout} returned \#{layout}. It " \
- "should have returned a String, false, or nil"
+ prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"]
+ name_clause = if name
+ <<-RUBY
+ lookup_context.find_all("#{_implied_layout_name}", #{prefixes.inspect}).first || super
+ RUBY
+ end
+
+ if defined?(@_layout)
+ layout_definition = case @_layout
+ when String
+ @_layout.inspect
+ when Symbol
+ <<-RUBY
+ #{@_layout}.tap do |layout|
+ unless layout.is_a?(String) || !layout
+ raise ArgumentError, "Your layout method :#{@_layout} returned \#{layout}. It " \
+ "should have returned a String, false, or nil"
+ end
end
- end
+ RUBY
+ when Proc
+ define_method :_layout_from_proc, &@_layout
+ "_layout_from_proc(self)"
+ when false
+ nil
+ when true
+ raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
+ when nil
+ name_clause
end
- ruby_eval
- when Proc
- define_method :_layout_from_proc, &@_layout
- self.class_eval %{def _layout; _layout_from_proc(self) end}, __FILE__, __LINE__
- when false
- self.class_eval %{def _layout; end}, __FILE__, __LINE__
- when true
- raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
- when nil
- if name
- _prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"]
+ else
+ # Add a deprecation if the parent layout was explicitly set and the child
+ # still does a dynamic lookup. In next Rails release, we should @_layout
+ # to be inheritable so we can skip the child lookup if the parent explicitly
+ # set the layout.
+ parent = self.superclass.instance_variable_get(:@_layout)
+ @_layout = nil
+ inspect = parent.is_a?(Proc) ? parent.inspect : parent
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def _layout
- if template_exists?("#{_implied_layout_name}", #{_prefixes.inspect})
- "#{_implied_layout_name}"
+ layout_definition = if parent.nil?
+ name_clause
+ elsif name
+ <<-RUBY
+ if template = lookup_context.find_all("#{_implied_layout_name}", #{prefixes.inspect}).first
+ ActiveSupport::Deprecation.warn 'Layout found at "#{_implied_layout_name}" for #{name} but parent controller ' \
+ 'set layout to #{inspect.inspect}. Please explicitly set your layout to "#{_implied_layout_name}" ' \
+ 'or set it to nil to force a dynamic lookup.'
+ template
else
super
end
- end
- RUBY
- end
+ RUBY
+ end
end
- self.class_eval { private :_layout }
+
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def _layout
+ if conditional_layout?
+ #{layout_definition}
+ else
+ #{name_clause}
+ end
+ end
+ private :_layout
+ RUBY
end
end
@@ -289,8 +347,7 @@ module AbstractController
if _include_layout?(options)
layout = options.key?(:layout) ? options.delete(:layout) : :default
- value = _layout_for_option(layout)
- options[:layout] = (value =~ /\blayouts/ ? value : "layouts/#{value}") if value
+ options[:layout] = _layout_for_option(layout)
end
end
@@ -305,6 +362,10 @@ module AbstractController
@_action_has_layout
end
+ def conditional_layout?
+ true
+ end
+
private
# This will be overwritten by _write_layout_method
@@ -316,9 +377,10 @@ module AbstractController
# * <tt>name</tt> - The name of the template
def _layout_for_option(name)
case name
- when String then name
- when true then _default_layout(true)
- when :default then _default_layout(false)
+ when String then _normalize_layout(name)
+ when Proc then name
+ when true then Proc.new { _default_layout(true) }
+ when :default then Proc.new { _default_layout(false) }
when false, nil then nil
else
raise ArgumentError,
@@ -326,6 +388,10 @@ module AbstractController
end
end
+ def _normalize_layout(value)
+ value.is_a?(String) && value !~ /\blayouts/ ? "layouts/#{value}" : value
+ end
+
# Returns the default layout for this controller.
# Optionally raises an exception if the layout could not be found.
#
@@ -337,17 +403,17 @@ module AbstractController
# * <tt>template</tt> - The template object for the default layout (or nil)
def _default_layout(require_layout = false)
begin
- layout_name = _layout if action_has_layout?
+ value = _layout if action_has_layout?
rescue NameError => e
raise e, "Could not render layout: #{e.message}"
end
- if require_layout && action_has_layout? && !layout_name
+ if require_layout && action_has_layout? && !value
raise ArgumentError,
"There was no default layout for #{self.class} in #{view_paths.inspect}"
end
- layout_name
+ _normalize_layout(value)
end
def _include_layout?(options)
diff --git a/actionpack/lib/abstract_controller/logger.rb b/actionpack/lib/abstract_controller/logger.rb
index 0b196119f4..8b36230397 100644
--- a/actionpack/lib/abstract_controller/logger.rb
+++ b/actionpack/lib/abstract_controller/logger.rb
@@ -7,7 +7,7 @@ module AbstractController
included do
config_accessor :logger
- extend ActiveSupport::Benchmarkable
+ include ActiveSupport::Benchmarkable
end
end
end
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 41fdc11196..d95770c049 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -66,12 +66,7 @@ module AbstractController
end
def view_context_class
- @_view_context_class || self.class.view_context_class
- end
-
- def initialize(*)
- @_view_context_class = nil
- super
+ @_view_context_class ||= self.class.view_context_class
end
# An instance of a view class. The default view class is ActionView::Base
diff --git a/actionpack/lib/abstract_controller/view_paths.rb b/actionpack/lib/abstract_controller/view_paths.rb
index e8394447a7..96118b940f 100644
--- a/actionpack/lib/abstract_controller/view_paths.rb
+++ b/actionpack/lib/abstract_controller/view_paths.rb
@@ -10,7 +10,7 @@ module AbstractController
self._view_paths.freeze
end
- delegate :find_template, :template_exists?, :view_paths, :formats, :formats=,
+ delegate :template_exists?, :view_paths, :formats, :formats=,
:locale, :locale=, :to => :lookup_context
module ClassMethods