Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #9449: action_controller_rescue_from.3.diff

File action_controller_rescue_from.3.diff, 7.3 kB (added by norbert, 1 year ago)

Using different machinery

  • actionpack/test/controller/rescue_test.rb

    old new  
    33uses_mocha 'rescue' do 
    44 
    55class RescueController < ActionController::Base 
     6  class NotAuthorized < StandardError 
     7  end 
     8 
     9  class RecordInvalid < StandardError 
     10  end 
     11 
     12  rescue_from NotAuthorized, :with => :deny_access 
     13  rescue_from RecordInvalid, :with => :show_errors 
     14 
    615  def raises 
    716    render :text => 'already rendered' 
    817    raise "don't panic!" 
     
    1524  def not_implemented 
    1625    raise ActionController::NotImplemented.new(:get, :put) 
    1726  end 
     27 
     28  def not_authorized 
     29    raise NotAuthorized 
     30  end 
     31 
     32  def record_invalid 
     33    raise RecordInvalid 
     34  end 
    1835   
    19   def missing_template; end 
     36  def missing_template 
     37  end 
     38 
     39  protected 
     40    def deny_access 
     41      head :forbidden 
     42    end 
     43 
     44    def show_errors(exception) 
     45      head :unprocessable_entity 
     46    end 
    2047end 
    2148 
    22  
    2349class RescueTest < Test::Unit::TestCase 
    2450  FIXTURE_PUBLIC = "#{File.dirname(__FILE__)}/../fixtures".freeze 
    2551 
     
    3864    end 
    3965  end 
    4066 
    41  
    4267  def test_rescue_action_locally_if_all_requests_local 
    4368    @controller.expects(:local_request?).never 
    4469    @controller.expects(:rescue_action_locally).with(@exception) 
     
    6994    end 
    7095  end 
    7196 
    72  
    7397  def test_rescue_action_in_public_with_error_file 
    7498    with_rails_root FIXTURE_PUBLIC do 
    7599      with_all_requests_local false do 
     
    93117    assert_equal ' ', @response.body 
    94118  end 
    95119 
    96  
    97120  def test_rescue_unknown_action_in_public_with_error_file 
    98121    with_rails_root FIXTURE_PUBLIC do 
    99122      with_all_requests_local false do 
     
    117140    assert_equal ' ', @response.body 
    118141  end 
    119142 
    120  
    121143  def test_rescue_missing_template_in_public 
    122144    with_rails_root FIXTURE_PUBLIC do 
    123145      with_all_requests_local true do 
     
    129151    assert @response.body.include?('missing_template'), "Response should include the template name." 
    130152  end 
    131153 
    132  
    133154  def test_rescue_action_locally 
    134155    get :raises 
    135156    assert_response :internal_server_error 
     
    138159    assert @response.body.include?("don't panic"), "Response should include exception message." 
    139160  end 
    140161 
    141  
    142162  def test_local_request_when_remote_addr_is_localhost 
    143163    @controller.expects(:request).returns(@request).at_least_once 
    144164    with_remote_addr '127.0.0.1' do 
     
    153173    end 
    154174  end 
    155175 
    156  
    157176  def test_rescue_responses 
    158177    responses = ActionController::Base.rescue_responses 
    159178 
     
    182201    assert_equal 'template_error',    templates[ActionView::TemplateError.name] 
    183202  end 
    184203 
    185  
    186204  def test_clean_backtrace 
    187205    with_rails_root nil do 
    188206      # No action if RAILS_ROOT isn't set. 
     
    217235    assert_equal "GET, HEAD, PUT", @response.headers['Allow'] 
    218236  end 
    219237 
     238  def test_rescue_handler 
     239    get :not_authorized 
     240    assert_response :forbidden 
     241  end 
     242 
     243  def test_rescue_handler_with_argument 
     244    @controller.expects(:show_errors).once.with { |e| e.is_a?(Exception) } 
     245    get :record_invalid 
     246  end 
     247 
    220248  protected 
    221249    def with_all_requests_local(local = true) 
    222250      old_local, ActionController::Base.consider_all_requests_local = 
  • actionpack/lib/action_controller/rescue.rb

    old new  
    4040      base.rescue_templates = Hash.new(DEFAULT_RESCUE_TEMPLATE) 
    4141      base.rescue_templates.update DEFAULT_RESCUE_TEMPLATES 
    4242 
     43      base.class_inheritable_hash :rescue_handlers 
     44      base.rescue_handlers = {} 
     45 
    4346      base.extend(ClassMethods) 
    4447      base.class_eval do 
    4548        alias_method_chain :perform_action, :rescue 
     
    5053      def process_with_exception(request, response, exception) 
    5154        new.process(request, response, :rescue_action, exception) 
    5255      end 
     56 
     57      # 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. 
     58      # Handler methods that take one argument will be called with the exception, so that the exception can be inspected when dealing with it. 
     59      # 
     60      # class ApplicationController < ActionController::Base 
     61      #   rescue_from User::NotAuthorized, :with => :deny_access # self defined exception 
     62      #   rescue_from ActiveRecord::RecordInvalid, :with => :show_errors 
     63      # 
     64      #   protected 
     65      #     def deny_access 
     66      #       ... 
     67      #     end 
     68      # 
     69      #     def show_errors(exception) 
     70      #       exception.record.new_record? ? ... 
     71      #     end 
     72      # end 
     73      def rescue_from(*klasses) 
     74        options = klasses.extract_options! 
     75        unless options.has_key?(:with) # allow nil 
     76          raise ArgumentError, "Need a handler. Supply an options hash that has a :with key as the last argument." 
     77        end 
     78 
     79        klasses.each do |klass| 
     80          rescue_handlers[klass.name] = options[:with] 
     81        end 
     82      end 
    5383    end 
    5484 
    5585    protected 
     
    5888        log_error(exception) if logger 
    5989        erase_results if performed? 
    6090 
     91        return if rescue_action_with_handler(exception) 
     92 
    6193        # Let the exception alter the response if it wants. 
    6294        # For example, MethodNotAllowed sets the Allow header. 
    6395        if exception.respond_to?(:handle_response!) 
     
    86118        end 
    87119      end 
    88120 
    89  
    90121      # Overwrite to implement public exception handling (for requests answering false to <tt>local_request?</tt>).  By 
    91122      # default will call render_optional_error_file.  Override this method to provide more user friendly error messages.s 
    92123      def rescue_action_in_public(exception) #:doc: 
     
    96127      # Attempts to render a static error page based on the <tt>status_code</tt> thrown, 
    97128      # or just return headers if no such file exists. For example, if a 500 error is  
    98129      # being handled Rails will first attempt to render the file at <tt>public/500.html</tt>.  
    99       # If the file doesn't exist, the body of the response will be left empty 
     130      # If the file doesn't exist, the body of the response will be left empty. 
    100131      def render_optional_error_file(status_code) 
    101132        status = interpret_status(status_code) 
    102133        path = "#{RAILS_ROOT}/public/#{status[0,3]}.html" 
     
    128159        render_for_file(rescues_path("layout"), response_code_for_rescue(exception)) 
    129160      end 
    130161 
     162      # Tries to rescue the exception by looking up and calling a registered handler. 
     163      def rescue_action_with_handler(exception) 
     164        if handler = handler_for_rescue(exception) 
     165          if handler.arity != 0 
     166            handler.call(exception) 
     167          else 
     168            handler.call 
     169          end 
     170          true # don't rely on the return value of the handler 
     171        end 
     172      end 
     173 
    131174    private 
    132175      def perform_action_with_rescue #:nodoc: 
    133176        perform_action_without_rescue 
     
    147190        rescue_responses[exception.class.name] 
    148191      end 
    149192 
     193      def handler_for_rescue(exception) 
     194        if handler = rescue_handlers[exception.class.name] 
     195          method(handler) 
     196        end 
     197      end 
     198 
    150199      def clean_backtrace(exception) 
    151200        if backtrace = exception.backtrace 
    152201          if defined?(RAILS_ROOT)