2011-04-02 22:45:07 -04:00
|
|
|
require 'erubis'
|
2010-04-22 06:00:13 -04:00
|
|
|
require 'active_support/configurable'
|
2010-06-19 11:15:21 -04:00
|
|
|
require 'active_support/descendants_tracker'
|
2010-06-02 16:56:41 -04:00
|
|
|
require 'active_support/core_ext/module/anonymous'
|
2015-09-01 14:30:37 -04:00
|
|
|
require 'active_support/core_ext/module/attr_internal'
|
2010-03-04 00:29:26 -05:00
|
|
|
|
2009-02-24 20:25:21 -05:00
|
|
|
module AbstractController
|
2012-04-07 16:26:41 -04:00
|
|
|
class Error < StandardError #:nodoc:
|
|
|
|
end
|
|
|
|
|
2014-05-11 08:53:50 -04:00
|
|
|
# Raised when a non-existing controller action is triggered.
|
|
|
|
class ActionNotFound < StandardError
|
2012-04-07 16:26:41 -04:00
|
|
|
end
|
2009-05-28 10:49:02 -04:00
|
|
|
|
2014-12-19 15:49:50 -05:00
|
|
|
# AbstractController::Base is a low-level API. Nobody should be
|
2010-08-25 13:51:20 -04:00
|
|
|
# using it directly, and subclasses (like ActionController::Base) are
|
2010-08-25 12:57:27 -04:00
|
|
|
# expected to provide their own +render+ method, since rendering means
|
2011-05-23 19:22:33 -04:00
|
|
|
# different things depending on the context.
|
2009-02-24 20:25:21 -05:00
|
|
|
class Base
|
|
|
|
attr_internal :response_body
|
2009-02-27 14:42:13 -05:00
|
|
|
attr_internal :action_name
|
2010-10-10 03:51:52 -04:00
|
|
|
attr_internal :formats
|
2009-04-30 18:13:40 -04:00
|
|
|
|
2010-04-22 06:00:13 -04:00
|
|
|
include ActiveSupport::Configurable
|
2010-06-19 11:15:21 -04:00
|
|
|
extend ActiveSupport::DescendantsTracker
|
2010-04-22 06:00:13 -04:00
|
|
|
|
2011-04-02 22:45:07 -04:00
|
|
|
undef_method :not_implemented
|
2009-04-30 18:13:40 -04:00
|
|
|
class << self
|
2009-05-28 10:49:02 -04:00
|
|
|
attr_reader :abstract
|
2009-06-08 19:14:38 -04:00
|
|
|
alias_method :abstract?, :abstract
|
2009-05-28 10:49:02 -04:00
|
|
|
|
2009-06-08 19:14:38 -04:00
|
|
|
# Define a controller as abstract. See internal_methods for more
|
|
|
|
# details.
|
2009-04-30 18:13:40 -04:00
|
|
|
def abstract!
|
|
|
|
@abstract = true
|
|
|
|
end
|
2009-05-28 10:49:02 -04:00
|
|
|
|
2012-10-25 13:49:04 -04:00
|
|
|
def inherited(klass) # :nodoc:
|
2013-03-18 05:31:24 -04:00
|
|
|
# Define the abstract ivar on subclasses so that we don't get
|
2012-10-25 13:49:04 -04:00
|
|
|
# uninitialized ivar warnings
|
|
|
|
unless klass.instance_variable_defined?(:@abstract)
|
|
|
|
klass.instance_variable_set(:@abstract, false)
|
|
|
|
end
|
|
|
|
super
|
|
|
|
end
|
|
|
|
|
2009-06-08 19:14:38 -04:00
|
|
|
# A list of all internal methods for a controller. This finds the first
|
|
|
|
# abstract superclass of a controller, and gets a list of all public
|
|
|
|
# instance methods on that abstract class. Public instance methods of
|
2010-11-30 10:00:47 -05:00
|
|
|
# a controller would normally be considered action methods, so methods
|
|
|
|
# declared on abstract classes are being removed.
|
|
|
|
# (ActionController::Metal and ActionController::Base are defined as abstract)
|
2009-04-30 18:13:40 -04:00
|
|
|
def internal_methods
|
|
|
|
controller = self
|
2012-10-25 13:49:04 -04:00
|
|
|
|
2009-04-30 18:13:40 -04:00
|
|
|
controller = controller.superclass until controller.abstract?
|
|
|
|
controller.public_instance_methods(true)
|
|
|
|
end
|
2009-05-28 10:49:02 -04:00
|
|
|
|
2009-06-08 19:14:38 -04:00
|
|
|
# A list of method names that should be considered actions. This
|
|
|
|
# includes all public instance methods on a controller, less
|
2014-12-19 15:49:50 -05:00
|
|
|
# any internal methods (see internal_methods), adding back in
|
2009-06-08 19:14:38 -04:00
|
|
|
# any methods that are internal, but still exist on the class
|
2015-01-06 15:33:31 -05:00
|
|
|
# itself.
|
2009-06-08 19:14:38 -04:00
|
|
|
#
|
|
|
|
# ==== Returns
|
2012-05-19 18:50:22 -04:00
|
|
|
# * <tt>Set</tt> - A set of all methods that should be considered actions.
|
2009-04-30 18:13:40 -04:00
|
|
|
def action_methods
|
2010-01-17 23:06:28 -05:00
|
|
|
@action_methods ||= begin
|
2009-04-30 18:13:40 -04:00
|
|
|
# All public instance methods of this class, including ancestors
|
2010-09-29 14:47:27 -04:00
|
|
|
methods = (public_instance_methods(true) -
|
2010-01-17 23:06:28 -05:00
|
|
|
# Except for public instance methods of Base and its ancestors
|
2010-09-29 14:47:27 -04:00
|
|
|
internal_methods +
|
2010-01-17 23:06:28 -05:00
|
|
|
# Be sure to include shadowed public instance methods of this class
|
2015-01-06 15:33:31 -05:00
|
|
|
public_instance_methods(false)).uniq.map(&:to_s)
|
2010-01-17 23:06:28 -05:00
|
|
|
|
2015-01-05 02:26:23 -05:00
|
|
|
methods.to_set
|
2010-01-17 23:06:28 -05:00
|
|
|
end
|
2009-04-30 18:13:40 -04:00
|
|
|
end
|
2009-12-30 19:33:54 -05:00
|
|
|
|
2010-08-04 12:58:18 -04:00
|
|
|
# action_methods are cached and there is sometimes need to refresh
|
2014-12-19 15:49:50 -05:00
|
|
|
# them. ::clear_action_methods! allows you to do that, so next time
|
2010-08-04 12:58:18 -04:00
|
|
|
# you run action_methods, they will be recalculated
|
|
|
|
def clear_action_methods!
|
|
|
|
@action_methods = nil
|
|
|
|
end
|
|
|
|
|
2009-12-30 19:33:54 -05:00
|
|
|
# Returns the full controller name, underscored, without the ending Controller.
|
2014-12-19 15:49:50 -05:00
|
|
|
#
|
2015-01-03 10:36:33 -05:00
|
|
|
# class MyApp::MyPostsController < AbstractController::Base
|
2015-06-22 12:36:01 -04:00
|
|
|
#
|
2014-12-19 15:49:50 -05:00
|
|
|
# end
|
2015-01-03 10:36:33 -05:00
|
|
|
#
|
2014-12-19 15:49:50 -05:00
|
|
|
# MyApp::MyPostsController.controller_path # => "my_app/my_posts"
|
2009-12-30 19:33:54 -05:00
|
|
|
#
|
|
|
|
# ==== Returns
|
2012-05-19 18:50:22 -04:00
|
|
|
# * <tt>String</tt>
|
2009-12-30 19:33:54 -05:00
|
|
|
def controller_path
|
2015-07-19 17:19:15 -04:00
|
|
|
@controller_path ||= name.sub(/Controller$/, ''.freeze).underscore unless anonymous?
|
2009-12-30 19:33:54 -05:00
|
|
|
end
|
2010-08-04 12:58:18 -04:00
|
|
|
|
2012-05-19 18:50:22 -04:00
|
|
|
# Refresh the cached action_methods when a new action_method is added.
|
2010-08-04 12:58:18 -04:00
|
|
|
def method_added(name)
|
|
|
|
super
|
|
|
|
clear_action_methods!
|
|
|
|
end
|
2009-02-24 20:25:21 -05:00
|
|
|
end
|
2009-05-28 10:49:02 -04:00
|
|
|
|
2009-04-30 18:13:40 -04:00
|
|
|
abstract!
|
2009-05-28 10:49:02 -04:00
|
|
|
|
2009-06-08 19:14:38 -04:00
|
|
|
# Calls the action going through the entire action dispatch stack.
|
2009-12-12 19:48:34 -05:00
|
|
|
#
|
2009-06-08 19:14:38 -04:00
|
|
|
# The actual method that is called is determined by calling
|
|
|
|
# #method_for_action. If no method can handle the action, then an
|
2014-05-11 08:41:23 -04:00
|
|
|
# AbstractController::ActionNotFound error is raised.
|
2009-06-08 19:14:38 -04:00
|
|
|
#
|
|
|
|
# ==== Returns
|
2010-08-25 12:57:27 -04:00
|
|
|
# * <tt>self</tt>
|
2009-12-22 14:17:27 -05:00
|
|
|
def process(action, *args)
|
2014-05-05 07:14:06 -04:00
|
|
|
@_action_name = action.to_s
|
2009-05-14 20:25:10 -04:00
|
|
|
|
2014-04-17 15:50:39 -04:00
|
|
|
unless action_name = _find_action_name(@_action_name)
|
2010-08-14 01:13:00 -04:00
|
|
|
raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}"
|
2009-03-17 21:04:22 -04:00
|
|
|
end
|
2009-05-15 18:57:12 -04:00
|
|
|
|
2009-12-14 17:07:46 -05:00
|
|
|
@_response_body = nil
|
|
|
|
|
2009-12-22 14:17:27 -05:00
|
|
|
process_action(action_name, *args)
|
2009-02-24 20:25:21 -05:00
|
|
|
end
|
2009-05-28 10:49:02 -04:00
|
|
|
|
2014-12-19 15:49:50 -05:00
|
|
|
# Delegates to the class' ::controller_path
|
2009-12-30 19:33:54 -05:00
|
|
|
def controller_path
|
|
|
|
self.class.controller_path
|
|
|
|
end
|
|
|
|
|
2014-12-19 15:49:50 -05:00
|
|
|
# Delegates to the class' ::action_methods
|
2010-01-17 22:51:07 -05:00
|
|
|
def action_methods
|
|
|
|
self.class.action_methods
|
2009-05-14 20:25:10 -04:00
|
|
|
end
|
2009-05-28 10:49:02 -04:00
|
|
|
|
2011-05-06 12:39:10 -04:00
|
|
|
# Returns true if a method for the action is available and
|
|
|
|
# can be dispatched, false otherwise.
|
2011-03-30 11:22:05 -04:00
|
|
|
#
|
2011-05-06 13:23:25 -04:00
|
|
|
# Notice that <tt>action_methods.include?("foo")</tt> may return
|
|
|
|
# false and <tt>available_action?("foo")</tt> returns true because
|
2012-05-23 13:12:49 -04:00
|
|
|
# this method considers actions that are also available
|
2011-05-06 12:39:10 -04:00
|
|
|
# through other means, for example, implicit render ones.
|
2012-05-19 18:50:22 -04:00
|
|
|
#
|
|
|
|
# ==== Parameters
|
|
|
|
# * <tt>action_name</tt> - The name of an action to be tested
|
2011-05-06 12:39:10 -04:00
|
|
|
def available_action?(action_name)
|
2014-04-17 15:50:39 -04:00
|
|
|
_find_action_name(action_name).present?
|
2011-03-30 11:22:05 -04:00
|
|
|
end
|
2010-04-22 06:00:13 -04:00
|
|
|
|
2014-06-19 18:26:29 -04:00
|
|
|
# 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
|
|
|
|
|
2011-03-30 11:22:05 -04:00
|
|
|
private
|
2009-05-15 18:57:12 -04:00
|
|
|
|
2011-05-06 12:39:10 -04:00
|
|
|
# Returns true if the name can be considered an action because
|
|
|
|
# it has a method defined in the controller.
|
|
|
|
#
|
|
|
|
# ==== Parameters
|
|
|
|
# * <tt>name</tt> - The name of an action to be tested
|
|
|
|
#
|
|
|
|
# :api: private
|
|
|
|
def action_method?(name)
|
|
|
|
self.class.action_methods.include?(name)
|
|
|
|
end
|
|
|
|
|
2010-01-17 22:51:07 -05:00
|
|
|
# Call the action. Override this in a subclass to modify the
|
|
|
|
# behavior around processing an action. This, and not #process,
|
|
|
|
# is the intended way to override action dispatching.
|
2011-05-06 12:39:10 -04:00
|
|
|
#
|
|
|
|
# Notice that the first argument is the method to be dispatched
|
|
|
|
# which is *not* necessarily the same as the action name.
|
2010-01-17 22:51:07 -05:00
|
|
|
def process_action(method_name, *args)
|
|
|
|
send_action(method_name, *args)
|
|
|
|
end
|
2009-05-15 18:57:12 -04:00
|
|
|
|
2010-01-17 22:51:07 -05:00
|
|
|
# Actually call the method associated with the action. Override
|
|
|
|
# this method if you wish to change how action methods are called,
|
|
|
|
# not to add additional behavior around it. For example, you would
|
|
|
|
# override #send_action if you want to inject arguments into the
|
|
|
|
# method.
|
|
|
|
alias send_action send
|
|
|
|
|
|
|
|
# If the action name was not found, but a method called "action_missing"
|
|
|
|
# was found, #method_for_action will return "_handle_action_missing".
|
|
|
|
# This method calls #action_missing with the current action name.
|
2011-03-30 11:22:05 -04:00
|
|
|
def _handle_action_missing(*args)
|
|
|
|
action_missing(@_action_name, *args)
|
2010-01-17 22:51:07 -05:00
|
|
|
end
|
|
|
|
|
2014-04-17 15:50:39 -04:00
|
|
|
# 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
|
2014-05-11 08:41:23 -04:00
|
|
|
# * false - No valid method name could be found.
|
|
|
|
# Raise AbstractController::ActionNotFound.
|
2014-04-17 15:50:39 -04:00
|
|
|
def _find_action_name(action_name)
|
|
|
|
_valid_action_name?(action_name) && method_for_action(action_name)
|
|
|
|
end
|
|
|
|
|
2010-01-17 22:51:07 -05:00
|
|
|
# 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
|
|
|
|
# method and return "_handle_action_missing" if one is found.
|
|
|
|
#
|
|
|
|
# Subclasses may override this method to add additional conditions
|
|
|
|
# that should be considered an action. For instance, an HTTP controller
|
|
|
|
# with a template matching the action name is considered to exist.
|
|
|
|
#
|
|
|
|
# If you override this method to handle additional cases, you may
|
|
|
|
# also provide a method (like _handle_method_missing) to handle
|
|
|
|
# the case.
|
|
|
|
#
|
|
|
|
# If none of these conditions are true, and method_for_action
|
2014-05-11 08:41:23 -04:00
|
|
|
# returns nil, an AbstractController::ActionNotFound exception will be raised.
|
2010-01-17 22:51:07 -05:00
|
|
|
#
|
|
|
|
# ==== Parameters
|
2010-08-25 12:57:27 -04:00
|
|
|
# * <tt>action_name</tt> - An action name to find a method name for
|
2010-01-17 22:51:07 -05:00
|
|
|
#
|
|
|
|
# ==== Returns
|
2010-08-25 13:51:20 -04:00
|
|
|
# * <tt>string</tt> - The name of the method that handles the action
|
2014-04-17 15:50:39 -04:00
|
|
|
# * <tt>nil</tt> - No method name could be found.
|
2010-01-17 22:51:07 -05:00
|
|
|
def method_for_action(action_name)
|
2012-10-14 11:08:25 -04:00
|
|
|
if action_method?(action_name)
|
|
|
|
action_name
|
|
|
|
elsif respond_to?(:action_missing, true)
|
|
|
|
"_handle_action_missing"
|
2010-01-17 22:51:07 -05:00
|
|
|
end
|
2009-05-15 18:57:12 -04:00
|
|
|
end
|
2014-04-17 15:50:39 -04:00
|
|
|
|
|
|
|
# Checks if the action name is valid and returns false otherwise.
|
|
|
|
def _valid_action_name?(action_name)
|
2014-06-19 11:17:42 -04:00
|
|
|
!action_name.to_s.include? File::SEPARATOR
|
2014-04-17 15:50:39 -04:00
|
|
|
end
|
2009-02-24 20:25:21 -05:00
|
|
|
end
|
2009-05-28 10:49:02 -04:00
|
|
|
end
|