原创作者: hideto
阅读:2030次
评论:0条
更新时间:2011-05-26
我们上次看过了ActiveRecord的callbacks,这次看看ActionController的filters
1,filter继承
先执行父类中的filter,再执行子类中的filter,如果父类中的filter返回false,则不执行子类中后续的filter
2,filter类型
1)method reference(symbol)
2)external class
3)inline method(proc)
3,filter链的执行顺序
可以使用prepend_before_filter、prepend_after_filter和prepend_around_filter来让某些filter最先执行
现在filter的执行顺序为:ensure_items_in_cart -> :ensure_items_in_stock -> :verify_open_shop
4,Around filters
5,filter链skipping
6,filter conditions
7,filter chain halting
对于如下filter定义:
执行顺序为:
如果#before返回false,则第二个#around和#after仍会执行
源代码文件为filters.rb:
代码一目了然,十有八九能猜到,使用alias_method_chain给perform_action/process/process_cleanup加上filter
perform_action_with_filters -> call_filter -> filter.call -> BeforeFilterProxy.call || AfterFilterProxy.call || proxy_before_and_after_filter
其他则为一些增删改filter_chain的public方法,如before_filter、after_filter、around_filter、skip_filter等
1,filter继承
先执行父类中的filter,再执行子类中的filter,如果父类中的filter返回false,则不执行子类中后续的filter
2,filter类型
1)method reference(symbol)
class BankController < ActionController::Base before_filter :audit end
2)external class
class OutputCompressionFilter def self.filter(controller) controller.response.body = compress(controller.response.body) end end class NewspaperController < ActionController::Base after_filter OutputCompressionFilter end
3)inline method(proc)
class WeblogController < ActionController::Base before_filter { |controller| false if controller.params["stop_action"] } end
3,filter链的执行顺序
可以使用prepend_before_filter、prepend_after_filter和prepend_around_filter来让某些filter最先执行
class ShoppingController < ActionController::Base before_filter :verify_open_shop end class CheckoutController < ShoppingController prepend_before_filter :ensure_items_in_cart, :ensure_items_in_stock end
现在filter的执行顺序为:ensure_items_in_cart -> :ensure_items_in_stock -> :verify_open_shop
4,Around filters
class SomeController < ActionController::Base around_filter :catch_exceptions private def catch_exceptions yield rescue => exception logger.debug "Caught exception! #{exception}" raise end end
5,filter链skipping
class ApplicationController < ActionController::Base before_filter :authenticate around_filter :catch_exceptions end class SignupController < ApplicationController # Skip :authenticate, run :catch_exceptions. skip_before_filter :authenticate end
6,filter conditions
class Journal < ActionController::Base before_filter :authorize, : only => [:edit, :delete] around_filter :catch_exceptions, :except => [:foo, :bar] end
7,filter chain halting
对于如下filter定义:
class SomeController < ActionController::Base before_filter :be around_filter :ar after_filter :af end
执行顺序为:
# ... # . \ # . #around (code before yield) # . . \ # . . #before (actual filter code is run) # . . . \ # . . . execute controller action # . . . / # . . ... # . . / # . #around (code after yield) # . / # #after (actual filter code is run)
如果#before返回false,则第二个#around和#after仍会执行
源代码文件为filters.rb:
module ActionController module Filters def self.included(base) base.extend(ClassMethods) base.send(:include, ActionController::Filters::InstanceMethods) end module ClassMethods def append_before_filter(*filters, &block) append_filter_to_chain(filters, :before, &block) end alias :before_filter :append_before_filter def append_after_filter(*filters, &block) prepend_filter_to_chain(filters, :after, &block) end alias :after_filter :append_after_filter def prepend_before_filter(*filters, &block) prepend_filter_to_chain(filters, :before, &block) end def prepend_after_filter(*filters, &block) append_filter_to_chain(filters, :after, &block) end def append_around_filter(*filters, &block) filters, conditions = extract_conditions(filters, &block) filters.map { |f| proxy_before_and_after_filter(f) }.each do |filter| append_filter_to_chain([filter, conditions]) end end alias :around_filter :append_around_filter def prepend_around_filter(*filters, &block) filters, conditions = extract_conditions(filters, &block) filters.map { |f| proxy_before_and_after_filter(f) }.each do |filter| prepend_filter_to_chain([filter, conditions]) end end def skip_before_filter(*filters) skip_filter_in_chain(*filters, &:before?) end def skip_after_filter(*filters) skip_filter_in_chain(*filters, &:after?) end def skip_filter(*filters) skip_filter_in_chain(*filters) end class Filter def call(controller, &block) raise(ActionControllerError, 'No filter type: Nothing to do here.') end end class FilterProxy < Filter def filter @filter.filter end def around? false end end class BeforeFilterProxy < FilterProxy def before? true end def call(controller, &block) if false == @filter.call(controller) controller.halt_filter_chain(@filter, :returned_false) else yield end end end class AfterFilterProxy < FilterProxy def after? true end def call(controller, &block) yield @filter.call(controller) end end class SymbolFilter < Filter def call(controller, &block) controller.send(@filter, &block) end end class ProcFilter < Filter def call(controller) @filter.call(controller) rescue LocalJumpError raise(ActionControllerError, 'Cannot yield from a Proc type filter. The Proc must take two arguments and execute #call on the second argument.') end end class ProcWithCallFilter < Filter def call(controller, &block) @filter.call(controller, block) rescue LocalJumpError raise(ActionControllerError, 'Cannot yield from a Proc type filter. The Proc must take two arguments and execute #call on the second argument.') end end class MethodFilter < Filter def call(controller, &block) @filter.call(controller, &block) end end class ClassFilter < Filter def call(controller, &block) @filter.filter(controller, &block) end end protected def append_filter_to_chain(filters, position = :around, &block) write_inheritable_array('filter_chain', create_filters(filters, position, &block) ) end def prepend_filter_to_chain(filters, position = :around, &block) write_inheritable_attribute('filter_chain', create_filters(filters, position, &block) + filter_chain) end def create_filters(filters, position, &block) filters, conditions = extract_conditions(filters, &block) filters.map! { |filter| find_or_create_filter(filter,position) } update_conditions(filters, conditions) filters end def find_or_create_filter(filter,position) if found_filter = find_filter(filter) { |f| f.send("#{position}?") } found_filter else f = class_for_filter(filter).new(filter) case position when :before BeforeFilterProxy.new(f) when :after AfterFilterProxy.new(f) else f end end end def class_for_filter(filter) case when filter.is_a?(Symbol) SymbolFilter when filter.respond_to?(:call) if filter.is_a?(Method) MethodFilter elsif filter.arity == 1 ProcFilter else ProcWithCallFilter end when filter.respond_to?(:filter) ClassFilter else raise(ActionControllerError, 'A filters must be a Symbol, Proc, Method, or object responding to filter.') end end def skip_filter_in_chain(*filters, &test) filters, conditions = extract_conditions(filters) filters.map! { |f| block_given? ? find_filter(f, &test) : find_filter(f) } filters.compact! if conditions.empty? delete_filters_in_chain(filters) else remove_actions_from_included_actions!(filters,conditions[: only] || []) conditions[: only], conditions[:except] = conditions[:except], conditions[: only] update_conditions(filters,conditions) end end def proxy_before_and_after_filter(filter) return filter unless filter_responds_to_before_and_after(filter) Proc.new do |controller, action| unless filter.before(controller) == false begin action.call ensure filter.after(controller) end end end end end module InstanceMethods def self.included(base) base.class_eval do alias_method_chain :perform_action, :filters alias_method_chain :process, :filters alias_method_chain :process_cleanup, :filters end end def perform_action_with_filters call_filter(self.class.filter_chain, 0) end def process_with_filters(request, response, method = :perform_action, *arguments) @before_filter_chain_aborted = false process_without_filters(request, response, method, *arguments) end def filter_chain self.class.filter_chain end def call_filter(chain, index) return (performed? || perform_action_without_filters) if index >= chain.size filter = chain[index] return call_filter(chain, index.next) if self.class.filter_excluded_from_action?(filter,action_name) halted = false filter.call(self) do halted = call_filter(chain, index.next) end halt_filter_chain(filter.filter, :no_yield) if halted == false unless @before_filter_chain_aborted halted end def halt_filter_chain(filter, reason) if logger case reason when :no_yield logger.info "Filter chain halted as [#{filter.inspect}] did not yield." when :returned_false logger.info "Filter chain halted as [#{filter.inspect}] returned false." end end @before_filter_chain_aborted = true return false end end end end
代码一目了然,十有八九能猜到,使用alias_method_chain给perform_action/process/process_cleanup加上filter
perform_action_with_filters -> call_filter -> filter.call -> BeforeFilterProxy.call || AfterFilterProxy.call || proxy_before_and_after_filter
其他则为一些增删改filter_chain的public方法,如before_filter、after_filter、around_filter、skip_filter等
评论 共 0 条 请登录后发表评论