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