主要看看如何添加到ActionController的
1 ClassMethods
- module ClassMethods
- ########################################
- # The passed <tt>filters</tt> will be appended to the filter_chain and
- # will execute before the action on this controller is performed.
- def append_before_filter(*filters, &block)
- filter_chain.append_filter_to_chain(filters, :before, &block)
- end
- # The passed <tt>filters</tt> will be prepended to the filter_chain and
- # will execute before the action on this controller is performed.
- def prepend_before_filter(*filters, &block)
- filter_chain.prepend_filter_to_chain(filters, :before, &block)
- end
- # Shorthand for append_before_filter since it's the most common.
- alias :before_filter :append_before_filter
- # The passed <tt>filters</tt> will be appended to the array of filters
- # that run _after_ actions on this controller are performed.
- def append_after_filter(*filters, &block)
- filter_chain.append_filter_to_chain(filters, :after, &block)
- end
- # The passed <tt>filters</tt> will be prepended to the array of filters
- # that run _after_ actions on this controller are performed.
- def prepend_after_filter(*filters, &block)
- filter_chain.prepend_filter_to_chain(filters, :after, &block)
- end
- # Shorthand for append_after_filter since it's the most common.
- alias :after_filter :append_after_filter
- # If you <tt>append_around_filter A.new, B.new</tt>, the filter chain looks like
- #
- # B#before
- # A#before
- # # run the action
- # A#after
- # B#after
- #
- # With around filters which yield to the action block, +before+ and +after+
- # are the code before and after the yield.
- def append_around_filter(*filters, &block)
- filter_chain.append_filter_to_chain(filters, :around, &block)
- end
- # If you <tt>prepend_around_filter A.new, B.new</tt>, the filter chain looks like:
- #
- # A#before
- # B#before
- # # run the action
- # B#after
- # A#after
- #
- # With around filters which yield to the action block, +before+ and +after+
- # are the code before and after the yield.
- def prepend_around_filter(*filters, &block)
- filter_chain.prepend_filter_to_chain(filters, :around, &block)
- end
- # Shorthand for +append_around_filter+ since it's the most common.
- alias :around_filter :append_around_filter
- # Removes the specified filters from the +before+ filter chain. Note that this only works for skipping method-reference
- # filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out
- # of many sub-controllers need a different hierarchy.
- #
- # You can control the actions to skip the filter for with the <tt>:only</tt> and <tt>:except</tt> options,
- # just like when you apply the filters.
- def skip_before_filter(*filters)
- filter_chain.skip_filter_in_chain(*filters, :before?)
- end
- # Removes the specified filters from the +after+ filter chain. Note that this only works for skipping method-reference
- # filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out
- # of many sub-controllers need a different hierarchy.
- #
- # You can control the actions to skip the filter for with the <tt>:only</tt> and <tt>:except</tt> options,
- # just like when you apply the filters.
- def skip_after_filter(*filters)
- filter_chain.skip_filter_in_chain(*filters, :after?)
- end
- # Removes the specified filters from the filter chain. This only works for method reference (symbol)
- # filters, not procs. This method is different from skip_after_filter and skip_before_filter in that
- # it will match any before, after or yielding around filter.
- #
- # You can control the actions to skip the filter for with the <tt>:only</tt> and <tt>:except</tt> options,
- # just like when you apply the filters.
- def skip_filter(*filters)
- filter_chain.skip_filter_in_chain(*filters)
- end
- # Returns an array of Filter objects for this controller.
- def filter_chain
- if chain = read_inheritable_attribute('filter_chain')
- return chain
- else
- write_inheritable_attribute('filter_chain', FilterChain.new)
- return filter_chain
- end
- end
- # Returns all the before filters for this class and all its ancestors.
- # This method returns the actual filter that was assigned in the controller to maintain existing functionality.
- def before_filters #:nodoc:
- filter_chain.select(:before?).map(:method)
- end
- # Returns all the after filters for this class and all its ancestors.
- # This method returns the actual filter that was assigned in the controller to maintain existing functionality.
- def after_filters #:nodoc:
- filter_chain.select(:after?).map(:method)
- end
- end
2 InstanceMethods
用了兩個alias_method_chain,前面看controller的時候知道了核心方法是process,然後process裡調用perform_action方法。
這裡等于是在process之前設定@before_filter_chain_aborted ,然後進入perform_action_with_filters
- module InstanceMethods # :nodoc:
- def self.included(base)
- base.class_eval do
- alias_method_chain :perform_action, :filters
- alias_method_chain :process, :filters
- end
- end
- protected
- def process_with_filters(request, response, method = :perform_action, *arguments) #:nodoc:
- @before_filter_chain_aborted = false
- process_without_filters(request, response, method, *arguments)
- end
- def perform_action_with_filters
- call_filters(self.class.filter_chain, 0, 0)
- end
- private
- def call_filters(chain, index, nesting)
- #1 調用before_filters方法 運作before和around filter
- index = run_before_filters(chain, index, nesting)
- aborted = @before_filter_chain_aborted
- #2 如果沒有問題執行過render 或者 redirect 或者設定了@before_filter_chain_aborted變量為true 執行
- perform_action_without_filters unless performed? || aborted
- #3 設定了@before_filter_chain_aborted變量為true 傳回
- return index if nesting != 0 || aborted
- #4 運作after_filter
- run_after_filters(chain, index)
- end
- def run_before_filters(chain, index, nesting)
- while chain[index]
- filter, index = chain[index], index
- break unless filter # end of call chain reached
- case filter
- # 先運作所有的before filter
- when BeforeFilter
- filter.call(self) # invoke before filter
- index = index.next
- break if @before_filter_chain_aborted
- when AroundFilter
- yielded = false
- filter.call(self) do
- yielded = true
- # all remaining before and around filters will be run in this call
- # 遞歸調用around filter
- index = call_filters(chain, index.next, nesting.next)
- end
- # 如果沒有執行yield程式,運作結束
- halt_filter_chain(filter, :did_not_yield) unless yielded
- break
- else
- break # no before or around filters left
- end
- end
- index
- end
- def run_after_filters(chain, index)
- seen_after_filter = false
- while chain[index]
- filter, index = chain[index], index
- break unless filter # end of call chain reached
- case filter
- when AfterFilter
- seen_after_filter = true
- filter.call(self) # invoke after filter
- else
- # implementation error or someone has mucked with the filter chain
- raise ActionControllerError, "filter #{filter.inspect} was in the wrong place!" if seen_after_filter
- end
- index = index.next
- end
- index.next
- end
- # 這個方法在AroundFilter和BeforeFilter call都可能被調用
- def halt_filter_chain(filter, reason)
- @before_filter_chain_aborted = true
- logger.info "Filter chain halted as [#{filter.inspect}] #{reason}." if logger
- end
- end
- end
最後。。。完成
- def self.included(base)
- base.class_eval do
- extend ClassMethods
- include ActionController::Filters::InstanceMethods
- end
- end
終于結束了Filter