Ticket #8226: filter_speedup_trunk.patch
| File filter_speedup_trunk.patch, 18.6 kB (added by skaes, 2 years ago) |
|---|
-
actionpack/lib/action_controller/filters.rb
old new 263 263 # The passed <tt>filters</tt> will be appended to the array of filters 264 264 # that run _after_ actions on this controller are performed. 265 265 def append_after_filter(*filters, &block) 266 prepend_filter_to_chain(filters, :after, &block)266 append_filter_to_chain(filters, :after, &block) 267 267 end 268 268 269 269 # The passed <tt>filters</tt> will be prepended to the array of filters 270 270 # that run _after_ actions on this controller are performed. 271 271 def prepend_after_filter(*filters, &block) 272 append_filter_to_chain(filters, :after, &block)272 prepend_filter_to_chain(filters, :after, &block) 273 273 end 274 274 275 275 # Shorthand for append_after_filter since it's the most common. … … 362 362 363 363 # Returns a mapping between filters and the actions that may run them. 364 364 def included_actions #:nodoc: 365 read_inheritable_attribute("included_actions") || {}365 @included_actions ||= read_inheritable_attribute("included_actions") || {} 366 366 end 367 367 368 368 # Returns a mapping between filters and actions that may not run them. 369 369 def excluded_actions #:nodoc: 370 read_inheritable_attribute("excluded_actions") || {}370 @excluded_actions ||= read_inheritable_attribute("excluded_actions") || {} 371 371 end 372 372 373 373 # Find a filter in the filter_chain where the filter method matches the _filter_ param … … 381 381 382 382 # Returns true if the filter is excluded from the given action 383 383 def filter_excluded_from_action?(filter,action) #:nodoc: 384 if (ia = included_actions[filter]) && !ia.empty? 384 case 385 when ia = included_actions[filter] 385 386 !ia.include?(action) 386 else387 (excluded_actions[filter] || []).include?(action)387 when ea = excluded_actions[filter] 388 ea.include?(action) 388 389 end 389 390 end 390 391 … … 397 398 @filter = filter 398 399 end 399 400 401 def type 402 :around 403 end 404 400 405 def before? 401 false406 type == :before 402 407 end 403 408 404 409 def after? 405 false410 type == :after 406 411 end 407 412 408 413 def around? 409 t rue414 type == :around 410 415 end 411 416 417 def run(controller) 418 raise ActionControllerError, 'No filter type: Nothing to do here.' 419 end 420 412 421 def call(controller, &block) 413 r aise(ActionControllerError, 'No filter type: Nothing to do here.')422 run(controller) 414 423 end 415 424 end 416 425 … … 420 429 def filter 421 430 @filter.filter 422 431 end 423 424 def around?425 false426 end427 432 end 428 433 429 434 class BeforeFilterProxy < FilterProxy #:nodoc: 430 def before?431 true435 def type 436 :before 432 437 end 433 438 434 def call(controller, &block) 435 if false == @filter.call(controller) # must only stop if equal to false. only filters returning false are halted. 436 controller.halt_filter_chain(@filter, :returned_false) 437 else 438 yield 439 def run(controller) 440 # only filters returning false are halted. 441 if false == @filter.call(controller) 442 controller.halt_filter_chain(@filter) 439 443 end 440 444 end 445 446 def call(controller) 447 yield unless run(controller) 448 end 441 449 end 442 450 443 451 class AfterFilterProxy < FilterProxy #:nodoc: 444 def after?445 true452 def type 453 :after 446 454 end 447 455 448 def call(controller, &block) 449 yield 456 def run(controller) 450 457 @filter.call(controller) 451 458 end 459 460 def call(controller) 461 yield 462 run(controller) 463 end 452 464 end 453 465 454 466 class SymbolFilter < Filter #:nodoc: … … 485 497 end 486 498 end 487 499 500 class ClassBeforeFilter < Filter #:nodoc: 501 def call(controller, &block) 502 @filter.before(controller) 503 end 504 end 505 506 class ClassAfterFilter < Filter #:nodoc: 507 def call(controller, &block) 508 @filter.after(controller) 509 end 510 end 511 488 512 protected 489 def append_filter_to_chain(filters, position = :around, &block) 490 write_inheritable_array('filter_chain', create_filters(filters, position, &block) ) 513 def append_filter_to_chain(filters, filter_type = :around, &block) 514 pos = find_filter_append_position(filters, filter_type) 515 update_filter_chain(filters, filter_type, pos, &block) 491 516 end 492 517 493 def prepend_filter_to_chain(filters, position = :around, &block) 494 write_inheritable_attribute('filter_chain', create_filters(filters, position, &block) + filter_chain) 518 def prepend_filter_to_chain(filters, filter_type = :around, &block) 519 pos = find_filter_prepend_position(filters, filter_type) 520 update_filter_chain(filters, filter_type, pos, &block) 495 521 end 496 522 497 def create_filters(filters, position, &block) #:nodoc: 523 def update_filter_chain(filters, filter_type, pos, &block) 524 new_filters = create_filters(filters, filter_type, &block) 525 new_chain = filter_chain.insert(pos, new_filters).flatten 526 write_inheritable_attribute('filter_chain', new_chain) 527 end 528 529 def find_filter_append_position(filters, filter_type) 530 unless filter_type == :after 531 filter_chain.each_with_index do |f,i| 532 return i if f.after? 533 end 534 end 535 return -1 536 end 537 538 def find_filter_prepend_position(filters, filter_type) 539 if filter_type == :after 540 filter_chain.each_with_index do |f,i| 541 return i if f.after? 542 end 543 end 544 return 0 545 end 546 547 def create_filters(filters, filter_type, &block) #:nodoc: 498 548 filters, conditions = extract_conditions(filters, &block) 499 filters.map! { |filter| find_or_create_filter(filter, position) }549 filters.map! { |filter| find_or_create_filter(filter, filter_type) } 500 550 update_conditions(filters, conditions) 501 551 filters 502 552 end 503 553 504 def find_or_create_filter(filter, position)505 if found_filter = find_filter(filter) { |f| f. send("#{position}?")}554 def find_or_create_filter(filter, filter_type) 555 if found_filter = find_filter(filter) { |f| f.type == filter_type } 506 556 found_filter 507 557 else 508 f = class_for_filter(filter ).new(filter)558 f = class_for_filter(filter, filter_type).new(filter) 509 559 # apply proxy to filter if necessary 510 case position560 case filter_type 511 561 when :before 512 562 BeforeFilterProxy.new(f) 513 563 when :after … … 520 570 521 571 # The determination of the filter type was once done at run time. 522 572 # This method is here to extract as much logic from the filter run time as possible 523 def class_for_filter(filter ) #:nodoc:573 def class_for_filter(filter, filter_type) #:nodoc: 524 574 case 525 575 when filter.is_a?(Symbol) 526 576 SymbolFilter … … 534 584 end 535 585 when filter.respond_to?(:filter) 536 586 ClassFilter 587 when filter.respond_to?(:before) && filter_type == :before 588 ClassBeforeFilter 589 when filter.respond_to?(:after) && filter_type == :after 590 ClassAfterFilter 537 591 else 538 raise(ActionControllerError, 'A filter s must be a Symbol, Proc, Method, or object responding to filter.')592 raise(ActionControllerError, 'A filter must be a Symbol, Proc, Method, or object responding to filter, after or before.') 539 593 end 540 594 end 541 595 … … 550 604 return if conditions.empty? 551 605 if conditions[:only] 552 606 write_inheritable_hash('included_actions', condition_hash(filters, conditions[:only])) 553 els e554 write_inheritable_hash('excluded_actions', condition_hash(filters, conditions[:except])) if conditions[:except]607 elsif conditions[:except] 608 write_inheritable_hash('excluded_actions', condition_hash(filters, conditions[:except])) 555 609 end 556 610 end 557 611 … … 576 630 577 631 def remove_actions_from_included_actions!(filters,*actions) 578 632 actions = actions.flatten.map(&:to_s) 579 updated_hash = filters.inject( included_actions) do |hash,filter|633 updated_hash = filters.inject(read_inheritable_attribute('included_actions')||{}) do |hash,filter| 580 634 ia = (hash[filter] || []) - actions 581 ia. blank? ? hash.delete(filter) : hash[filter] = ia635 ia.empty? ? hash.delete(filter) : hash[filter] = ia 582 636 hash 583 637 end 584 638 write_inheritable_attribute('included_actions', updated_hash) … … 595 649 def proxy_before_and_after_filter(filter) #:nodoc: 596 650 return filter unless filter_responds_to_before_and_after(filter) 597 651 Proc.new do |controller, action| 598 unless filter.before(controller) == false 652 if filter.before(controller) == false 653 controller.send :halt_filter_chain, filter 654 else 599 655 begin 600 656 action.call 601 657 ensure … … 619 675 self.class.filter_chain 620 676 end 621 677 622 def call_filter(chain, index) 623 return (performed? || perform_action_without_filters) if index >= chain.size 624 filter = chain[index] 625 return call_filter(chain, index.next) if self.class.filter_excluded_from_action?(filter,action_name) 626 627 halted = false 628 filter.call(self) do 629 halted = call_filter(chain, index.next) 678 def skip_excluded_filters(chain, index) 679 while (filter = chain[index]) && self.class.filter_excluded_from_action?(filter, action_name) 680 index = index.next 630 681 end 631 halt_filter_chain(filter.filter, :no_yield) if halted == false unless @before_filter_chain_aborted 632 halted 682 [filter, index] 633 683 end 634 684 635 def halt_filter_chain(filter, reason) 636 if logger 637 case reason 638 when :no_yield 639 logger.info "Filter chain halted as [#{filter.inspect}] did not yield." 640 when :returned_false 641 logger.info "Filter chain halted as [#{filter.inspect}] returned false." 685 def call_filters(chain, index, nesting) 686 # run before filters until we find an after filter or around filter 687 while true 688 filter, index = skip_excluded_filters(chain, index) 689 break unless filter 690 case filter.type 691 when :before 692 # invoke before filter 693 filter.run(self) 694 index = index.next 695 break if @before_filter_chain_aborted 696 when :around 697 filter.call(self) do 698 # all remaining before and around filters will be run in this call 699 index = call_filters(chain, index.next, nesting.next) 700 end 701 break 702 else 703 # no before or around filters left 704 break 642 705 end 643 706 end 707 708 aborted = @before_filter_chain_aborted 709 perform_action_without_filters unless performed? || aborted 710 return index if aborted || nesting != 0 711 712 # run after filters, if any 713 while filter = chain[index] 714 filter, index = skip_excluded_filters(chain, index) 715 case filter.type 716 when :after 717 filter.run(self) 718 index = index.next 719 else 720 raise ActionControllerError, "filter #{filter.inspect} was in the wrong place!" 721 end 722 end 723 724 index.next 725 end 726 727 def halt_filter_chain(filter) 728 logger.info "Filter chain halted as [#{filter.inspect}] returned false." if logger 644 729 @before_filter_chain_aborted = true 645 returnfalse730 false 646 731 end 647 732 648 733 protected … … 654 739 655 740 private 656 741 def perform_action_with_filters 657 call_filter (self.class.filter_chain, 0)742 call_filters(self.class.filter_chain, 0, 0) 658 743 end 659 744 660 745 def process_cleanup_with_filters -
actionpack/test/controller/filters_test.rb
old new 14 14 @ran_filter ||= [] 15 15 @ran_filter << "ensure_login" 16 16 end 17 17 18 18 def clean_up 19 19 @ran_after_filter ||= [] 20 20 @ran_after_filter << "clean_up" … … 62 62 render :inline => "something else" 63 63 end 64 64 end 65 65 66 66 class ConditionalFilterController < ActionController::Base 67 67 def show 68 68 render :inline => "ran action" … … 86 86 @ran_filter ||= [] 87 87 @ran_filter << "clean_up_tmp" 88 88 end 89 89 90 90 def rescue_action(e) raise(e) end 91 91 end 92 92 … … 94 94 before_filter :ensure_login, :except => [ :show_without_filter, :another_action ] 95 95 end 96 96 97 class OnlyConditionSymController < ConditionalFilterController 97 class OnlyConditionSymController < ConditionalFilterController 98 98 before_filter :ensure_login, :only => :show 99 99 end 100 100 … … 104 104 105 105 class BeforeAndAfterConditionController < ConditionalFilterController 106 106 before_filter :ensure_login, :only => :show 107 after_filter :clean_up_tmp, :only => :show 107 after_filter :clean_up_tmp, :only => :show 108 108 end 109 110 class OnlyConditionProcController < ConditionalFilterController 109 110 class OnlyConditionProcController < ConditionalFilterController 111 111 before_filter(:only => :show) {|c| c.assigns["ran_proc_filter"] = true } 112 112 end 113 113 … … 145 145 class ConditionalSkippingController < TestController 146 146 skip_before_filter :ensure_login, :only => [ :login ] 147 147 skip_after_filter :clean_up, :only => [ :login ] 148 148 149 149 before_filter :find_user, :only => [ :change_password ] 150 150 151 151 def login … … 155 155 def change_password 156 156 render :inline => "ran action" 157 157 end 158 158 159 159 protected 160 160 def find_user 161 161 @ran_filter ||= [] … … 166 166 class ConditionalParentOfConditionalSkippingController < ConditionalFilterController 167 167 before_filter :conditional_in_parent, :only => [:show, :another_action] 168 168 after_filter :conditional_in_parent, :only => [:show, :another_action] 169 169 170 170 private 171 171 172 172 def conditional_in_parent 173 173 @ran_filter ||= [] 174 174 @ran_filter << 'conditional_in_parent' 175 175 end 176 176 end 177 177 178 178 class ChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController 179 179 skip_before_filter :conditional_in_parent, :only => :another_action 180 180 skip_after_filter :conditional_in_parent, :only => :another_action … … 197 197 controller.assigns["was_audited"] = true 198 198 end 199 199 end 200 200 201 201 class AroundFilter 202 202 def before(controller) 203 203 @execution_log = "before" … … 209 209 controller.assigns["execution_log"] = @execution_log + " and after" 210 210 controller.assigns["after_ran"] = true 211 211 controller.class.execution_log << " after aroundfilter " if controller.respond_to? :execution_log 212 end 212 end 213 213 end 214 214 215 215 class AppendedAroundFilter … … 219 219 220 220 def after(controller) 221 221 controller.class.execution_log << " after appended aroundfilter " 222 end 223 end 224 222 end 223 end 224 225 225 class AuditController < ActionController::Base 226 226 before_filter(AuditFilter) 227 227 228 228 def show 229 229 render_text "hello" 230 230 end … … 234 234 around_filter AroundFilter.new 235 235 end 236 236 237 class BeforeAfterClassFilterController < PrependingController 238 begin 239 filter = AroundFilter.new 240 before_filter filter 241 after_filter filter 242 end 243 end 244 237 245 class MixedFilterController < PrependingController 238 246 cattr_accessor :execution_log 239 247 … … 247 255 after_filter { |c| c.class.execution_log << " after procfilter " } 248 256 append_around_filter AppendedAroundFilter.new 249 257 end 250 258 251 259 class MixedSpecializationController < ActionController::Base 252 260 class OutOfOrder < StandardError; end 253 261 … … 292 300 def test_base_class_in_isolation 293 301 assert_equal [ ], ActionController::Base.before_filters 294 302 end 295 303 296 304 def test_prepending_filter 297 305 assert_equal [ :wonderful_life, :ensure_login ], PrependingController.before_filters 298 306 end 299 307 300 308 def test_running_filters 301 309 assert_equal %w( wonderful_life ensure_login ), test_process(PrependingController).template.assigns["ran_filter"] 302 310 end … … 304 312 def test_running_filters_with_proc 305 313 assert test_process(ProcController).template.assigns["ran_proc_filter"] 306 314 end 307 315 308 316 def test_running_filters_with_implicit_proc 309 317 assert test_process(ImplicitProcController).template.assigns["ran_proc_filter"] 310 318 end 311 319 312 320 def test_running_filters_with_class 313 321 assert test_process(AuditController).template.assigns["was_audited"] 314 322 end … … 319 327 assert response.template.assigns["ran_class_filter"] 320 328 assert response.template.assigns["ran_proc_filter1"] 321 329 assert response.template.assigns["ran_proc_filter2"] 322 330 323 331 response = test_process(AnomolousYetValidConditionController, "show_without_filter") 324 332 assert_equal nil, response.template.assigns["ran_filter"] 325 333 assert !response.template.assigns["ran_class_filter"] … … 373 381 assert controller.template.assigns["after_ran"] 374 382 end 375 383 384 def test_before_after_class_filter 385 controller = test_process(BeforeAfterClassFilterController) 386 assert controller.template.assigns["before_ran"] 387 assert controller.template.assigns["after_ran"] 388 end 389 376 390 def test_having_properties_in_around_filter 377 391 controller = test_process(AroundFilterController) 378 392 assert_equal "before and after", controller.template.assigns["execution_log"] … … 381 395 def test_prepending_and_appending_around_filter 382 396 controller = test_process(MixedFilterController) 383 397 assert_equal " before aroundfilter before procfilter before appended aroundfilter " + 384 " after appended aroundfilter after aroundfilter after procfilter ", 398 " after appended aroundfilter after aroundfilter after procfilter ", 385 399 MixedFilterController.execution_log 386 400 end 387 401 388 402 def test_rendering_breaks_filtering_chain 389 403 response = test_process(RenderingController) 390 404 assert_equal "something else", response.body