Changeset 8081
- Timestamp:
- 11/06/07 06:02:24 (1 year ago)
- Files:
-
- trunk/actionpack/lib/action_controller/rescue.rb (modified) (4 diffs)
- trunk/actionpack/test/controller/rescue_test.rb (modified) (10 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/actionpack/lib/action_controller/rescue.rb
r7822 r8081 42 42 base.rescue_templates.update DEFAULT_RESCUE_TEMPLATES 43 43 44 base.class_inheritable_ hash:rescue_handlers45 base.rescue_handlers = {}44 base.class_inheritable_array :rescue_handlers 45 base.rescue_handlers = [] 46 46 47 47 base.extend(ClassMethods) … … 56 56 end 57 57 58 # 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. 59 # Handler methods that take one argument will be called with the exception, so that the exception can be inspected when dealing with it. 58 # Rescue exceptions raised in controller actions. 59 # 60 # <tt>rescue_from</tt> receives a series of exception classes or class 61 # names, and a trailing :with option with the name of a method or a Proc 62 # object to be called to handle them. Alternatively a block can be given. 63 # 64 # Handlers that take one argument will be called with the exception, so 65 # that the exception can be inspected when dealing with it. 66 # 67 # Handlers are inherited. They are searched from right to left, from 68 # bottom to top, and up the hierarchy. The handler of the first class for 69 # which exception.is_a?(klass) holds true is the one invoked, if any. 60 70 # 61 71 # class ApplicationController < ActionController::Base 62 72 # rescue_from User::NotAuthorized, :with => :deny_access # self defined exception 63 73 # rescue_from ActiveRecord::RecordInvalid, :with => :show_errors 74 # 75 # rescue_from 'MyAppError::Base' do |exception| 76 # render :xml => exception, :status => 500 77 # end 64 78 # 65 79 # protected … … 79 93 80 94 klasses.each do |klass| 81 rescue_handlers[klass.name] = options[:with] 95 key = if klass.is_a?(Class) && klass <= Exception 96 klass.name 97 elsif klass.is_a?(String) 98 klass 99 else 100 raise(ArgumentError, "#{klass} is neither an Exception nor a String") 101 end 102 103 # Order is important, we put the pair at the end. When dealing with an 104 # exception we will follow the documented order going from right to left. 105 rescue_handlers << [key, options[:with]] 82 106 end 83 107 end … … 193 217 194 218 def handler_for_rescue(exception) 195 case handler = rescue_handlers[exception.class.name] 219 # We go from right to left because pairs are pushed onto rescue_handlers 220 # as rescue_from declarations are found. 221 _, handler = *rescue_handlers.reverse.detect do |klass_name, handler| 222 # The purpose of allowing strings in rescue_from is to support the 223 # declaration of handler associations for exception classes whose 224 # definition is yet unknown. 225 # 226 # Since this loop needs the constants it would be inconsistent to 227 # assume they should exist at this point. An early raised exception 228 # could trigger some other handler and the array could include 229 # precisely a string whose corresponding constant has not yet been 230 # seen. This is why we are tolerant to unkown constants. 231 # 232 # Note that this tolerance only matters if the exception was given as 233 # a string, otherwise a NameError will be raised by the interpreter 234 # itself when rescue_from CONSTANT is executed. 235 klass = self.class.const_get(klass_name) rescue nil 236 klass ||= klass_name.constantize rescue nil 237 exception.is_a?(klass) if klass 238 end 239 240 case handler 196 241 when Symbol 197 242 method(handler) trunk/actionpack/test/controller/rescue_test.rb
r7822 r8081 6 6 class NotAuthorized < StandardError 7 7 end 8 class NotAuthorizedToRescueAsString < StandardError 9 end 8 10 9 11 class RecordInvalid < StandardError 10 12 end 13 class RecordInvalidToRescueAsString < StandardError 14 end 11 15 12 16 class NotAllowed < StandardError 13 17 end 18 class NotAllowedToRescueAsString < StandardError 19 end 14 20 15 21 class InvalidRequest < StandardError 16 22 end 23 class InvalidRequestToRescueAsString < StandardError 24 end 17 25 18 26 class BadGateway < StandardError 19 27 end 28 class BadGatewayToRescueAsString < StandardError 29 end 20 30 21 31 class ResourceUnavailable < StandardError 22 32 end 33 class ResourceUnavailableToRescueAsString < StandardError 34 end 35 36 # We use a fully-qualified name in some strings, and a relative constant 37 # name in some other to test correct handling of both cases. 23 38 24 39 rescue_from NotAuthorized, :with => :deny_access 40 rescue_from 'RescueController::NotAuthorizedToRescueAsString', :with => :deny_access 41 25 42 rescue_from RecordInvalid, :with => :show_errors 43 rescue_from 'RescueController::RecordInvalidToRescueAsString', :with => :show_errors 26 44 27 45 rescue_from NotAllowed, :with => proc { head :forbidden } 46 rescue_from 'RescueController::NotAllowedToRescueAsString', :with => proc { head :forbidden } 47 28 48 rescue_from InvalidRequest, :with => proc { |exception| render :text => exception.message } 49 rescue_from 'InvalidRequestToRescueAsString', :with => proc { |exception| render :text => exception.message } 29 50 30 51 rescue_from BadGateway do 31 52 head :status => 502 32 53 end 54 rescue_from 'BadGatewayToRescueAsString' do 55 head :status => 502 56 end 33 57 34 58 rescue_from ResourceUnavailable do |exception| 59 render :text => exception.message 60 end 61 rescue_from 'ResourceUnavailableToRescueAsString' do |exception| 35 62 render :text => exception.message 36 63 end … … 52 79 raise NotAuthorized 53 80 end 81 def not_authorized_raise_as_string 82 raise NotAuthorizedToRescueAsString 83 end 54 84 55 85 def not_allowed 56 86 raise NotAllowed 57 87 end 88 def not_allowed_raise_as_string 89 raise NotAllowedToRescueAsString 90 end 58 91 59 92 def invalid_request 60 93 raise InvalidRequest 61 94 end 95 def invalid_request_raise_as_string 96 raise InvalidRequestToRescueAsString 97 end 62 98 63 99 def record_invalid 64 100 raise RecordInvalid 101 end 102 def record_invalid_raise_as_string 103 raise RecordInvalidToRescueAsString 65 104 end 66 105 … … 68 107 raise BadGateway 69 108 end 109 def bad_gateway_raise_as_string 110 raise BadGatewayToRescueAsString 111 end 70 112 71 113 def resource_unavailable 72 114 raise ResourceUnavailable 115 end 116 def resource_unavailable_raise_as_string 117 raise ResourceUnavailableToRescueAsString 73 118 end 74 119 … … 279 324 assert_response :forbidden 280 325 end 326 def test_rescue_handler_string 327 get :not_authorized_raise_as_string 328 assert_response :forbidden 329 end 281 330 282 331 def test_rescue_handler_with_argument … … 284 333 get :record_invalid 285 334 end 335 def test_rescue_handler_with_argument_as_string 336 @controller.expects(:show_errors).once.with { |e| e.is_a?(Exception) } 337 get :record_invalid_raise_as_string 338 end 286 339 287 340 def test_proc_rescue_handler … … 289 342 assert_response :forbidden 290 343 end 344 def test_proc_rescue_handler_as_string 345 get :not_allowed_raise_as_string 346 assert_response :forbidden 347 end 291 348 292 349 def test_proc_rescue_handle_with_argument … … 294 351 assert_equal "RescueController::InvalidRequest", @response.body 295 352 end 353 def test_proc_rescue_handle_with_argument_as_string 354 get :invalid_request_raise_as_string 355 assert_equal "RescueController::InvalidRequestToRescueAsString", @response.body 356 end 296 357 297 358 def test_block_rescue_handler … … 299 360 assert_response 502 300 361 end 362 def test_block_rescue_handler_as_string 363 get :bad_gateway_raise_as_string 364 assert_response 502 365 end 301 366 302 367 def test_block_rescue_handler_with_argument … … 304 369 assert_equal "RescueController::ResourceUnavailable", @response.body 305 370 end 371 372 def test_block_rescue_handler_with_argument_as_string 373 get :resource_unavailable_raise_as_string 374 assert_equal "RescueController::ResourceUnavailableToRescueAsString", @response.body 375 end 376 306 377 307 378 protected … … 340 411 end 341 412 413 class ExceptionInheritanceRescueController < ActionController::Base 414 415 class ParentException < StandardError 416 end 417 418 class ChildException < ParentException 419 end 420 421 class GrandchildException < ChildException 422 end 423 424 rescue_from ChildException, :with => lambda { head :ok } 425 rescue_from ParentException, :with => lambda { head :created } 426 rescue_from GrandchildException, :with => lambda { head :no_content } 427 428 def raise_parent_exception 429 raise ParentException 430 end 431 432 def raise_child_exception 433 raise ChildException 434 end 435 436 def raise_grandchild_exception 437 raise GrandchildException 438 end 439 end 440 441 class ExceptionInheritanceRescueTest < Test::Unit::TestCase 442 443 def setup 444 @controller = ExceptionInheritanceRescueController.new 445 @request = ActionController::TestRequest.new 446 @response = ActionController::TestResponse.new 447 end 448 449 def test_bottom_first 450 get :raise_grandchild_exception 451 assert_response :no_content 452 end 453 454 def test_inheritance_works 455 get :raise_child_exception 456 assert_response :created 457 end 458 end 459 460 class ControllerInheritanceRescueController < ExceptionInheritanceRescueController 461 class FirstExceptionInChildController < StandardError 462 end 463 464 class SecondExceptionInChildController < StandardError 465 end 466 467 rescue_from FirstExceptionInChildController, 'SecondExceptionInChildController', :with => lambda { head :gone } 468 469 def raise_first_exception_in_child_controller 470 raise FirstExceptionInChildController 471 end 472 473 def raise_second_exception_in_child_controller 474 raise SecondExceptionInChildController 475 end 476 end 477 478 class ControllerInheritanceRescueControllerTest < Test::Unit::TestCase 479 480 def setup 481 @controller = ControllerInheritanceRescueController.new 482 @request = ActionController::TestRequest.new 483 @response = ActionController::TestResponse.new 484 end 485 486 def test_first_exception_in_child_controller 487 get :raise_first_exception_in_child_controller 488 assert_response :gone 489 end 490 491 def test_second_exception_in_child_controller 492 get :raise_second_exception_in_child_controller 493 assert_response :gone 494 end 495 496 def test_exception_in_parent_controller 497 get :raise_parent_exception 498 assert_response :created 499 end 500 end 342 501 end # uses_mocha