aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller/rescue.rb
diff options
context:
space:
mode:
authorMichael Koziarski <michael@koziarski.com>2007-11-06 06:02:24 +0000
committerMichael Koziarski <michael@koziarski.com>2007-11-06 06:02:24 +0000
commit788ece4799da9727dcc0f249c456041b01f62c98 (patch)
tree12c06212374b325ef66d235059c89a845c562a13 /actionpack/lib/action_controller/rescue.rb
parentaa1313dd3b754e5786117578ea4dd62c0f3b09da (diff)
downloadrails-788ece4799da9727dcc0f249c456041b01f62c98.tar.gz
rails-788ece4799da9727dcc0f249c456041b01f62c98.tar.bz2
rails-788ece4799da9727dcc0f249c456041b01f62c98.zip
Make rescue_from behave like rescue when dealing with subclasses. Closes #10079 [fxn]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@8081 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'actionpack/lib/action_controller/rescue.rb')
-rw-r--r--actionpack/lib/action_controller/rescue.rb57
1 files changed, 51 insertions, 6 deletions
diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb
index fa847a8274..7f82e38f54 100644
--- a/actionpack/lib/action_controller/rescue.rb
+++ b/actionpack/lib/action_controller/rescue.rb
@@ -41,8 +41,8 @@ module ActionController #:nodoc:
base.rescue_templates = Hash.new(DEFAULT_RESCUE_TEMPLATE)
base.rescue_templates.update DEFAULT_RESCUE_TEMPLATES
- base.class_inheritable_hash :rescue_handlers
- base.rescue_handlers = {}
+ base.class_inheritable_array :rescue_handlers
+ base.rescue_handlers = []
base.extend(ClassMethods)
base.class_eval do
@@ -55,13 +55,27 @@ module ActionController #:nodoc:
new.process(request, response, :rescue_action, exception)
end
- # Rescue exceptions raised in controller actions by passing at least one exception class and a :with option that contains the name of the method to be called to respond to the exception.
- # Handler methods that take one argument will be called with the exception, so that the exception can be inspected when dealing with it.
+ # Rescue exceptions raised in controller actions.
+ #
+ # <tt>rescue_from</tt> receives a series of exception classes or class
+ # names, and a trailing :with option with the name of a method or a Proc
+ # object to be called to handle them. Alternatively a block can be given.
+ #
+ # Handlers that take one argument will be called with the exception, so
+ # that the exception can be inspected when dealing with it.
+ #
+ # Handlers are inherited. They are searched from right to left, from
+ # bottom to top, and up the hierarchy. The handler of the first class for
+ # which exception.is_a?(klass) holds true is the one invoked, if any.
#
# class ApplicationController < ActionController::Base
# rescue_from User::NotAuthorized, :with => :deny_access # self defined exception
# rescue_from ActiveRecord::RecordInvalid, :with => :show_errors
#
+ # rescue_from 'MyAppError::Base' do |exception|
+ # render :xml => exception, :status => 500
+ # end
+ #
# protected
# def deny_access
# ...
@@ -78,7 +92,17 @@ module ActionController #:nodoc:
end
klasses.each do |klass|
- rescue_handlers[klass.name] = options[:with]
+ key = if klass.is_a?(Class) && klass <= Exception
+ klass.name
+ elsif klass.is_a?(String)
+ klass
+ else
+ raise(ArgumentError, "#{klass} is neither an Exception nor a String")
+ end
+
+ # Order is important, we put the pair at the end. When dealing with an
+ # exception we will follow the documented order going from right to left.
+ rescue_handlers << [key, options[:with]]
end
end
end
@@ -192,7 +216,28 @@ module ActionController #:nodoc:
end
def handler_for_rescue(exception)
- case handler = rescue_handlers[exception.class.name]
+ # We go from right to left because pairs are pushed onto rescue_handlers
+ # as rescue_from declarations are found.
+ _, handler = *rescue_handlers.reverse.detect do |klass_name, handler|
+ # The purpose of allowing strings in rescue_from is to support the
+ # declaration of handler associations for exception classes whose
+ # definition is yet unknown.
+ #
+ # Since this loop needs the constants it would be inconsistent to
+ # assume they should exist at this point. An early raised exception
+ # could trigger some other handler and the array could include
+ # precisely a string whose corresponding constant has not yet been
+ # seen. This is why we are tolerant to unkown constants.
+ #
+ # Note that this tolerance only matters if the exception was given as
+ # a string, otherwise a NameError will be raised by the interpreter
+ # itself when rescue_from CONSTANT is executed.
+ klass = self.class.const_get(klass_name) rescue nil
+ klass ||= klass_name.constantize rescue nil
+ exception.is_a?(klass) if klass
+ end
+
+ case handler
when Symbol
method(handler)
when Proc