天天看點

Rails源代碼分析(9):ActionController::Filter(3)1 ClassMethods2 InstanceMethods 最後。。。完成

主要看看如何添加到ActionController的

1 ClassMethods

  1.     module ClassMethods
  2.       ########################################
  3.       # The passed <tt>filters</tt> will be appended to the filter_chain and
  4.       # will execute before the action on this controller is performed.
  5.       def append_before_filter(*filters, &block)
  6.         filter_chain.append_filter_to_chain(filters, :before, &block)
  7.       end
  8.       # The passed <tt>filters</tt> will be prepended to the filter_chain and
  9.       # will execute before the action on this controller is performed.
  10.       def prepend_before_filter(*filters, &block)
  11.         filter_chain.prepend_filter_to_chain(filters, :before, &block)
  12.       end
  13.       # Shorthand for append_before_filter since it's the most common.
  14.       alias :before_filter :append_before_filter
  15.       # The passed <tt>filters</tt> will be appended to the array of filters
  16.       # that run _after_ actions on this controller are performed.
  17.       def append_after_filter(*filters, &block)
  18.         filter_chain.append_filter_to_chain(filters, :after, &block)
  19.       end
  20.       # The passed <tt>filters</tt> will be prepended to the array of filters
  21.       # that run _after_ actions on this controller are performed.
  22.       def prepend_after_filter(*filters, &block)
  23.         filter_chain.prepend_filter_to_chain(filters, :after, &block)
  24.       end
  25.       # Shorthand for append_after_filter since it's the most common.
  26.       alias :after_filter :append_after_filter
  27.       # If you <tt>append_around_filter A.new, B.new</tt>, the filter chain looks like
  28.       #
  29.       #   B#before
  30.       #     A#before
  31.       #       # run the action
  32.       #     A#after
  33.       #   B#after
  34.       #
  35.       # With around filters which yield to the action block, +before+ and +after+
  36.       # are the code before and after the yield.
  37.       def append_around_filter(*filters, &block)
  38.         filter_chain.append_filter_to_chain(filters, :around, &block)
  39.       end
  40.       # If you <tt>prepend_around_filter A.new, B.new</tt>, the filter chain looks like:
  41.       #
  42.       #   A#before
  43.       #     B#before
  44.       #       # run the action
  45.       #     B#after
  46.       #   A#after
  47.       #
  48.       # With around filters which yield to the action block, +before+ and +after+
  49.       # are the code before and after the yield.
  50.       def prepend_around_filter(*filters, &block)
  51.         filter_chain.prepend_filter_to_chain(filters, :around, &block)
  52.       end
  53.       # Shorthand for +append_around_filter+ since it's the most common.
  54.       alias :around_filter :append_around_filter
  55.       # Removes the specified filters from the +before+ filter chain. Note that this only works for skipping method-reference
  56.       # filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out
  57.       # of many sub-controllers need a different hierarchy.
  58.       #
  59.       # You can control the actions to skip the filter for with the <tt>:only</tt> and <tt>:except</tt> options,
  60.       # just like when you apply the filters.
  61.       def skip_before_filter(*filters)
  62.         filter_chain.skip_filter_in_chain(*filters, :before?)
  63.       end
  64.       # Removes the specified filters from the +after+ filter chain. Note that this only works for skipping method-reference
  65.       # filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out
  66.       # of many sub-controllers need a different hierarchy.
  67.       #
  68.       # You can control the actions to skip the filter for with the <tt>:only</tt> and <tt>:except</tt> options,
  69.       # just like when you apply the filters.
  70.       def skip_after_filter(*filters)
  71.         filter_chain.skip_filter_in_chain(*filters, :after?)
  72.       end
  73.       # Removes the specified filters from the filter chain. This only works for method reference (symbol)
  74.       # filters, not procs. This method is different from skip_after_filter and skip_before_filter in that
  75.       # it will match any before, after or yielding around filter.
  76.       #
  77.       # You can control the actions to skip the filter for with the <tt>:only</tt> and <tt>:except</tt> options,
  78.       # just like when you apply the filters.
  79.       def skip_filter(*filters)
  80.         filter_chain.skip_filter_in_chain(*filters)
  81.       end
  82.       # Returns an array of Filter objects for this controller.
  83.       def filter_chain
  84.         if chain = read_inheritable_attribute('filter_chain')
  85.           return chain
  86.         else
  87.           write_inheritable_attribute('filter_chain', FilterChain.new)
  88.           return filter_chain
  89.         end
  90.       end
  91.       # Returns all the before filters for this class and all its ancestors.
  92.       # This method returns the actual filter that was assigned in the controller to maintain existing functionality.
  93.       def before_filters #:nodoc:
  94.         filter_chain.select(:before?).map(:method)
  95.       end
  96.       # Returns all the after filters for this class and all its ancestors.
  97.       # This method returns the actual filter that was assigned in the controller to maintain existing functionality.
  98.       def after_filters #:nodoc:
  99.         filter_chain.select(:after?).map(:method)
  100.       end
  101.     end

2 InstanceMethods

      用了兩個alias_method_chain,前面看controller的時候知道了核心方法是process,然後process裡調用perform_action方法。

      這裡等于是在process之前設定@before_filter_chain_aborted ,然後進入perform_action_with_filters

  1.     module InstanceMethods # :nodoc:
  2.       def self.included(base)
  3.         base.class_eval do
  4.           alias_method_chain :perform_action, :filters
  5.           alias_method_chain :process, :filters
  6.         end
  7.       end
  8.       protected
  9.         def process_with_filters(request, response, method = :perform_action, *arguments) #:nodoc:
  10.           @before_filter_chain_aborted = false
  11.           process_without_filters(request, response, method, *arguments)
  12.         end
  13.         def perform_action_with_filters
  14.           call_filters(self.class.filter_chain, 0, 0)
  15.         end
  16.       private
  17.         def call_filters(chain, index, nesting)
  18.           #1 調用before_filters方法 運作before和around filter
  19.           index = run_before_filters(chain, index, nesting)
  20.           aborted = @before_filter_chain_aborted
  21.           #2 如果沒有問題執行過render 或者 redirect 或者設定了@before_filter_chain_aborted變量為true 執行
  22.           perform_action_without_filters unless performed? || aborted
  23.           #3 設定了@before_filter_chain_aborted變量為true 傳回
  24.           return index if nesting != 0 || aborted
  25.           #4 運作after_filter
  26.           run_after_filters(chain, index)
  27.         end
  28.         def run_before_filters(chain, index, nesting)
  29.           while chain[index]
  30.             filter, index = chain[index], index
  31.             break unless filter # end of call chain reached
  32.             case filter
  33.             # 先運作所有的before filter
  34.             when BeforeFilter
  35.               filter.call(self)  # invoke before filter
  36.               index = index.next
  37.               break if @before_filter_chain_aborted
  38.             when AroundFilter
  39.               yielded = false
  40.               filter.call(self) do
  41.                 yielded = true
  42.                 # all remaining before and around filters will be run in this call
  43.                 # 遞歸調用around filter
  44.                 index = call_filters(chain, index.next, nesting.next)
  45.               end
  46.               # 如果沒有執行yield程式,運作結束 
  47.               halt_filter_chain(filter, :did_not_yield) unless yielded
  48.               break
  49.             else
  50.               break  # no before or around filters left
  51.             end
  52.           end
  53.           index
  54.         end
  55.         def run_after_filters(chain, index)
  56.           seen_after_filter = false
  57.           while chain[index]
  58.             filter, index = chain[index], index
  59.             break unless filter # end of call chain reached
  60.             case filter
  61.             when AfterFilter
  62.               seen_after_filter = true
  63.               filter.call(self)  # invoke after filter
  64.             else
  65.               # implementation error or someone has mucked with the filter chain
  66.               raise ActionControllerError, "filter #{filter.inspect} was in the wrong place!" if seen_after_filter
  67.             end
  68.             index = index.next
  69.           end
  70.           index.next
  71.         end
  72.              # 這個方法在AroundFilter和BeforeFilter call都可能被調用
  73.         def halt_filter_chain(filter, reason)
  74.           @before_filter_chain_aborted = true
  75.           logger.info "Filter chain halted as [#{filter.inspect}] #{reason}." if logger
  76.         end
  77.     end
  78.   end

最後。。。完成

  1.     def self.included(base)
  2.       base.class_eval do
  3.         extend ClassMethods
  4.         include ActionController::Filters::InstanceMethods
  5.       end
  6.     end

終于結束了Filter

繼續閱讀