Ticket #9449: action_controller_rescue_from.3.diff
| File action_controller_rescue_from.3.diff, 7.3 kB (added by norbert, 1 year ago) |
|---|
-
actionpack/test/controller/rescue_test.rb
old new 3 3 uses_mocha 'rescue' do 4 4 5 5 class 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 6 15 def raises 7 16 render :text => 'already rendered' 8 17 raise "don't panic!" … … 15 24 def not_implemented 16 25 raise ActionController::NotImplemented.new(:get, :put) 17 26 end 27 28 def not_authorized 29 raise NotAuthorized 30 end 31 32 def record_invalid 33 raise RecordInvalid 34 end 18 35 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 20 47 end 21 48 22 23 49 class RescueTest < Test::Unit::TestCase 24 50 FIXTURE_PUBLIC = "#{File.dirname(__FILE__)}/../fixtures".freeze 25 51 … … 38 64 end 39 65 end 40 66 41 42 67 def test_rescue_action_locally_if_all_requests_local 43 68 @controller.expects(:local_request?).never 44 69 @controller.expects(:rescue_action_locally).with(@exception) … … 69 94 end 70 95 end 71 96 72 73 97 def test_rescue_action_in_public_with_error_file 74 98 with_rails_root FIXTURE_PUBLIC do 75 99 with_all_requests_local false do … … 93 117 assert_equal ' ', @response.body 94 118 end 95 119 96 97 120 def test_rescue_unknown_action_in_public_with_error_file 98 121 with_rails_root FIXTURE_PUBLIC do 99 122 with_all_requests_local false do … … 117 140 assert_equal ' ', @response.body 118 141 end 119 142 120 121 143 def test_rescue_missing_template_in_public 122 144 with_rails_root FIXTURE_PUBLIC do 123 145 with_all_requests_local true do … … 129 151 assert @response.body.include?('missing_template'), "Response should include the template name." 130 152 end 131 153 132 133 154 def test_rescue_action_locally 134 155 get :raises 135 156 assert_response :internal_server_error … … 138 159 assert @response.body.include?("don't panic"), "Response should include exception message." 139 160 end 140 161 141 142 162 def test_local_request_when_remote_addr_is_localhost 143 163 @controller.expects(:request).returns(@request).at_least_once 144 164 with_remote_addr '127.0.0.1' do … … 153 173 end 154 174 end 155 175 156 157 176 def test_rescue_responses 158 177 responses = ActionController::Base.rescue_responses 159 178 … … 182 201 assert_equal 'template_error', templates[ActionView::TemplateError.name] 183 202 end 184 203 185 186 204 def test_clean_backtrace 187 205 with_rails_root nil do 188 206 # No action if RAILS_ROOT isn't set. … … 217 235 assert_equal "GET, HEAD, PUT", @response.headers['Allow'] 218 236 end 219 237 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 220 248 protected 221 249 def with_all_requests_local(local = true) 222 250 old_local, ActionController::Base.consider_all_requests_local = -
actionpack/lib/action_controller/rescue.rb
old new 40 40 base.rescue_templates = Hash.new(DEFAULT_RESCUE_TEMPLATE) 41 41 base.rescue_templates.update DEFAULT_RESCUE_TEMPLATES 42 42 43 base.class_inheritable_hash :rescue_handlers 44 base.rescue_handlers = {} 45 43 46 base.extend(ClassMethods) 44 47 base.class_eval do 45 48 alias_method_chain :perform_action, :rescue … … 50 53 def process_with_exception(request, response, exception) 51 54 new.process(request, response, :rescue_action, exception) 52 55 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 53 83 end 54 84 55 85 protected … … 58 88 log_error(exception) if logger 59 89 erase_results if performed? 60 90 91 return if rescue_action_with_handler(exception) 92 61 93 # Let the exception alter the response if it wants. 62 94 # For example, MethodNotAllowed sets the Allow header. 63 95 if exception.respond_to?(:handle_response!) … … 86 118 end 87 119 end 88 120 89 90 121 # Overwrite to implement public exception handling (for requests answering false to <tt>local_request?</tt>). By 91 122 # default will call render_optional_error_file. Override this method to provide more user friendly error messages.s 92 123 def rescue_action_in_public(exception) #:doc: … … 96 127 # Attempts to render a static error page based on the <tt>status_code</tt> thrown, 97 128 # or just return headers if no such file exists. For example, if a 500 error is 98 129 # 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. 100 131 def render_optional_error_file(status_code) 101 132 status = interpret_status(status_code) 102 133 path = "#{RAILS_ROOT}/public/#{status[0,3]}.html" … … 128 159 render_for_file(rescues_path("layout"), response_code_for_rescue(exception)) 129 160 end 130 161 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 131 174 private 132 175 def perform_action_with_rescue #:nodoc: 133 176 perform_action_without_rescue … … 147 190 rescue_responses[exception.class.name] 148 191 end 149 192 193 def handler_for_rescue(exception) 194 if handler = rescue_handlers[exception.class.name] 195 method(handler) 196 end 197 end 198 150 199 def clean_backtrace(exception) 151 200 if backtrace = exception.backtrace 152 201 if defined?(RAILS_ROOT)