diff options
author | Michael Koziarski <michael@koziarski.com> | 2007-11-06 06:02:24 +0000 |
---|---|---|
committer | Michael Koziarski <michael@koziarski.com> | 2007-11-06 06:02:24 +0000 |
commit | 788ece4799da9727dcc0f249c456041b01f62c98 (patch) | |
tree | 12c06212374b325ef66d235059c89a845c562a13 /actionpack/lib/action_controller/rescue.rb | |
parent | aa1313dd3b754e5786117578ea4dd62c0f3b09da (diff) | |
download | rails-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.rb | 57 |
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 |