Rails源代码分析(9):ActionController::Filter(3)

来源:互联网 发布:筑龙投标软件 编辑:程序博客网 时间:2024/05/01 17:28
主要看看如何添加到ActionController的

1 ClassMethods

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

原创粉丝点击