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

Ticket #8891: filters_cleanup_1-2-stable.patch

File filters_cleanup_1-2-stable.patch, 9.3 kB (added by skaes, 2 years ago)

filter cleanup for 1-2-stable

  • actionpack/test/controller/filters_test.rb

    old new  
    325325    end 
    326326  end 
    327327 
     328  class NonYieldingAroundFilterController < ActionController::Base 
     329 
     330    before_filter :filter_one 
     331    around_filter :non_yielding_filter 
     332    before_filter :filter_two 
     333    after_filter :filter_three 
     334 
     335    def index 
     336      render :inline => "index" 
     337    end 
     338 
     339    #make sure the controller complains 
     340    def rescue_action(e); raise e; end 
     341 
     342    private 
     343 
     344      def filter_one 
     345        @filters  ||= [] 
     346        @filters  << "filter_one" 
     347      end 
     348 
     349      def filter_two 
     350        @filters  << "filter_two" 
     351      end 
     352 
     353      def non_yielding_filter 
     354        @filters  << "zomg it didn't yield" 
     355        @filter_return_value 
     356      end 
     357 
     358      def filter_three 
     359        @filters  << "filter_three" 
     360      end 
     361 
     362  end 
     363 
     364  def test_non_yielding_around_filters_not_returning_false_do_not_raise 
     365    controller = NonYieldingAroundFilterController.new 
     366    controller.instance_variable_set "@filter_return_value", true 
     367    assert_nothing_raised do 
     368      test_process(controller, "index") 
     369    end 
     370  end 
     371 
     372  def test_non_yielding_around_filters_returning_false_do_not_raise 
     373    controller = NonYieldingAroundFilterController.new 
     374    controller.instance_variable_set "@filter_return_value", false 
     375    assert_nothing_raised do 
     376      test_process(controller, "index") 
     377    end 
     378  end 
     379 
     380  def test_after_filters_are_not_run_if_around_filter_returns_false 
     381    controller = NonYieldingAroundFilterController.new 
     382    controller.instance_variable_set "@filter_return_value", false 
     383    test_process(controller, "index") 
     384    assert_equal ["filter_one", "zomg it didn't yield"], controller.assigns['filters'] 
     385  end 
     386 
     387  def test_after_filters_are_not_run_if_around_filter_does_not_yield 
     388    controller = NonYieldingAroundFilterController.new 
     389    controller.instance_variable_set "@filter_return_value", true 
     390    test_process(controller, "index") 
     391    assert_equal ["filter_one", "zomg it didn't yield"], controller.assigns['filters'] 
     392  end 
     393 
    328394  def test_empty_filter_chain 
    329395    assert_equal 0, EmptyFilterChainController.filter_chain.size 
    330396    assert test_process(EmptyFilterChainController).template.assigns['action_executed'] 
  • actionpack/lib/action_controller/filters.rb

    old new  
    214214    # == Filter Chain Halting 
    215215    # 
    216216    # <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 deny 
     217    # before a controller action is run. This is useful, for example, to deny 
    218218    # access to unauthenticated users or to redirect from http to https. 
    219219    # Simply return false from the filter or call render or redirect. 
     220    # After filters will no be executed if the filter chain is halted. 
    220221    # 
    221222    # Around filters halt the request unless the action block is called. 
    222223    # Given these filters 
     
    238239    #   .  .  / 
    239240    #   .  #around (code after yield) 
    240241    #   . / 
    241     #   #after (actual filter code is run
     242    #   #after (actual filter code is run, unless around filter returns false or does not yield
    242243    # 
    243     # If #around returns before yielding, only #after will be run. The #before 
    244     # 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 but the 
    246     # 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. 
    247248    module ClassMethods 
    248249      # The passed <tt>filters</tt> will be appended to the filter_chain and 
    249250      # will execute before the action on this controller is performed. 
     
    439440        def run(controller) 
    440441          # only filters returning false are halted. 
    441442          if false == @filter.call(controller) 
    442             controller.halt_filter_chain(@filter) 
     443            controller.send :halt_filter_chain, @filter, :returned_false 
    443444          end 
    444445        end 
    445446 
     
    528529 
    529530        def find_filter_append_position(filters, filter_type) 
    530531          # appending an after filter puts it at the end of the call chain 
    531           # before and around filters goe before the first after filter in the chain 
     532          # before and around filters go before the first after filter in the chain 
    532533          unless filter_type == :after 
    533534            filter_chain.each_with_index do |f,i| 
    534535              return i if f.after? 
     
    655656          return filter unless filter_responds_to_before_and_after(filter) 
    656657          Proc.new do |controller, action| 
    657658            if filter.before(controller) == false 
    658               controller.send :halt_filter_chain, filter 
     659              controller.send :halt_filter_chain, filter, :returned_false 
    659660            else 
    660661              begin 
    661662                action.call 
     
    676677        end 
    677678      end 
    678679 
    679       def perform_action_with_filters 
    680         call_filters(self.class.filter_chain, 0, 0) 
    681       end 
     680      protected 
    682681 
    683682      def process_with_filters(request, response, method = :perform_action, *arguments) #:nodoc: 
    684683        @before_filter_chain_aborted = false 
    685684        process_without_filters(request, response, method, *arguments) 
    686685      end 
    687686 
    688       def filter_chain 
    689         self.class.filter_chain 
     687      def perform_action_with_filters 
     688        call_filters(self.class.filter_chain, 0, 0) 
    690689      end 
    691690 
     691      private 
     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 
    692701      def skip_excluded_filters(chain, index) 
    693702        while (filter = chain[index]) && self.class.filter_excluded_from_action?(filter, action_name) 
    694703          index = index.next 
     
    696705        [filter, index] 
    697706      end 
    698707 
    699       def call_filters(chain, index, nesting) 
    700         # run before filters until we find an after filter or around filter 
     708      def run_before_filters(chain, index, nesting) 
    701709        while chain[index] 
    702710          filter, index = skip_excluded_filters(chain, index) 
    703711          break unless filter # end of call chain reached 
    704712          case filter.type 
    705713          when :before 
    706             # invoke before filter 
    707             filter.run(self) 
     714            filter.run(self)  # invoke before filter 
    708715            index = index.next 
    709716            break if @before_filter_chain_aborted 
    710717          when :around 
     718            yielded = false 
    711719            filter.call(self) do 
     720              yielded = true 
    712721              # all remaining before and around filters will be run in this call 
    713722              index = call_filters(chain, index.next, nesting.next) 
    714723            end 
     724            halt_filter_chain(filter, :did_not_yield) unless yielded 
    715725            break 
    716726          else 
    717             # no before or around filters left 
    718             break 
     727            break  # no before or around filters left 
    719728          end 
    720729        end 
     730        index 
     731      end 
    721732 
    722         aborted = @before_filter_chain_aborted 
    723         perform_action_without_filters unless performed? || aborted 
    724         return index if aborted || nesting != 0 
    725  
    726         # run after filters, if any 
     733      def run_after_filters(chain, index) 
     734        seen_after_filter = false 
    727735        while chain[index] 
    728736          filter, index = skip_excluded_filters(chain, index) 
    729           break unless filter 
     737          break unless filter # end of call chain reached 
    730738          case filter.type 
    731739          when :after 
    732             filter.run(self) 
    733             index = index.next 
     740            seen_after_filter = true 
     741            filter.run(self)  # invoke after filter 
    734742          else 
    735             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 
    736745          end 
     746          index = index.next 
    737747        end 
    738  
    739748        index.next 
    740749      end 
    741750 
    742       def halt_filter_chain(filter) 
    743         logger.info "Filter chain halted as [#{filter.inspect}] returned false." if logger 
     751      def halt_filter_chain(filter, reason) 
    744752        @before_filter_chain_aborted = true 
     753        logger.info "Filter chain halted as [#{filter.inspect}] #{reason}." if logger 
    745754        false 
    746755      end 
    747756 
    748       private 
    749         def process_cleanup_with_filters 
    750           if @before_filter_chain_aborted 
    751             close_session 
    752           else 
    753             process_cleanup_without_filters 
    754           end 
     757      def process_cleanup_with_filters 
     758        if @before_filter_chain_aborted 
     759          close_session 
     760        else 
     761          process_cleanup_without_filters 
    755762        end 
     763      end 
    756764    end 
    757765  end 
    758766end