rails / rails

Ruby on Rails

This URL has Read+Write access

rails / activesupport / lib / active_support / rescuable.rb
f8dbd416 » jeremy 2009-03-24 Explicit dependency on clas... 1 require 'active_support/core_ext/class/inheritable_attributes'
2
964dfc15 » norbert 2008-10-04 First draft of ActiveSuppor... 3 module ActiveSupport
259a7a84 » lifo 2008-10-04 Add tests for ActiveSupport... 4 # Rescuable module adds support for easier exception handling.
964dfc15 » norbert 2008-10-04 First draft of ActiveSuppor... 5 module Rescuable
6 def self.included(base) # :nodoc:
259a7a84 » lifo 2008-10-04 Add tests for ActiveSupport... 7 base.class_inheritable_accessor :rescue_handlers
964dfc15 » norbert 2008-10-04 First draft of ActiveSuppor... 8 base.rescue_handlers = []
259a7a84 » lifo 2008-10-04 Add tests for ActiveSupport... 9
964dfc15 » norbert 2008-10-04 First draft of ActiveSuppor... 10 base.extend(ClassMethods)
11 end
12
13 module ClassMethods
259a7a84 » lifo 2008-10-04 Add tests for ActiveSupport... 14 # Rescue exceptions raised in controller actions.
15 #
16 # <tt>rescue_from</tt> receives a series of exception classes or class
17 # names, and a trailing <tt>:with</tt> option with the name of a method
18 # or a Proc object to be called to handle them. Alternatively a block can
19 # be given.
20 #
21 # Handlers that take one argument will be called with the exception, so
22 # that the exception can be inspected when dealing with it.
23 #
24 # Handlers are inherited. They are searched from right to left, from
25 # bottom to top, and up the hierarchy. The handler of the first class for
26 # which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if
27 # any.
28 #
29 # class ApplicationController < ActionController::Base
30 # rescue_from User::NotAuthorized, :with => :deny_access # self defined exception
31 # rescue_from ActiveRecord::RecordInvalid, :with => :show_errors
32 #
33 # rescue_from 'MyAppError::Base' do |exception|
34 # render :xml => exception, :status => 500
35 # end
36 #
37 # protected
38 # def deny_access
39 # ...
40 # end
41 #
42 # def show_errors(exception)
43 # exception.record.new_record? ? ...
44 # end
45 # end
964dfc15 » norbert 2008-10-04 First draft of ActiveSuppor... 46 def rescue_from(*klasses, &block)
47 options = klasses.extract_options!
259a7a84 » lifo 2008-10-04 Add tests for ActiveSupport... 48
964dfc15 » norbert 2008-10-04 First draft of ActiveSuppor... 49 unless options.has_key?(:with)
50 if block_given?
51 options[:with] = block
52 else
53 raise ArgumentError, "Need a handler. Supply an options hash that has a :with key as the last argument."
54 end
55 end
56
57 klasses.each do |klass|
58 key = if klass.is_a?(Class) && klass <= Exception
59 klass.name
60 elsif klass.is_a?(String)
61 klass
62 else
63 raise ArgumentError, "#{klass} is neither an Exception nor a String"
64 end
65
66 # put the new handler at the end because the list is read in reverse
67 rescue_handlers << [key, options[:with]]
68 end
69 end
70 end
71
259a7a84 » lifo 2008-10-04 Add tests for ActiveSupport... 72 # Tries to rescue the exception by looking up and calling a registered handler.
964dfc15 » norbert 2008-10-04 First draft of ActiveSuppor... 73 def rescue_with_handler(exception)
74 if handler = handler_for_rescue(exception)
75 handler.arity != 0 ? handler.call(exception) : handler.call
259a7a84 » lifo 2008-10-04 Add tests for ActiveSupport... 76 true # don't rely on the return value of the handler
964dfc15 » norbert 2008-10-04 First draft of ActiveSuppor... 77 end
78 end
79
80 def handler_for_rescue(exception)
259a7a84 » lifo 2008-10-04 Add tests for ActiveSupport... 81 # We go from right to left because pairs are pushed onto rescue_handlers
82 # as rescue_from declarations are found.
983dc807 » jeremy 2008-11-06 Don't shadow local with bla... 83 _, rescuer = Array(rescue_handlers).reverse.detect do |klass_name, handler|
259a7a84 » lifo 2008-10-04 Add tests for ActiveSupport... 84 # The purpose of allowing strings in rescue_from is to support the
85 # declaration of handler associations for exception classes whose
86 # definition is yet unknown.
87 #
88 # Since this loop needs the constants it would be inconsistent to
89 # assume they should exist at this point. An early raised exception
90 # could trigger some other handler and the array could include
91 # precisely a string whose corresponding constant has not yet been
92 # seen. This is why we are tolerant to unknown constants.
93 #
94 # Note that this tolerance only matters if the exception was given as
95 # a string, otherwise a NameError will be raised by the interpreter
96 # itself when rescue_from CONSTANT is executed.
964dfc15 » norbert 2008-10-04 First draft of ActiveSuppor... 97 klass = self.class.const_get(klass_name) rescue nil
98 klass ||= klass_name.constantize rescue nil
99 exception.is_a?(klass) if klass
100 end
101
983dc807 » jeremy 2008-11-06 Don't shadow local with bla... 102 case rescuer
964dfc15 » norbert 2008-10-04 First draft of ActiveSuppor... 103 when Symbol
983dc807 » jeremy 2008-11-06 Don't shadow local with bla... 104 method(rescuer)
964dfc15 » norbert 2008-10-04 First draft of ActiveSuppor... 105 when Proc
983dc807 » jeremy 2008-11-06 Don't shadow local with bla... 106 rescuer.bind(self)
964dfc15 » norbert 2008-10-04 First draft of ActiveSuppor... 107 end
108 end
109 end
110 end