Ticket #8891: filters_cleanup_trunk.patch
| File filters_cleanup_trunk.patch, 10.5 kB (added by skaes, 2 years ago) |
|---|
-
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 -
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 # After filters will no be executed if the filter chain is halted. 220 221 # 221 222 # Around filters halt the request unless the action block is called. 222 223 # Given these filters … … 238 239 # . . / 239 240 # . #around (code after yield) 240 241 # . / 241 # #after (actual filter code is run )242 # #after (actual filter code is run, unless around filter returns false or does not yield) 242 243 # 243 # If #around returns before yielding, only #after willbe 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. 244 # If #around returns before yielding, #after will still not be run. The #before 245 # filter and controller action will not be run. If #before returns false, 246 # the second half of #around and will still run but #after and the 247 # action will not. If #around returns false, #after will not be run. 247 248 module ClassMethods 248 249 # The passed <tt>filters</tt> will be appended to the filter_chain and 249 250 # will execute before the action on this controller is performed. … … 439 440 def run(controller) 440 441 # only filters returning false are halted. 441 442 if false == @filter.call(controller) 442 controller. halt_filter_chain(@filter)443 controller.send :halt_filter_chain, @filter, :returned_false 443 444 end 444 445 end 445 446 … … 528 529 529 530 def find_filter_append_position(filters, filter_type) 530 531 # 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 chain532 # before and around filters go before the first after filter in the chain 532 533 unless filter_type == :after 533 534 filter_chain.each_with_index do |f,i| 534 535 return i if f.after? … … 655 656 return filter unless filter_responds_to_before_and_after(filter) 656 657 Proc.new do |controller, action| 657 658 if filter.before(controller) == false 658 controller.send :halt_filter_chain, filter 659 controller.send :halt_filter_chain, filter, :returned_false 659 660 else 660 661 begin 661 662 action.call … … 675 676 end 676 677 end 677 678 678 def filter_chain 679 self.class.filter_chain 679 protected 680 681 def process_with_filters(request, response, method = :perform_action, *arguments) #:nodoc: 682 @before_filter_chain_aborted = false 683 process_without_filters(request, response, method, *arguments) 680 684 end 681 685 686 def perform_action_with_filters 687 call_filters(self.class.filter_chain, 0, 0) 688 end 689 690 private 691 692 def call_filters(chain, index, nesting) 693 index = run_before_filters(chain, index, nesting) 694 aborted = @before_filter_chain_aborted 695 perform_action_without_filters unless performed? || aborted 696 return index if nesting != 0 || aborted 697 run_after_filters(chain, index) 698 end 699 682 700 def skip_excluded_filters(chain, index) 683 701 while (filter = chain[index]) && self.class.filter_excluded_from_action?(filter, action_name) 684 702 index = index.next … … 686 704 [filter, index] 687 705 end 688 706 689 def call_filters(chain, index, nesting) 690 # run before filters until we find an after filter or around filter 707 def run_before_filters(chain, index, nesting) 691 708 while chain[index] 692 709 filter, index = skip_excluded_filters(chain, index) 693 710 break unless filter # end of call chain reached 694 711 case filter.type 695 712 when :before 696 # invoke before filter 697 filter.run(self) 713 filter.run(self) # invoke before filter 698 714 index = index.next 699 715 break if @before_filter_chain_aborted 700 716 when :around 717 yielded = false 701 718 filter.call(self) do 719 yielded = true 702 720 # all remaining before and around filters will be run in this call 703 721 index = call_filters(chain, index.next, nesting.next) 704 722 end 723 halt_filter_chain(filter, :did_not_yield) unless yielded 705 724 break 706 725 else 707 # no before or around filters left 708 break 726 break # no before or around filters left 709 727 end 710 728 end 729 index 730 end 711 731 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 732 def run_after_filters(chain, index) 733 seen_after_filter = false 722 734 while chain[index] 723 735 filter, index = skip_excluded_filters(chain, index) 724 break unless filter 736 break unless filter # end of call chain reached 725 737 case filter.type 726 738 when :after 727 filter.run(self)728 index = index.next739 seen_after_filter = true 740 filter.run(self) # invoke after filter 729 741 else 730 raise ActionControllerError, "filter #{filter.inspect} was in the wrong place!" 742 # implementation error or someone has mucked with the filter chain 743 raise ActionControllerError, "filter #{filter.inspect} was in the wrong place!" if seen_after_filter 731 744 end 745 index = index.next 732 746 end 733 734 747 index.next 735 748 end 736 749 737 def halt_filter_chain(filter) 738 logger.info "Filter chain halted as [#{filter.inspect}] returned false." if logger 750 def halt_filter_chain(filter, reason) 739 751 @before_filter_chain_aborted = true 752 logger.info "Filter chain halted as [#{filter.inspect}] #{reason}." if logger 740 753 false 741 754 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 755 end 756 756 end 757 757 end