Changeset 6649
- Timestamp:
- 05/02/07 13:22:38 (1 year ago)
- Files:
-
- trunk/actionpack/CHANGELOG (modified) (1 diff)
- trunk/actionpack/lib/action_controller/filters.rb (modified) (14 diffs)
- trunk/actionpack/test/controller/filters_test.rb (modified) (19 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/actionpack/CHANGELOG
r6648 r6649 1 1 *SVN* 2 3 * Replace the current block/continuation filter chain handling by an implementation based on a simple loop. #8226 [Stefan Kaes] 2 4 3 5 * Update UrlWriter to accept :anchor parameter. Closes #6771. [octopod] trunk/actionpack/lib/action_controller/filters.rb
r6396 r6649 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 … … 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 … … 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 … … 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 … … 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 true 414 type == :around 415 end 416 417 def run(controller) 418 raise ActionControllerError, 'No filter type: Nothing to do here.' 410 419 end 411 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 … … 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 true 432 end 433 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 end 435 def type 436 :before 437 end 438 439 def run(controller) 440 # only filters returning false are halted. 441 if false == @filter.call(controller) 442 controller.halt_filter_chain(@filter) 443 end 444 end 445 446 def call(controller) 447 yield unless run(controller) 440 448 end 441 449 end 442 450 443 451 class AfterFilterProxy < FilterProxy #:nodoc: 444 def after? 445 true 446 end 447 448 def call(controller, &block) 452 def type 453 :after 454 end 455 456 def run(controller) 457 @filter.call(controller) 458 end 459 460 def call(controller) 449 461 yield 450 @filter.call(controller)462 run(controller) 451 463 end 452 464 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) ) 491 end 492 493 def prepend_filter_to_chain(filters, position = :around, &block) 494 write_inheritable_attribute('filter_chain', create_filters(filters, position, &block) + filter_chain) 495 end 496 497 def create_filters(filters, position, &block) #:nodoc: 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) 516 end 517 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) 521 end 522 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) … … 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) … … 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 … … 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 … … 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 … … 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 … … 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) 630 end 631 halt_filter_chain(filter.filter, :no_yield) if halted == false unless @before_filter_chain_aborted 632 halted 633 end 634 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." 642 end 643 end 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 681 end 682 [filter, index] 683 end 684 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 705 end 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 … … 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 trunk/actionpack/test/controller/filters_test.rb
r5301 r6649 15 15 @ran_filter << "ensure_login" 16 16 end 17 17 18 18 def clean_up 19 19 @ran_after_filter ||= [] … … 63 63 end 64 64 end 65 65 66 66 class ConditionalFilterController < ActionController::Base 67 67 def show … … 87 87 @ran_filter << "clean_up_tmp" 88 88 end 89 89 90 90 def rescue_action(e) raise(e) end 91 91 end … … 95 95 end 96 96 97 class OnlyConditionSymController < ConditionalFilterController 97 class OnlyConditionSymController < ConditionalFilterController 98 98 before_filter :ensure_login, :only => :show 99 99 end … … 105 105 class BeforeAndAfterConditionController < ConditionalFilterController 106 106 before_filter :ensure_login, :only => :show 107 after_filter :clean_up_tmp, :only => :show 108 end 109 110 class OnlyConditionProcController < ConditionalFilterController 107 after_filter :clean_up_tmp, :only => :show 108 end 109 110 class OnlyConditionProcController < ConditionalFilterController 111 111 before_filter(:only => :show) {|c| c.assigns["ran_proc_filter"] = true } 112 112 end … … 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 … … 156 156 render :inline => "ran action" 157 157 end 158 158 159 159 protected 160 160 def find_user … … 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 ||= [] … … 175 175 end 176 176 end 177 177 178 178 class ChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController 179 179 skip_before_filter :conditional_in_parent, :only => :another_action … … 198 198 end 199 199 end 200 200 201 201 class AroundFilter 202 202 def before(controller) … … 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 … … 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" … … 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 … … 248 256 append_around_filter AppendedAroundFilter.new 249 257 end 250 258 251 259 class MixedSpecializationController < ActionController::Base 252 260 class OutOfOrder < StandardError; end … … 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"] … … 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"] … … 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"] … … 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) … … 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)