~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/activesupport/lib/active_support/rescuable.rb

  • Committer: Richard Lee (Canonical)
  • Date: 2010-10-15 15:17:58 UTC
  • mfrom: (190.1.3 use-case-mapper)
  • Revision ID: richard.lee@canonical.com-20101015151758-wcvmfxrexsongf9d
Merge

Show diffs side-by-side

added added

removed removed

Lines of Context:
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