diff options
Diffstat (limited to 'actionpack/lib')
-rw-r--r-- | actionpack/lib/action_controller/rescue.rb | 53 |
1 files changed, 51 insertions, 2 deletions
diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb index c1d2152acb..a34356f172 100644 --- a/actionpack/lib/action_controller/rescue.rb +++ b/actionpack/lib/action_controller/rescue.rb @@ -41,6 +41,9 @@ 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.extend(ClassMethods) base.class_eval do alias_method_chain :perform_action, :rescue @@ -51,6 +54,33 @@ module ActionController #:nodoc: def process_with_exception(request, response, exception) 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. + # + # class ApplicationController < ActionController::Base + # rescue_from User::NotAuthorized, :with => :deny_access # self defined exception + # rescue_from ActiveRecord::RecordInvalid, :with => :show_errors + # + # protected + # def deny_access + # ... + # end + # + # def show_errors(exception) + # exception.record.new_record? ? ... + # end + # end + def rescue_from(*klasses) + options = klasses.extract_options! + unless options.has_key?(:with) # allow nil + raise ArgumentError, "Need a handler. Supply an options hash that has a :with key as the last argument." + end + + klasses.each do |klass| + rescue_handlers[klass.name] = options[:with] + end + end end protected @@ -59,6 +89,8 @@ module ActionController #:nodoc: log_error(exception) if logger erase_results if performed? + return if rescue_action_with_handler(exception) + # Let the exception alter the response if it wants. # For example, MethodNotAllowed sets the Allow header. if exception.respond_to?(:handle_response!) @@ -87,7 +119,6 @@ module ActionController #:nodoc: end end - # Overwrite to implement public exception handling (for requests answering false to <tt>local_request?</tt>). By # default will call render_optional_error_file. Override this method to provide more user friendly error messages.s def rescue_action_in_public(exception) #:doc: @@ -97,7 +128,7 @@ module ActionController #:nodoc: # Attempts to render a static error page based on the <tt>status_code</tt> thrown, # or just return headers if no such file exists. For example, if a 500 error is # being handled Rails will first attempt to render the file at <tt>public/500.html</tt>. - # If the file doesn't exist, the body of the response will be left empty + # If the file doesn't exist, the body of the response will be left empty. def render_optional_error_file(status_code) status = interpret_status(status_code) path = "#{RAILS_ROOT}/public/#{status[0,3]}.html" @@ -129,6 +160,18 @@ module ActionController #:nodoc: render_for_file(rescues_path("layout"), response_code_for_rescue(exception)) end + # Tries to rescue the exception by looking up and calling a registered handler. + def rescue_action_with_handler(exception) + if handler = handler_for_rescue(exception) + if handler.arity != 0 + handler.call(exception) + else + handler.call + end + true # don't rely on the return value of the handler + end + end + private def perform_action_with_rescue #:nodoc: perform_action_without_rescue @@ -148,6 +191,12 @@ module ActionController #:nodoc: rescue_responses[exception.class.name] end + def handler_for_rescue(exception) + if handler = rescue_handlers[exception.class.name] + method(handler) + end + end + def clean_backtrace(exception) if backtrace = exception.backtrace if defined?(RAILS_ROOT) |