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/base.rb88
-rw-r--r--actionpack/lib/abstract_controller/callbacks.rb122
-rw-r--r--actionpack/lib/abstract_controller/collector.rb12
-rw-r--r--actionpack/lib/abstract_controller/helpers.rb17
-rw-r--r--actionpack/lib/abstract_controller/railties/routes_helpers.rb6
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb85
-rw-r--r--actionpack/lib/abstract_controller/url_for.rb2
7 files changed, 209 insertions, 123 deletions
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index a84ed17bd4..c95b9a4097 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -8,10 +8,11 @@ module AbstractController
class Error < StandardError #:nodoc:
end
- class ActionNotFound < StandardError #:nodoc:
+ # Raised when a non-existing controller action is triggered.
+ class ActionNotFound < StandardError
end
- # <tt>AbstractController::Base</tt> is a low-level API. Nobody should be
+ # AbstractController::Base is a low-level API. Nobody should be
# using it directly, and subclasses (like ActionController::Base) are
# expected to provide their own +render+ method, since rendering means
# different things depending on the context.
@@ -56,21 +57,11 @@ module AbstractController
controller.public_instance_methods(true)
end
- # The list of hidden actions. Defaults to an empty array.
- # This can be modified by other modules or subclasses
- # to specify particular actions as hidden.
- #
- # ==== Returns
- # * <tt>Array</tt> - An array of method names that should not be considered actions.
- def hidden_actions
- []
- end
-
# A list of method names that should be considered actions. This
# includes all public instance methods on a controller, less
- # any internal methods (see #internal_methods), adding back in
+ # any internal methods (see internal_methods), adding back in
# any methods that are internal, but still exist on the class
- # itself. Finally, #hidden_actions are removed.
+ # itself.
#
# ==== Returns
# * <tt>Set</tt> - A set of all methods that should be considered actions.
@@ -81,25 +72,26 @@ module AbstractController
# Except for public instance methods of Base and its ancestors
internal_methods +
# Be sure to include shadowed public instance methods of this class
- public_instance_methods(false)).uniq.map { |x| x.to_s } -
- # And always exclude explicitly hidden actions
- hidden_actions.to_a
+ public_instance_methods(false)).uniq.map(&:to_s)
- # Clear out AS callback method pollution
- Set.new(methods.reject { |method| method =~ /_one_time_conditions/ })
+ methods.to_set
end
end
# action_methods are cached and there is sometimes need to refresh
- # them. clear_action_methods! allows you to do that, so next time
+ # them. ::clear_action_methods! allows you to do that, so next time
# you run action_methods, they will be recalculated
def clear_action_methods!
@action_methods = nil
end
# Returns the full controller name, underscored, without the ending Controller.
- # For instance, MyApp::MyPostsController would return "my_app/my_posts" for
- # controller_path.
+ #
+ # class MyApp::MyPostsController < AbstractController::Base
+ # end
+ # end
+ #
+ # MyApp::MyPostsController.controller_path # => "my_app/my_posts"
#
# ==== Returns
# * <tt>String</tt>
@@ -114,25 +106,20 @@ module AbstractController
end
end
- # Define some internal variables that should not be propagated to the view.
- def self.default_protected_instance_vars
- []
- end
-
abstract!
# Calls the action going through the entire action dispatch stack.
#
# The actual method that is called is determined by calling
# #method_for_action. If no method can handle the action, then an
- # ActionNotFound error is raised.
+ # AbstractController::ActionNotFound error is raised.
#
# ==== Returns
# * <tt>self</tt>
def process(action, *args)
- @_action_name = action_name = action.to_s
+ @_action_name = action.to_s
- unless action_name = method_for_action(action_name)
+ unless action_name = _find_action_name(@_action_name)
raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}"
end
@@ -141,12 +128,12 @@ module AbstractController
process_action(action_name, *args)
end
- # Delegates to the class' #controller_path
+ # Delegates to the class' ::controller_path
def controller_path
self.class.controller_path
end
- # Delegates to the class' #action_methods
+ # Delegates to the class' ::action_methods
def action_methods
self.class.action_methods
end
@@ -165,7 +152,15 @@ module AbstractController
# ==== Returns
# * <tt>TrueClass</tt>, <tt>FalseClass</tt>
def available_action?(action_name)
- method_for_action(action_name).present?
+ _find_action_name(action_name).present?
+ end
+
+ # Returns true if the given controller is capable of rendering
+ # a path. A subclass of +AbstractController::Base+
+ # may return false. An Email controller for example does not
+ # support paths, only full URLs.
+ def self.supports_path?
+ true
end
private
@@ -209,6 +204,24 @@ module AbstractController
end
# Takes an action name and returns the name of the method that will
+ # handle the action.
+ #
+ # It checks if the action name is valid and returns false otherwise.
+ #
+ # See method_for_action for more information.
+ #
+ # ==== Parameters
+ # * <tt>action_name</tt> - An action name to find a method name for
+ #
+ # ==== Returns
+ # * <tt>string</tt> - The name of the method that handles the action
+ # * false - No valid method name could be found.
+ # Raise AbstractController::ActionNotFound.
+ def _find_action_name(action_name)
+ _valid_action_name?(action_name) && method_for_action(action_name)
+ end
+
+ # Takes an action name and returns the name of the method that will
# handle the action. In normal cases, this method returns the same
# name as it receives. By default, if #method_for_action receives
# a name that is not an action, it will look for an #action_missing
@@ -223,14 +236,14 @@ module AbstractController
# the case.
#
# If none of these conditions are true, and method_for_action
- # returns nil, an ActionNotFound exception will be raised.
+ # returns nil, an AbstractController::ActionNotFound exception will be raised.
#
# ==== Parameters
# * <tt>action_name</tt> - An action name to find a method name for
#
# ==== Returns
# * <tt>string</tt> - The name of the method that handles the action
- # * <tt>nil</tt> - No method name could be found. Raise ActionNotFound.
+ # * <tt>nil</tt> - No method name could be found.
def method_for_action(action_name)
if action_method?(action_name)
action_name
@@ -238,5 +251,10 @@ module AbstractController
"_handle_action_missing"
end
end
+
+ # Checks if the action name is valid and returns false otherwise.
+ def _valid_action_name?(action_name)
+ !action_name.to_s.include? File::SEPARATOR
+ end
end
end
diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb
index d6c941832f..69f490b327 100644
--- a/actionpack/lib/abstract_controller/callbacks.rb
+++ b/actionpack/lib/abstract_controller/callbacks.rb
@@ -1,3 +1,5 @@
+require 'active_support/deprecation'
+
module AbstractController
module Callbacks
extend ActiveSupport::Concern
@@ -9,7 +11,7 @@ module AbstractController
included do
define_callbacks :process_action,
- terminator: ->(controller,_) { controller.response_body },
+ terminator: ->(controller, result_lambda) { result_lambda.call if result_lambda.is_a?(Proc); controller.response_body },
skip_after_callbacks_if_terminated: true
end
@@ -22,10 +24,21 @@ module AbstractController
end
module ClassMethods
- # If :only or :except are used, convert the options into the
- # :unless and :if options of ActiveSupport::Callbacks.
- # The basic idea is that :only => :index gets converted to
- # :if => proc {|c| c.action_name == "index" }.
+ # If +:only+ or +:except+ are used, convert the options into the
+ # +:if+ and +:unless+ options of ActiveSupport::Callbacks.
+ #
+ # The basic idea is that <tt>:only => :index</tt> gets converted to
+ # <tt>:if => proc {|c| c.action_name == "index" }</tt>.
+ #
+ # Note that <tt>:only</tt> has priority over <tt>:if</tt> in case they
+ # are used together.
+ #
+ # only: :index, if: -> { true } # the :if option will be ignored.
+ #
+ # Note that <tt>:if</tt> has priority over <tt>:except</tt> in case they
+ # are used together.
+ #
+ # except: :index, if: -> { true } # the :except option will be ignored.
#
# ==== Options
# * <tt>only</tt> - The callback should be run only for this action
@@ -42,21 +55,23 @@ module AbstractController
end
end
- # Skip before, after, and around action callbacks matching any of the names
- # Aliased as skip_filter.
+ # Skip before, after, and around action callbacks matching any of the names.
#
# ==== Parameters
# * <tt>names</tt> - A list of valid names that could be used for
# callbacks. Note that skipping uses Ruby equality, so it's
# impossible to skip a callback defined using an anonymous proc
- # using #skip_filter
+ # using #skip_action_callback
def skip_action_callback(*names)
skip_before_action(*names)
skip_after_action(*names)
skip_around_action(*names)
end
- alias_method :skip_filter, :skip_action_callback
+ def skip_filter(*names)
+ ActiveSupport::Deprecation.warn("#{callback}_filter is deprecated and will be removed in Rails 5.1. Use #{callback}_action instead.")
+ skip_action_callback(*names)
+ end
# Take callback names and an optional callback proc, normalize them,
# then call the block with each callback. This allows us to abstract
@@ -85,7 +100,6 @@ module AbstractController
# :call-seq: before_action(names, block)
#
# Append a callback before actions. See _insert_callbacks for parameter details.
- # Aliased as before_filter.
##
# :method: prepend_before_action
@@ -93,7 +107,6 @@ module AbstractController
# :call-seq: prepend_before_action(names, block)
#
# Prepend a callback before actions. See _insert_callbacks for parameter details.
- # Aliased as prepend_before_filter.
##
# :method: skip_before_action
@@ -101,7 +114,6 @@ module AbstractController
# :call-seq: skip_before_action(names)
#
# Skip a callback before actions. See _insert_callbacks for parameter details.
- # Aliased as skip_before_filter.
##
# :method: append_before_action
@@ -109,7 +121,6 @@ module AbstractController
# :call-seq: append_before_action(names, block)
#
# Append a callback before actions. See _insert_callbacks for parameter details.
- # Aliased as append_before_filter.
##
# :method: after_action
@@ -117,7 +128,6 @@ module AbstractController
# :call-seq: after_action(names, block)
#
# Append a callback after actions. See _insert_callbacks for parameter details.
- # Aliased as after_filter.
##
# :method: prepend_after_action
@@ -125,7 +135,6 @@ module AbstractController
# :call-seq: prepend_after_action(names, block)
#
# Prepend a callback after actions. See _insert_callbacks for parameter details.
- # Aliased as prepend_after_filter.
##
# :method: skip_after_action
@@ -133,7 +142,6 @@ module AbstractController
# :call-seq: skip_after_action(names)
#
# Skip a callback after actions. See _insert_callbacks for parameter details.
- # Aliased as skip_after_filter.
##
# :method: append_after_action
@@ -141,7 +149,6 @@ module AbstractController
# :call-seq: append_after_action(names, block)
#
# Append a callback after actions. See _insert_callbacks for parameter details.
- # Aliased as append_after_filter.
##
# :method: around_action
@@ -149,7 +156,6 @@ module AbstractController
# :call-seq: around_action(names, block)
#
# Append a callback around actions. See _insert_callbacks for parameter details.
- # Aliased as around_filter.
##
# :method: prepend_around_action
@@ -157,7 +163,6 @@ module AbstractController
# :call-seq: prepend_around_action(names, block)
#
# Prepend a callback around actions. See _insert_callbacks for parameter details.
- # Aliased as prepend_around_filter.
##
# :method: skip_around_action
@@ -165,7 +170,6 @@ module AbstractController
# :call-seq: skip_around_action(names)
#
# Skip a callback around actions. See _insert_callbacks for parameter details.
- # Aliased as skip_around_filter.
##
# :method: append_around_action
@@ -173,46 +177,52 @@ module AbstractController
# :call-seq: append_around_action(names, block)
#
# Append a callback around actions. See _insert_callbacks for parameter details.
- # Aliased as append_around_filter.
# set up before_action, prepend_before_action, skip_before_action, etc.
# for each of before, after, and around.
[:before, :after, :around].each do |callback|
- class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- # Append a before, after or around callback. See _insert_callbacks
- # for details on the allowed parameters.
- def #{callback}_action(*names, &blk) # def before_action(*names, &blk)
- _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
- set_callback(:process_action, :#{callback}, name, options) # set_callback(:process_action, :before, name, options)
- end # end
- end # end
-
- alias_method :#{callback}_filter, :#{callback}_action
-
- # Prepend a before, after or around callback. See _insert_callbacks
- # for details on the allowed parameters.
- def prepend_#{callback}_action(*names, &blk) # def prepend_before_action(*names, &blk)
- _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
- set_callback(:process_action, :#{callback}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true))
- end # end
- end # end
-
- alias_method :prepend_#{callback}_filter, :prepend_#{callback}_action
-
- # Skip a before, after or around callback. See _insert_callbacks
- # for details on the allowed parameters.
- def skip_#{callback}_action(*names) # def skip_before_action(*names)
- _insert_callbacks(names) do |name, options| # _insert_callbacks(names) do |name, options|
- skip_callback(:process_action, :#{callback}, name, options) # skip_callback(:process_action, :before, name, options)
- end # end
- end # end
-
- alias_method :skip_#{callback}_filter, :skip_#{callback}_action
-
- # *_action is the same as append_*_action
- alias_method :append_#{callback}_action, :#{callback}_action # alias_method :append_before_action, :before_action
- alias_method :append_#{callback}_filter, :#{callback}_action # alias_method :append_before_filter, :before_action
- RUBY_EVAL
+ define_method "#{callback}_action" do |*names, &blk|
+ _insert_callbacks(names, blk) do |name, options|
+ set_callback(:process_action, callback, name, options)
+ end
+ end
+
+ define_method "#{callback}_filter" do |*names, &blk|
+ ActiveSupport::Deprecation.warn("#{callback}_filter is deprecated and will be removed in Rails 5.1. Use #{callback}_action instead.")
+ send("#{callback}_action", *names, &blk)
+ end
+
+ define_method "prepend_#{callback}_action" do |*names, &blk|
+ _insert_callbacks(names, blk) do |name, options|
+ set_callback(:process_action, callback, name, options.merge(:prepend => true))
+ end
+ end
+
+ define_method "prepend_#{callback}_filter" do |*names, &blk|
+ ActiveSupport::Deprecation.warn("prepend_#{callback}_filter is deprecated and will be removed in Rails 5.1. Use prepend_#{callback}_action instead.")
+ send("prepend_#{callback}_action", *names, &blk)
+ end
+
+ # Skip a before, after or around callback. See _insert_callbacks
+ # for details on the allowed parameters.
+ define_method "skip_#{callback}_action" do |*names|
+ _insert_callbacks(names) do |name, options|
+ skip_callback(:process_action, callback, name, options)
+ end
+ end
+
+ define_method "skip_#{callback}_filter" do |*names, &blk|
+ ActiveSupport::Deprecation.warn("skip_#{callback}_filter is deprecated and will be removed in Rails 5.1. Use skip_#{callback}_action instead.")
+ send("skip_#{callback}_action", *names, &blk)
+ end
+
+ # *_action is the same as append_*_action
+ alias_method :"append_#{callback}_action", :"#{callback}_action"
+
+ define_method "append_#{callback}_filter" do |*names, &blk|
+ ActiveSupport::Deprecation.warn("append_#{callback}_filter is deprecated and will be removed in Rails 5.1. Use append_#{callback}_action instead.")
+ send("append_#{callback}_action", *names, &blk)
+ end
end
end
end
diff --git a/actionpack/lib/abstract_controller/collector.rb b/actionpack/lib/abstract_controller/collector.rb
index 09b9e7ddf0..ddd56b354a 100644
--- a/actionpack/lib/abstract_controller/collector.rb
+++ b/actionpack/lib/abstract_controller/collector.rb
@@ -23,7 +23,17 @@ module AbstractController
protected
def method_missing(symbol, &block)
- mime_constant = Mime.const_get(symbol.upcase)
+ const_name = symbol.upcase
+
+ unless Mime.const_defined?(const_name)
+ raise NoMethodError, "To respond to a custom format, register it as a MIME type first: " \
+ "http://guides.rubyonrails.org/action_controller_overview.html#restful-downloads. " \
+ "If you meant to respond to a variant like :tablet or :phone, not a custom format, " \
+ "be sure to nest your variant response within a format response: " \
+ "format.html { |html| html.tablet { ... } }"
+ end
+
+ mime_constant = Mime.const_get(const_name)
if Mime::SET.include?(mime_constant)
AbstractController::Collector.generate_method_for_mime(mime_constant)
diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb
index e77e4e01e9..109eff10eb 100644
--- a/actionpack/lib/abstract_controller/helpers.rb
+++ b/actionpack/lib/abstract_controller/helpers.rb
@@ -27,9 +27,6 @@ module AbstractController
end
module ClassMethods
- MissingHelperError = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('AbstractController::Helpers::ClassMethods::MissingHelperError',
- 'AbstractController::Helpers::MissingHelperError')
-
# When a class is inherited, wrap its helper module in a new module.
# This ensures that the parent class's module can be changed
# independently of the child class's.
@@ -153,7 +150,17 @@ module AbstractController
rescue LoadError => e
raise AbstractController::Helpers::MissingHelperError.new(e, file_name)
end
- file_name.camelize.constantize
+
+ mod_name = file_name.camelize
+ begin
+ mod_name.constantize
+ rescue LoadError
+ # dependencies.rb gives a similar error message but its wording is
+ # not as clear because it mentions autoloading. To the user all it
+ # matters is that a helper module couldn't be loaded, autoloading
+ # is an internal mechanism that should not leak.
+ raise NameError, "Couldn't find #{mod_name}, expected it to be defined in helpers/#{file_name}.rb"
+ end
when Module
arg
else
@@ -177,7 +184,7 @@ module AbstractController
module_name = name.sub(/Controller$/, '')
module_path = module_name.underscore
helper module_path
- rescue MissingSourceFile => e
+ rescue LoadError => e
raise e unless e.is_missing? "helpers/#{module_path}_helper"
rescue NameError => e
raise e unless e.missing_name? "#{module_name}Helper"
diff --git a/actionpack/lib/abstract_controller/railties/routes_helpers.rb b/actionpack/lib/abstract_controller/railties/routes_helpers.rb
index 6684f46f64..14b574e322 100644
--- a/actionpack/lib/abstract_controller/railties/routes_helpers.rb
+++ b/actionpack/lib/abstract_controller/railties/routes_helpers.rb
@@ -1,14 +1,14 @@
module AbstractController
module Railties
module RoutesHelpers
- def self.with(routes)
+ def self.with(routes, include_path_helpers = true)
Module.new do
define_method(:inherited) do |klass|
super(klass)
if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_routes_url_helpers) }
- klass.send(:include, namespace.railtie_routes_url_helpers)
+ klass.include(namespace.railtie_routes_url_helpers(include_path_helpers))
else
- klass.send(:include, routes.url_helpers)
+ klass.include(routes.url_helpers(include_path_helpers))
end
end
end
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 41f19fba78..5514213ad8 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -1,5 +1,8 @@
require 'active_support/concern'
require 'active_support/core_ext/class/attribute'
+require 'action_view'
+require 'action_view/view_paths'
+require 'set'
module AbstractController
class DoubleRenderError < Error
@@ -12,57 +15,74 @@ module AbstractController
module Rendering
extend ActiveSupport::Concern
+ include ActionView::ViewPaths
- def self.default_protected_instance_vars
- super.concat [:@_action_name, :@_response_body, :@_formats, :@_prefixes, :@_config]
+ # Normalizes arguments, options and then delegates render_to_body and
+ # sticks the result in <tt>self.response_body</tt>.
+ # :api: public
+ def render(*args, &block)
+ options = _normalize_render(*args, &block)
+ self.response_body = render_to_body(options)
+ _process_format(rendered_format, options) if rendered_format
+ self.response_body
end
# Raw rendering of a template to a string.
#
# It is similar to render, except that it does not
- # set the response_body and it should be guaranteed
+ # set the +response_body+ and it should be guaranteed
# to always return a string.
#
- # If a component extends the semantics of response_body
- # (as Action Controller extends it to be anything that
+ # If a component extends the semantics of +response_body+
+ # (as ActionController extends it to be anything that
# responds to the method each), this method needs to be
# overridden in order to still return a string.
# :api: plugin
def render_to_string(*args, &block)
+ options = _normalize_render(*args, &block)
+ render_to_body(options)
end
- # Raw rendering of a template.
- # :api: plugin
- def render_to_body(options = {})
- end
-
- # Normalize arguments, options and then delegates render_to_body and
- # sticks the result in self.response_body.
+ # Performs the actual template rendering.
# :api: public
- def render(*args, &block)
+ def render_to_body(options = {})
end
- # Return Content-Type of rendered content
+ # Returns Content-Type of rendered content
# :api: public
def rendered_format
+ Mime::TEXT
end
+ DEFAULT_PROTECTED_INSTANCE_VARIABLES = Set.new %w(
+ @_action_name @_response_body @_formats @_prefixes @_config
+ @_view_context_class @_view_renderer @_lookup_context
+ @_routes @_db_runtime
+ ).map(&:to_sym)
+
# This method should return a hash with assigns.
# You can overwrite this configuration per controller.
# :api: public
def view_assigns
- hash = {}
- (instance_variables - self.class.default_protected_instance_vars).each do |name|
- hash[name[1..-1]] = instance_variable_get(name)
- end
- hash
+ protected_vars = _protected_ivars
+ variables = instance_variables
+
+ variables.reject! { |s| protected_vars.include? s }
+ variables.each_with_object({}) { |name, hash|
+ hash[name.slice(1, name.length)] = instance_variable_get(name)
+ }
end
- # Normalize args by converting render "foo" to render :action => "foo" and
- # render "foo/bar" to render :file => "foo/bar".
+ # Normalize args by converting <tt>render "foo"</tt> to
+ # <tt>render :action => "foo"</tt> and <tt>render "foo/bar"</tt> to
+ # <tt>render :file => "foo/bar"</tt>.
# :api: plugin
def _normalize_args(action=nil, options={})
- options
+ if action.is_a? Hash
+ action
+ else
+ options
+ end
end
# Normalize options.
@@ -76,5 +96,26 @@ module AbstractController
def _process_options(options)
options
end
+
+ # Process the rendered format.
+ # :api: private
+ def _process_format(format, options = {})
+ end
+
+ # Normalize args and options.
+ # :api: private
+ def _normalize_render(*args, &block)
+ options = _normalize_args(*args, &block)
+ #TODO: remove defined? when we restore AP <=> AV dependency
+ if defined?(request) && request && request.variant.present?
+ options[:variant] = request.variant
+ end
+ _normalize_options(options)
+ options
+ end
+
+ def _protected_ivars # :nodoc:
+ DEFAULT_PROTECTED_INSTANCE_VARIABLES
+ end
end
end
diff --git a/actionpack/lib/abstract_controller/url_for.rb b/actionpack/lib/abstract_controller/url_for.rb
index 4a95e1f276..72d07b0927 100644
--- a/actionpack/lib/abstract_controller/url_for.rb
+++ b/actionpack/lib/abstract_controller/url_for.rb
@@ -11,7 +11,7 @@ module AbstractController
def _routes
raise "In order to use #url_for, you must include routing helpers explicitly. " \
- "For instance, `include Rails.application.routes.url_helpers"
+ "For instance, `include Rails.application.routes.url_helpers`."
end
module ClassMethods