Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #8226: filter_speedup_fixed.patch

File filter_speedup_fixed.patch, 18.7 kB (added by skaes, 2 years ago)
  • actionpack/lib/action_controller/filters.rb

    old new  
    263263      # The passed <tt>filters</tt> will be appended to the array of filters 
    264264      # that run _after_ actions on this controller are performed. 
    265265      def append_after_filter(*filters, &block) 
    266         prepend_filter_to_chain(filters, :after, &block) 
     266        append_filter_to_chain(filters, :after, &block) 
    267267      end 
    268268 
    269269      # The passed <tt>filters</tt> will be prepended to the array of filters 
    270270      # that run _after_ actions on this controller are performed. 
    271271      def prepend_after_filter(*filters, &block) 
    272         append_filter_to_chain(filters, :after, &block) 
     272        prepend_filter_to_chain(filters, :after, &block) 
    273273      end 
    274274 
    275275      # Shorthand for append_after_filter since it's the most common. 
     
    362362 
    363363      # Returns a mapping between filters and the actions that may run them. 
    364364      def included_actions #:nodoc: 
    365         read_inheritable_attribute("included_actions") || {} 
     365        @included_actions ||= read_inheritable_attribute("included_actions") || {} 
    366366      end 
    367367 
    368368      # Returns a mapping between filters and actions that may not run them. 
    369369      def excluded_actions #:nodoc: 
    370         read_inheritable_attribute("excluded_actions") || {} 
     370        @excluded_actions ||= read_inheritable_attribute("excluded_actions") || {} 
    371371      end 
    372372 
    373373      # Find a filter in the filter_chain where the filter method matches the _filter_ param 
     
    381381 
    382382      # Returns true if the filter is excluded from the given action 
    383383      def filter_excluded_from_action?(filter,action) #:nodoc: 
    384         if (ia = included_actions[filter]) && !ia.empty? 
     384        case 
     385        when ia = included_actions[filter] 
    385386          !ia.include?(action) 
    386         else 
    387           (excluded_actions[filter] || []).include?(action) 
     387        when ea = excluded_actions[filter] 
     388          ea.include?(action) 
    388389        end 
    389390      end 
    390391 
     
    397398          @filter = filter 
    398399        end 
    399400 
     401        def type 
     402          :around 
     403        end 
     404 
    400405        def before? 
    401           fals
     406          type == :befor
    402407        end 
    403408 
    404409        def after? 
    405           false 
     410          type == :after 
    406411        end 
    407412 
    408413        def around? 
    409           true 
     414          type == :around 
    410415        end 
    411416 
     417        def run(controller) 
     418          raise ActionControllerError, 'No filter type: Nothing to do here.' 
     419        end 
     420 
    412421        def call(controller, &block) 
    413           raise(ActionControllerError, 'No filter type: Nothing to do here.'
     422          run(controller
    414423        end 
    415424      end 
    416425 
     
    420429        def filter 
    421430          @filter.filter 
    422431        end 
    423  
    424         def around? 
    425           false 
    426         end 
    427432      end 
    428433 
    429434      class BeforeFilterProxy < FilterProxy #:nodoc: 
    430         def before? 
    431           tru
     435        def type 
     436          :befor
    432437        end 
    433438 
    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) 
    439443          end 
    440444        end 
     445 
     446        def call(controller) 
     447          yield unless run(controller) 
     448        end 
    441449      end 
    442450 
    443451      class AfterFilterProxy < FilterProxy #:nodoc: 
    444         def after? 
    445           true 
     452        def type 
     453          :after 
    446454        end 
    447455 
    448         def call(controller, &block) 
    449           yield 
     456        def run(controller) 
    450457          @filter.call(controller) 
    451458        end 
     459 
     460        def call(controller) 
     461          yield 
     462          run(controller) 
     463        end 
    452464      end 
    453465 
    454466      class SymbolFilter < Filter #:nodoc: 
     
    485497        end 
    486498      end 
    487499 
     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 
    488512      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) 
    491516        end 
    492517 
    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) 
    495521        end 
    496522 
    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: 
    498548          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) } 
    500550          update_conditions(filters, conditions) 
    501551          filters 
    502552        end 
    503553 
    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
    506556            found_filter 
    507557          else 
    508             f = class_for_filter(filter).new(filter) 
     558            f = class_for_filter(filter, filter_type).new(filter) 
    509559            # apply proxy to filter if necessary 
    510             case position 
     560            case filter_type 
    511561            when :before 
    512562              BeforeFilterProxy.new(f) 
    513563            when :after 
     
    520570 
    521571        # The determination of the filter type was once done at run time. 
    522572        # 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: 
    524574          case 
    525575          when filter.is_a?(Symbol) 
    526576            SymbolFilter 
     
    534584            end 
    535585          when filter.respond_to?(:filter) 
    536586            ClassFilter 
     587          when filter.respond_to?(:before) && filter_type == :before 
     588            ClassBeforeFilter 
     589          when filter.respond_to?(:after) && filter_type == :after 
     590            ClassAfterFilter 
    537591          else 
    538             raise(ActionControllerError, 'A filters 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.') 
    539593          end 
    540594        end 
    541595 
     
    550604          return if conditions.empty? 
    551605          if conditions[:only] 
    552606            write_inheritable_hash('included_actions', condition_hash(filters, conditions[:only])) 
    553           else 
    554             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])) 
    555609          end 
    556610        end 
    557611 
     
    576630 
    577631        def remove_actions_from_included_actions!(filters,*actions) 
    578632          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| 
    580634            ia = (hash[filter] || []) - actions 
    581             ia.blank? ? hash.delete(filter) : hash[filter] = ia 
     635            ia.empty? ? hash.delete(filter) : hash[filter] = ia 
    582636            hash 
    583637          end 
    584638          write_inheritable_attribute('included_actions', updated_hash) 
     
    595649        def proxy_before_and_after_filter(filter) #:nodoc: 
    596650          return filter unless filter_responds_to_before_and_after(filter) 
    597651          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 
    599655              begin 
    600656                action.call 
    601657              ensure 
     
    616672      end 
    617673 
    618674      def perform_action_with_filters 
    619         call_filter(self.class.filter_chain, 0) 
     675        call_filters(self.class.filter_chain, 0, 0) 
    620676      end 
    621677 
    622678      def process_with_filters(request, response, method = :perform_action, *arguments) #:nodoc: 
     
    628684        self.class.filter_chain 
    629685      end 
    630686 
    631       def call_filter(chain, index) 
    632         return (performed? || perform_action_without_filters) if index >= chain.size 
    633         filter = chain[index] 
    634         return call_filter(chain, index.next) if self.class.filter_excluded_from_action?(filter,action_name) 
    635  
    636         halted = false 
    637         filter.call(self) do 
    638           halted = call_filter(chain, index.next) 
     687      def skip_excluded_filters(chain, index) 
     688        while (filter = chain[index]) && self.class.filter_excluded_from_action?(filter, action_name) 
     689          index = index.next 
    639690        end 
    640         halt_filter_chain(filter.filter, :no_yield) if halted == false unless @before_filter_chain_aborted 
    641         halted 
     691        [filter, index] 
    642692      end 
    643693 
    644       def halt_filter_chain(filter, reason) 
    645         if logger 
    646           case reason 
    647           when :no_yield 
    648             logger.info "Filter chain halted as [#{filter.inspect}] did not yield." 
    649           when :returned_false 
    650             logger.info "Filter chain halted as [#{filter.inspect}] returned false." 
     694      def call_filters(chain, index, nesting) 
     695        # run before filters until we find an after filter or around filter 
     696        while true 
     697          filter, index = skip_excluded_filters(chain, index) 
     698          break unless filter 
     699          case filter.type 
     700          when :before 
     701            # invoke before filter 
     702            filter.run(self) 
     703            index = index.next 
     704            break if @before_filter_chain_aborted 
     705          when :around 
     706            result = filter.call(self) do 
     707              # all remaining before and around filters will be run in this call 
     708              index = call_filters(chain, index.next, nesting.next) 
     709            end 
     710            break 
     711          else 
     712            # no before or around filters left 
     713            break 
    651714          end 
    652715        end 
     716 
     717        aborted = @before_filter_chain_aborted 
     718        perform_action_without_filters unless performed? || aborted 
     719        return index if aborted || nesting != 0 
     720 
     721        # run after filters, if any 
     722        while filter = chain[index] 
     723          filter, index = skip_excluded_filters(chain, index) 
     724          case filter.type 
     725          when :after 
     726            filter.run(self) 
     727            index = index.next 
     728          else 
     729            raise ActionControllerError, "filter #{filter.inspect} was in the wrong place!" 
     730          end 
     731        end 
     732 
     733        index.next 
     734      end 
     735 
     736      def halt_filter_chain(filter) 
     737        logger.info "Filter chain halted as [#{filter.inspect}] returned false." if logger 
    653738        @before_filter_chain_aborted = true 
    654         return false 
     739        false 
    655740      end 
    656741 
    657742      private 
  • actionpack/test/controller/filters_test.rb

    old new  
    1414        @ran_filter ||= [] 
    1515        @ran_filter << "ensure_login" 
    1616      end 
    17        
     17 
    1818      def clean_up 
    1919        @ran_after_filter ||= [] 
    2020        @ran_after_filter << "clean_up" 
     
    6262        render :inline => "something else" 
    6363      end 
    6464  end 
    65    
     65 
    6666  class ConditionalFilterController < ActionController::Base 
    6767    def show 
    6868      render :inline => "ran action" 
     
    8686        @ran_filter ||= [] 
    8787        @ran_filter << "clean_up_tmp" 
    8888      end 
    89        
     89 
    9090      def rescue_action(e) raise(e) end 
    9191  end 
    9292 
     
    9494    before_filter :ensure_login, :except => [ :show_without_filter, :another_action ] 
    9595  end 
    9696 
    97   class OnlyConditionSymController < ConditionalFilterController  
     97  class OnlyConditionSymController < ConditionalFilterController 
    9898    before_filter :ensure_login, :only => :show 
    9999  end 
    100100 
     
    104104 
    105105  class BeforeAndAfterConditionController < ConditionalFilterController 
    106106    before_filter :ensure_login, :only => :show 
    107     after_filter  :clean_up_tmp, :only => :show  
     107    after_filter  :clean_up_tmp, :only => :show 
    108108  end 
    109    
    110   class OnlyConditionProcController < ConditionalFilterController  
     109 
     110  class OnlyConditionProcController < ConditionalFilterController 
    111111    before_filter(:only => :show) {|c| c.assigns["ran_proc_filter"] = true } 
    112112  end 
    113113 
     
    145145  class ConditionalSkippingController < TestController 
    146146    skip_before_filter :ensure_login, :only => [ :login ] 
    147147    skip_after_filter  :clean_up,     :only => [ :login ] 
    148      
     148 
    149149    before_filter :find_user, :only => [ :change_password ] 
    150150 
    151151    def login 
     
    155155    def change_password 
    156156      render :inline => "ran action" 
    157157    end 
    158      
     158 
    159159    protected 
    160160      def find_user 
    161161        @ran_filter ||= [] 
     
    166166  class ConditionalParentOfConditionalSkippingController < ConditionalFilterController 
    167167    before_filter :conditional_in_parent, :only => [:show, :another_action] 
    168168    after_filter  :conditional_in_parent, :only => [:show, :another_action] 
    169      
     169 
    170170    private 
    171        
     171 
    172172      def conditional_in_parent 
    173173        @ran_filter ||= [] 
    174174        @ran_filter << 'conditional_in_parent' 
    175175      end 
    176176  end 
    177    
     177 
    178178  class ChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController 
    179179    skip_before_filter :conditional_in_parent, :only => :another_action 
    180180    skip_after_filter  :conditional_in_parent, :only => :another_action 
     
    197197      controller.assigns["was_audited"] = true 
    198198    end 
    199199  end 
    200    
     200 
    201201  class AroundFilter 
    202202    def before(controller) 
    203203      @execution_log = "before" 
     
    209209      controller.assigns["execution_log"] = @execution_log + " and after" 
    210210      controller.assigns["after_ran"] = true 
    211211      controller.class.execution_log << " after aroundfilter " if controller.respond_to? :execution_log 
    212     end     
     212    end 
    213213  end 
    214214 
    215215  class AppendedAroundFilter 
     
    219219 
    220220    def after(controller) 
    221221      controller.class.execution_log << " after appended aroundfilter " 
    222     end     
    223   end   
    224    
     222    end 
     223  end 
     224 
    225225  class AuditController < ActionController::Base 
    226226    before_filter(AuditFilter) 
    227      
     227 
    228228    def show 
    229229      render_text "hello" 
    230230    end 
     
    234234    around_filter AroundFilter.new 
    235235  end 
    236236 
     237  class BeforeAfterClassFilterController < PrependingController 
     238    begin 
     239      filter = AroundFilter.new 
     240      before_filter filter 
     241      after_filter filter 
     242    end 
     243  end 
     244 
    237245  class MixedFilterController < PrependingController 
    238246    cattr_accessor :execution_log 
    239247 
     
    247255    after_filter  { |c| c.class.execution_log << " after procfilter " } 
    248256    append_around_filter AppendedAroundFilter.new 
    249257  end 
    250    
     258 
    251259  class MixedSpecializationController < ActionController::Base 
    252260    class OutOfOrder < StandardError; end 
    253261 
     
    292300  def test_base_class_in_isolation 
    293301    assert_equal [ ], ActionController::Base.before_filters 
    294302  end 
    295    
     303 
    296304  def test_prepending_filter 
    297305    assert_equal [ :wonderful_life, :ensure_login ], PrependingController.before_filters 
    298306  end 
    299    
     307 
    300308  def test_running_filters 
    301309    assert_equal %w( wonderful_life ensure_login ), test_process(PrependingController).template.assigns["ran_filter"] 
    302310  end 
     
    304312  def test_running_filters_with_proc 
    305313    assert test_process(ProcController).template.assigns["ran_proc_filter"] 
    306314  end 
    307    
     315 
    308316  def test_running_filters_with_implicit_proc 
    309317    assert test_process(ImplicitProcController).template.assigns["ran_proc_filter"] 
    310318  end 
    311    
     319 
    312320  def test_running_filters_with_class 
    313321    assert test_process(AuditController).template.assigns["was_audited"] 
    314322  end 
     
    319327    assert response.template.assigns["ran_class_filter"] 
    320328    assert response.template.assigns["ran_proc_filter1"] 
    321329    assert response.template.assigns["ran_proc_filter2"] 
    322      
     330 
    323331    response = test_process(AnomolousYetValidConditionController, "show_without_filter") 
    324332    assert_equal nil, response.template.assigns["ran_filter"] 
    325333    assert !response.template.assigns["ran_class_filter"] 
     
    373381    assert controller.template.assigns["after_ran"] 
    374382  end 
    375383 
     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 
    376390  def test_having_properties_in_around_filter 
    377391    controller = test_process(AroundFilterController) 
    378392    assert_equal "before and after", controller.template.assigns["execution_log"] 
     
    381395  def test_prepending_and_appending_around_filter 
    382396    controller = test_process(MixedFilterController) 
    383397    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 ", 
    385399                 MixedFilterController.execution_log 
    386400  end 
    387    
     401 
    388402  def test_rendering_breaks_filtering_chain 
    389403    response = test_process(RenderingController) 
    390404    assert_equal "something else", response.body