1 概述
cache机制分为5个Module实现
Pages, Actions, Fragments, Sweeping, SqlCache
2 使用
今天先看看ActionCache:
Action Cache和Page Cache的相同之处在于-整个输出被cache起来了,
但是两者的区别在于-Action Cache每个请求仍然会经过整个Action Pack,
这个cache的好处在于在访问页面之前,filter仍然会执行,这样可以判断权限之类的:
- class ListsController < ApplicationController
- before_filter :authenticate, :except => :public
- caches_page :public
- caches_action :index, :show, :feed
- end
但是其他的方法,比如index show feed需要判断权限,所以可以作为action cache级别,
整个Action cache的实现采用了around filter来完成,
fragment cache的命名采用了host和path组合的模式,
http://david.somewhere.com/lists/show/1 fragment名字就是 "david.somewhere.com/lists/show/1"
这样就便于子域名的使用。
http://david.somewhere.com/lists和http://david.somewhere.com/lists.xml 是作为不同的request分开处理的。所以,记住使action cache :action => 'lists'过期和使:action => 'list', :format => :xml过期不同。
可以手动设置:cache_path来设置cache路径,也支持:if判断:
- class ListsController < ApplicationController
- before_filter :authenticate, :except => :public
- caches_page :public
- caches_action :index, :if => Proc.new { |c| !c.request.format.json? } # cache if is not a JSON request
- caches_action :show, :cache_path => { :project => 1 }
- caches_action :feed, :cache_path => Proc.new { |controller|
- controller.params[:user_id] ?
- controller.send(:user_list_url, c.params[:user_id], c.params[:id]) :
- controller.send(:list_url, c.params[:id]) }
- end
2 源代码分析
- def self.included(base) #:nodoc:
- base.extend(ClassMethods)
- base.class_eval do
- attr_accessor :rendered_action_cache, :action_cache_path
- end
- end
1 增加了ClassMethods提供的方法,
2 增加了rendered_action_cache 和 action_cache_path 两个变量用于在around_filter传递变量
- module ClassMethods
- # Declares that +actions+ should be cached.
- # See ActionController::Caching::Actions for details.
- def caches_action(*actions)
- return unless cache_configured?
- options = actions.extract_options!
- # 给方法增加一个around_filter来进行cache操作
- around_filter(ActionCacheFilter.new(:cache_path => options.delete(:cache_path)), {:only => actions}.merge(options))
- end
- end
- protected
- def expire_action(options = {})
- return unless cache_configured?
- if options[:action].is_a?(Array)
- options[:action].dup.each do |action|
- expire_fragment(ActionCachePath.path_for(self, options.merge({ :action => action })))
- end
- else
- expire_fragment(ActionCachePath.path_for(self, options))
- end
- end
这个AroundFilter的实现ActionCacheFilter :
- class ActionCacheFilter #:nodoc:
- def initialize(options, &block)
- @options = options
- end
- def before(controller)
- # 获得cache_path
- cache_path = ActionCachePath.new(controller, path_options_for(controller, @options))
- if cache = controller.read_fragment(cache_path.path)
- # 如果有cache那么设置 rendered_action_cache ,并且设置response content_type,直接返回cache文本
- controller.rendered_action_cache = true
- set_content_type!(controller, cache_path.extension)
- controller.send!(:render_for_text, cache)
- false
- else
- # 如果没有cache那么action_cache_path设置为cache_path
- controller.action_cache_path = cache_path
- end
- end
- def after(controller)
- # 如果存在cache并且允许cache
- return if controller.rendered_action_cache || !caching_allowed(controller)
- # 第一次,那么写cache
- controller.write_fragment(controller.action_cache_path.path, controller.response.body)
- end
- private
- def set_content_type!(controller, extension)
- controller.response.content_type = Mime::Type.lookup_by_extension(extension).to_s if extension
- end
- def path_options_for(controller, options)
- ((path_options = options[:cache_path]).respond_to?(:call) ? path_options.call(controller) : path_options) || {}
- end
- def caching_allowed(controller)
- # 如果是get方法,如果是OK状态
- controller.request.get? && controller.response.headers['Status'].to_i == 200
- end
- end
- class ActionCachePath
- attr_reader :path, :extension
- class << self
- def path_for(controller, options)
- new(controller, options).path
- end
- end
- def initialize(controller, options = {})
- @extension = extract_extension(controller.request.path)
- path = controller.url_for(options).split('://').last
- normalize!(path)
- add_extension!(path, @extension)
- @path = URI.unescape(path)
- end
- private
- def normalize!(path)
- path << 'index' if path[-1] == ?/
- end
- def add_extension!(path, extension)
- path << ".#{extension}" if extension
- end
- def extract_extension(file_path)
- # Don't want just what comes after the last '.' to accommodate multi part extensions
- # such as tar.gz.
- file_path[/^[^.]+/.(.+)$/, 1]
- end
- end