public
Description: Ruby on Rails
Homepage: http://rubyonrails.org
Clone URL: git://github.com/rails/rails.git
Add tests for ActiveSupport::Rescuable. Use ActiveSupport::Rescuable in 
ActionController::Base.
lifo (author)
Sat Oct 04 13:48:18 -0700 2008
commit  259a7a844b53b7d508145cc61fed9e11581e5409
tree    ca91608fbab39d03b59137d018046606717883bd
parent  964dfc15572d7c10771c81ac3cbfb455dd5e378e
...
41
42
43
44
45
46
47
 
 
48
49
50
...
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
 
116
117
118
...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
...
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
...
41
42
43
 
 
 
44
45
46
47
48
49
...
53
54
55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
57
58
59
60
 
61
62
63
64
...
114
115
116
 
 
 
 
 
 
 
 
 
 
 
 
117
118
119
...
150
151
152
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
154
155
0
@@ -41,10 +41,9 @@ module ActionController #:nodoc:
0
       base.rescue_templates = Hash.new(DEFAULT_RESCUE_TEMPLATE)
0
       base.rescue_templates.update DEFAULT_RESCUE_TEMPLATES
0
 
0
-      base.class_inheritable_array :rescue_handlers
0
-      base.rescue_handlers = []
0
-
0
       base.extend(ClassMethods)
0
+      base.send :include, ActiveSupport::Rescuable
0
+
0
       base.class_eval do
0
         alias_method_chain :perform_action, :rescue
0
       end
0
@@ -54,65 +53,12 @@ module ActionController #:nodoc:
0
       def process_with_exception(request, response, exception) #:nodoc:
0
         new.process(request, response, :rescue_action, exception)
0
       end
0
-
0
-      # Rescue exceptions raised in controller actions.
0
-      #
0
-      # <tt>rescue_from</tt> receives a series of exception classes or class
0
-      # names, and a trailing <tt>:with</tt> option with the name of a method
0
-      # or a Proc object to be called to handle them. Alternatively a block can
0
-      # be given.
0
-      #
0
-      # Handlers that take one argument will be called with the exception, so
0
-      # that the exception can be inspected when dealing with it.
0
-      #
0
-      # Handlers are inherited. They are searched from right to left, from
0
-      # bottom to top, and up the hierarchy. The handler of the first class for
0
-      # which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if
0
-      # any.
0
-      #
0
-      #   class ApplicationController < ActionController::Base
0
-      #     rescue_from User::NotAuthorized, :with => :deny_access # self defined exception
0
-      #     rescue_from ActiveRecord::RecordInvalid, :with => :show_errors
0
-      #
0
-      #     rescue_from 'MyAppError::Base' do |exception|
0
-      #       render :xml => exception, :status => 500
0
-      #     end
0
-      #
0
-      #     protected
0
-      #       def deny_access
0
-      #         ...
0
-      #       end
0
-      #
0
-      #       def show_errors(exception)
0
-      #         exception.record.new_record? ? ...
0
-      #       end
0
-      #   end
0
-      def rescue_from(*klasses, &block)
0
-        options = klasses.extract_options!
0
-        unless options.has_key?(:with)
0
-          block_given? ? options[:with] = block : raise(ArgumentError, "Need a handler. Supply an options hash that has a :with key as the last argument.")
0
-        end
0
-
0
-        klasses.each do |klass|
0
-          key = if klass.is_a?(Class) && klass <= Exception
0
-            klass.name
0
-          elsif klass.is_a?(String)
0
-            klass
0
-          else
0
-            raise(ArgumentError, "#{klass} is neither an Exception nor a String")
0
-          end
0
-
0
-          # Order is important, we put the pair at the end. When dealing with an
0
-          # exception we will follow the documented order going from right to left.
0
-          rescue_handlers << [key, options[:with]]
0
-        end
0
-      end
0
     end
0
 
0
     protected
0
       # Exception handler called when the performance of an action raises an exception.
0
       def rescue_action(exception)
0
-        rescue_action_with_handler(exception) || rescue_action_without_handler(exception)
0
+        rescue_with_handler(exception) || rescue_action_without_handler(exception)
0
       end
0
 
0
       # Overwrite to implement custom logging of errors. By default logs as fatal.
0
@@ -168,18 +114,6 @@ module ActionController #:nodoc:
0
         render_for_file(rescues_path("layout"), response_code_for_rescue(exception))
0
       end
0
 
0
-      # Tries to rescue the exception by looking up and calling a registered handler.
0
-      def rescue_action_with_handler(exception)
0
-        if handler = handler_for_rescue(exception)
0
-          if handler.arity != 0
0
-            handler.call(exception)
0
-          else
0
-            handler.call
0
-          end
0
-          true # don't rely on the return value of the handler
0
-        end
0
-      end
0
-
0
       def rescue_action_without_handler(exception)
0
         log_error(exception) if logger
0
         erase_results if performed?
0
@@ -216,36 +150,6 @@ module ActionController #:nodoc:
0
         rescue_responses[exception.class.name]
0
       end
0
 
0
-      def handler_for_rescue(exception)
0
-        # We go from right to left because pairs are pushed onto rescue_handlers
0
-        # as rescue_from declarations are found.
0
-        _, handler = *rescue_handlers.reverse.detect do |klass_name, handler|
0
-          # The purpose of allowing strings in rescue_from is to support the
0
-          # declaration of handler associations for exception classes whose
0
-          # definition is yet unknown.
0
-          #
0
-          # Since this loop needs the constants it would be inconsistent to
0
-          # assume they should exist at this point. An early raised exception
0
-          # could trigger some other handler and the array could include
0
-          # precisely a string whose corresponding constant has not yet been
0
-          # seen. This is why we are tolerant to unknown constants.
0
-          #
0
-          # Note that this tolerance only matters if the exception was given as
0
-          # a string, otherwise a NameError will be raised by the interpreter
0
-          # itself when rescue_from CONSTANT is executed.
0
-          klass = self.class.const_get(klass_name) rescue nil
0
-          klass ||= klass_name.constantize rescue nil
0
-          exception.is_a?(klass) if klass
0
-        end
0
-
0
-        case handler
0
-        when Symbol
0
-          method(handler)
0
-        when Proc
0
-          handler.bind(self)
0
-        end
0
-      end
0
-
0
       def clean_backtrace(exception)
0
         if backtrace = exception.backtrace
0
           if defined?(RAILS_ROOT)
...
1
2
 
 
3
4
5
...
1
2
3
4
5
6
7
0
@@ -1,5 +1,7 @@
0
 *Edge*
0
 
0
+* Add ActiveSupport::Rescuable module abstracting ActionController::Base rescue_from features. [Norbert Crombach, Pratik]
0
+
0
 * Switch from String#chars to String#mb_chars for the unicode proxy.  [Manfred Stienstra]
0
 
0
   This helps with 1.8.7 compatibility and also improves performance for some operations by reducing indirection.
...
56
57
58
 
 
59
60
61
...
56
57
58
59
60
61
62
63
0
@@ -56,6 +56,8 @@ require 'active_support/time_with_zone'
0
 
0
 require 'active_support/secure_random'
0
 
0
+require 'active_support/rescuable'
0
+
0
 I18n.load_path << File.dirname(__FILE__) + '/active_support/locale/en-US.yml'
0
 
0
 Inflector = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Inflector', 'ActiveSupport::Inflector')
...
1
 
2
3
4
 
5
 
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
25
 
26
27
28
...
46
47
48
 
49
50
51
52
53
 
54
55
56
57
58
59
60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
62
63
...
1
2
3
4
 
5
6
7
8
9
10
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
...
67
68
69
70
71
72
73
 
 
74
75
76
77
78
 
 
 
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
0
@@ -1,28 +1,49 @@
0
 module ActiveSupport
0
+  # Rescuable module adds support for easier exception handling.
0
   module Rescuable
0
     def self.included(base) # :nodoc:
0
-      base.class_inheritable_array :rescue_handlers
0
+      base.class_inheritable_accessor :rescue_handlers
0
       base.rescue_handlers = []
0
+
0
       base.extend(ClassMethods)
0
     end
0
 
0
     module ClassMethods
0
-      def enable_rescue_for(*methods)
0
-        methods.each do |method|
0
-          class_eval <<-EOS
0
-            def #{method}_with_rescue(*args, &block)
0
-              #{method}_without_rescue(*args, &block)
0
-            rescue Exception => exception
0
-              rescue_with_handler(exception)
0
-            end
0
-
0
-            alias_method_chain :#{method}, :rescue
0
-          EOS
0
-        end
0
-      end
0
-
0
+      # Rescue exceptions raised in controller actions.
0
+      #
0
+      # <tt>rescue_from</tt> receives a series of exception classes or class
0
+      # names, and a trailing <tt>:with</tt> option with the name of a method
0
+      # or a Proc object to be called to handle them. Alternatively a block can
0
+      # be given.
0
+      #
0
+      # Handlers that take one argument will be called with the exception, so
0
+      # that the exception can be inspected when dealing with it.
0
+      #
0
+      # Handlers are inherited. They are searched from right to left, from
0
+      # bottom to top, and up the hierarchy. The handler of the first class for
0
+      # which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if
0
+      # any.
0
+      #
0
+      #   class ApplicationController < ActionController::Base
0
+      #     rescue_from User::NotAuthorized, :with => :deny_access # self defined exception
0
+      #     rescue_from ActiveRecord::RecordInvalid, :with => :show_errors
0
+      #
0
+      #     rescue_from 'MyAppError::Base' do |exception|
0
+      #       render :xml => exception, :status => 500
0
+      #     end
0
+      #
0
+      #     protected
0
+      #       def deny_access
0
+      #         ...
0
+      #       end
0
+      #
0
+      #       def show_errors(exception)
0
+      #         exception.record.new_record? ? ...
0
+      #       end
0
+      #   end
0
       def rescue_from(*klasses, &block)
0
         options = klasses.extract_options!
0
+
0
         unless options.has_key?(:with)
0
           if block_given?
0
             options[:with] = block
0
@@ -46,18 +67,31 @@ module ActiveSupport
0
       end
0
     end
0
 
0
+    # Tries to rescue the exception by looking up and calling a registered handler.
0
     def rescue_with_handler(exception)
0
       if handler = handler_for_rescue(exception)
0
         handler.arity != 0 ? handler.call(exception) : handler.call
0
-      else
0
-        raise exception
0
+        true # don't rely on the return value of the handler
0
       end
0
     end
0
 
0
     def handler_for_rescue(exception)
0
-      # use reverse so what is added last is found first
0
-      _, handler = *rescue_handlers.reverse.detect do |klass_name, handler|
0
-        # allow strings to support constants that are not defined yet
0
+      # We go from right to left because pairs are pushed onto rescue_handlers
0
+      # as rescue_from declarations are found.
0
+      _, handler = Array(rescue_handlers).reverse.detect do |klass_name, handler|
0
+        # The purpose of allowing strings in rescue_from is to support the
0
+        # declaration of handler associations for exception classes whose
0
+        # definition is yet unknown.
0
+        #
0
+        # Since this loop needs the constants it would be inconsistent to
0
+        # assume they should exist at this point. An early raised exception
0
+        # could trigger some other handler and the array could include
0
+        # precisely a string whose corresponding constant has not yet been
0
+        # seen. This is why we are tolerant to unknown constants.
0
+        #
0
+        # Note that this tolerance only matters if the exception was given as
0
+        # a string, otherwise a NameError will be raised by the interpreter
0
+        # itself when rescue_from CONSTANT is executed.
0
         klass = self.class.const_get(klass_name) rescue nil
0
         klass ||= klass_name.constantize rescue nil
0
         exception.is_a?(klass) if klass

Comments