Ticket #8891: fix_for_around_filters_returning_false.2.patch
| File fix_for_around_filters_returning_false.2.patch, 10.5 kB (added by skaes, 2 years ago) |
|---|
-
actionpack/lib/action_controller/filters.rb
old new 214 214 # == Filter Chain Halting 215 215 # 216 216 # <tt>before_filter</tt> and <tt>around_filter</tt> may halt the request 217 # before controller action is run. This is useful, for example, to deny217 # before a controller action is run. This is useful, for example, to deny 218 218 # access to unauthenticated users or to redirect from http to https. 219 219 # Simply return false from the filter or call render or redirect. 220 # If fase is returned from a before or around filter, the after filters 221 # will not be executed. 220 222 # 221 223 # Around filters halt the request unless the action block is called. 222 224 # Given these filters … … 238 240 # . . / 239 241 # . #around (code after yield) 240 242 # . / 241 # #after (actual filter code is run )243 # #after (actual filter code is run, unless around filter returns false) 242 244 # 243 # If #around returns before yielding, only #after will be run. The #before244 # filter and controller action will not be run. If #before returns false,245 # the second half of #around and all of #after will still run butthe246 # action will not. 245 # If #around returns before yielding, #after will still be run. The #before 246 # filter and controller action will not be run. If #before returns false, 247 # the second half of #around and will still run but #after and the 248 # action will not. If #around returns false, #after will not be run. 247 249 module ClassMethods 248 250 # The passed <tt>filters</tt> will be appended to the filter_chain and 249 251 # will execute before the action on this controller is performed. … … 439 441 def run(controller) 440 442 # only filters returning false are halted. 441 443 if false == @filter.call(controller) 442 controller. halt_filter_chain(@filter)444 controller.send :halt_filter_chain, @filter, :returned_false 443 445 end 444 446 end 445 447 … … 528 530 529 531 def find_filter_append_position(filters, filter_type) 530 532 # appending an after filter puts it at the end of the call chain 531 # before and around filters go ebefore the first after filter in the chain533 # before and around filters go before the first after filter in the chain 532 534 unless filter_type == :after 533 535 filter_chain.each_with_index do |f,i| 534 536 return i if f.after? … … 655 657 return filter unless filter_responds_to_before_and_after(filter) 656 658 Proc.new do |controller, action| 657 659 if filter.before(controller) == false 658 controller.send :halt_filter_chain, filter 660 controller.send :halt_filter_chain, filter, :returned_false 659 661 else 660 662 begin 661 663 action.call … … 675 677 end 676 678 end 677 679 678 def filter_chain 679 self.class.filter_chain 680 protected 681 682 def process_with_filters(request, response, method = :perform_action, *arguments) #:nodoc: 683 @before_filter_chain_aborted = false 684 process_without_filters(request, response, method, *arguments) 680 685 end 681 686 687 private 688 689 def perform_action_with_filters 690 call_filters(self.class.filter_chain, 0, 0) 691 end 692 693 def call_filters(chain, index, nesting) 694 index = run_before_filters(chain, index, nesting) 695 aborted = @before_filter_chain_aborted 696 perform_action_without_filters unless performed? || aborted 697 return index if nesting != 0 || aborted 698 run_after_filters(chain, index) 699 end 700 682 701 def skip_excluded_filters(chain, index) 683 702 while (filter = chain[index]) && self.class.filter_excluded_from_action?(filter, action_name) 684 703 index = index.next … … 686 705 [filter, index] 687 706 end 688 707 689 def call_filters(chain, index, nesting) 690 # run before filters until we find an after filter or around filter 708 def run_before_filters(chain, index, nesting) 691 709 while chain[index] 692 710 filter, index = skip_excluded_filters(chain, index) 693 711 break unless filter # end of call chain reached 694 712 case filter.type 695 713 when :before 696 # invoke before filter 697 filter.run(self) 714 filter.run(self) # invoke before filter 698 715 index = index.next 699 716 break if @before_filter_chain_aborted 700 717 when :around 718 yielded = false 701 719 filter.call(self) do 720 yielded = true 702 721 # all remaining before and around filters will be run in this call 703 722 index = call_filters(chain, index.next, nesting.next) 704 723 end 724 halt_filter_chain(filter, :did_not_yield) unless yielded 705 725 break 706 726 else 707 # no before or around filters left 708 break 727 break # no before or around filters left 709 728 end 710 729 end 730 index 731 end 711 732 712 aborted = @before_filter_chain_aborted 713 perform_action_without_filters unless performed? || aborted 714 return index if aborted || nesting != 0 715 716 # if an around filter catches an exception during rendering and handles it, e.g. 717 # by rendering an error page, we might have left over around filters in the filter 718 # chain. so skip over these. 719 index = index.next while (filter = chain[index]) && filter.type == :around 720 721 # run after filters, if any 733 def run_after_filters(chain, index) 734 seen_after_filter = false 722 735 while chain[index] 723 736 filter, index = skip_excluded_filters(chain, index) 724 break unless filter 737 break unless filter # end of call chain reached 725 738 case filter.type 726 739 when :after 727 filter.run(self)728 index = index.next740 seen_after_filter = true 741 filter.run(self) # invoke after filter 729 742 else 730 raise ActionControllerError, "filter #{filter.inspect} was in the wrong place!" 743 # implementation error or someone has mucked with the filter chain 744 raise ActionControllerError, "filter #{filter.inspect} was in the wrong place!" if seen_after_filter 731 745 end 746 index = index.next 732 747 end 733 734 748 index.next 735 749 end 736 750 737 def halt_filter_chain(filter) 738 logger.info "Filter chain halted as [#{filter.inspect}] returned false." if logger 751 def halt_filter_chain(filter, reason) 739 752 @before_filter_chain_aborted = true 753 logger.info "Filter chain halted as [#{filter.inspect}] #{reason}." if logger 740 754 false 741 755 end 742 743 protected744 745 def process_with_filters(request, response, method = :perform_action, *arguments) #:nodoc:746 @before_filter_chain_aborted = false747 process_without_filters(request, response, method, *arguments)748 end749 750 private751 def perform_action_with_filters752 call_filters(self.class.filter_chain, 0, 0)753 end754 755 756 end 756 757 end 757 758 end -
actionpack/test/controller/filters_test.rb
old new 324 324 render :text => 'hello' 325 325 end 326 326 end 327 327 328 328 class ErrorToRescue < Exception; end 329 329 330 330 class RescuingAroundFilterWithBlock 331 331 def filter(controller) 332 332 begin … … 336 336 end 337 337 end 338 338 end 339 339 340 340 class RescuedController < ActionController::Base 341 341 around_filter RescuingAroundFilterWithBlock.new 342 342 343 343 def show 344 344 raise ErrorToRescue.new("Something made the bad noise.") 345 345 end 346 346 347 347 private 348 348 def rescue_action(exception) 349 349 raise exception 350 350 end 351 351 end 352 352 353 class NonYieldingAroundFilterController < ActionController::Base 354 355 before_filter :filter_one 356 around_filter :non_yielding_filter 357 before_filter :filter_two 358 after_filter :filter_three 359 360 def index 361 render :inline => "index" 362 end 363 364 #make sure the controller complains 365 def rescue_action(e); raise e; end 366 367 private 368 369 def filter_one 370 @filters ||= [] 371 @filters << "filter_one" 372 end 373 374 def filter_two 375 @filters << "filter_two" 376 end 377 378 def non_yielding_filter 379 @filters << "zomg it didn't yield" 380 @filter_return_value 381 end 382 383 def filter_three 384 @filters << "filter_three" 385 end 386 387 end 388 389 def test_non_yielding_around_filters_not_returning_false_do_not_raise 390 controller = NonYieldingAroundFilterController.new 391 controller.instance_variable_set "@filter_return_value", true 392 assert_nothing_raised do 393 test_process(controller, "index") 394 end 395 end 396 397 def test_non_yielding_around_filters_returning_false_do_not_raise 398 controller = NonYieldingAroundFilterController.new 399 controller.instance_variable_set "@filter_return_value", false 400 assert_nothing_raised do 401 test_process(controller, "index") 402 end 403 end 404 405 def test_after_filters_are_not_run_if_around_filter_returns_false 406 controller = NonYieldingAroundFilterController.new 407 controller.instance_variable_set "@filter_return_value", false 408 test_process(controller, "index") 409 assert_equal ["filter_one", "zomg it didn't yield"], controller.assigns['filters'] 410 end 411 412 def test_after_filters_are_not_run_if_around_filter_does_not_yield 413 controller = NonYieldingAroundFilterController.new 414 controller.instance_variable_set "@filter_return_value", true 415 test_process(controller, "index") 416 assert_equal ["filter_one", "zomg it didn't yield"], controller.assigns['filters'] 417 end 418 353 419 def test_empty_filter_chain 354 420 assert_equal 0, EmptyFilterChainController.filter_chain.size 355 421 assert test_process(EmptyFilterChainController).template.assigns['action_executed'] … … 516 582 def test_changing_the_requirements 517 583 assert_equal nil, test_process(ChangingTheRequirementsController, "go_wild").template.assigns['ran_filter'] 518 584 end 519 585 520 586 def test_a_rescuing_around_filter 521 587 response = nil 522 588 assert_nothing_raised do 523 589 response = test_process(RescuedController) 524 590 end 525 591 526 592 assert response.success? 527 593 assert_equal("I rescued this: #<FilterTest::ErrorToRescue: Something made the bad noise.>", response.body) 528 594 end