Rails源代码分析(20):ActionController::Caching(4) FragmentCache

本文深入探讨了Rails中Fragment缓存的应用场景和技术细节,包括如何使用Fragment缓存提高页面加载效率,以及源代码级别的实现原理分析。
1 应用
Fragment是用来cache一个block的时候使用的,
常用于一个页面的页面一部分变化,一部分不变化的情况,
  1.        <b>Hello <%= @name %></b>
  2.        <% cache do %>
  3.          All the topics in the system:
  4.          <%= render :partial => "topic":collection => Topic.find(:all) %>
  5.        <% end %>
如果这部分代码是topics/list的一部分,
使用
 expire_fragment(:controller => "topics", :action => "list")
使之失效

我们也可以使用下面这句来指定多个fragment:
<% cache(:action => "list", :action_suffix => "all_topics") do %>

使用
 expire_fragment(:controller => "topics", :action => "list", :action_suffix => "all_topics")
使之失效


2 源代码分析
  1.       def self.included(base) #:nodoc:
  2.         base.class_eval do
  3.          # 类方法
  4.           class << self
  5.             def fragment_cache_store=(store_option) #:nodoc:
  6.               ActiveSupport::Deprecation.warn('The fragment_cache_store= method is now use cache_store=')
  7.               self.cache_store = store_option
  8.             end
  9.           
  10.             def fragment_cache_store #:nodoc:
  11.               ActiveSupport::Deprecation.warn('The fragment_cache_store method is now use cache_store')
  12.               cache_store
  13.             end
  14.           end
  15.           # 实例方法 
  16.          def fragment_cache_store=(store_option) #:nodoc:
  17.             ActiveSupport::Deprecation.warn('The fragment_cache_store= method is now use cache_store=')
  18.             self.cache_store = store_option
  19.           end
  20.         
  21.           def fragment_cache_store #:nodoc:
  22.             ActiveSupport::Deprecation.warn('The fragment_cache_store method is now use cache_store')
  23.             cache_store
  24.           end
  25.         end
  26.       end

  1.       def fragment_cache_key(key) # 声称key字符串
  2.         ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
  3.       end
  4.       def fragment_for(block, name = {}, options = nil#:nodoc:
  5.         unless perform_caching then block.call; return end
  6.         buffer = yield
  7.         if cache = read_fragment(name, options) #如果读取到了cache内容,那么返回内容
  8.           buffer.concat(cache)
  9.         else # 如果没有读取到
  10.           pos = buffer.length
  11.           block.call
  12.           write_fragment(name, buffer[pos..-1], options)
  13.         end
  14.       end
  15.       # Writes <tt>content</tt> to the location signified by <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats)
  16.       def write_fragment(key, content, options = nil)
  17.         return unless cache_configured?
  18.         key = fragment_cache_key(key)
  19.         self.class.benchmark "Cached fragment miss: #{key}" do
  20.           cache_store.write(key, content, options)
  21.         end
  22.         content
  23.       end
  24.       # Reads a cached fragment from the location signified by <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats)
  25.       def read_fragment(key, options = nil)
  26.         return unless cache_configured?
  27.         key = fragment_cache_key(key)
  28.         self.class.benchmark "Cached fragment hit: #{key}" do
  29.           cache_store.read(key, options)
  30.         end
  31.       end
  32.       # Check if a cached fragment from the location signified by <tt>key</tt> exists (see <tt>expire_fragment</tt> for acceptable formats)
  33.       def fragment_exist?(key, options = nil)
  34.         return unless cache_configured?
  35.         key = fragment_cache_key(key)
  36.         self.class.benchmark "Cached fragment exists?: #{key}" do
  37.           cache_store.exist?(key, options)
  38.         end
  39.       end
  40.       # Name can take one of three forms:
  41.       # * String: This would normally take the form of a path like "pages/45/notes"
  42.       # * Hash: Is treated as an implicit call to url_for, like { :controller => "pages", :action => "notes", :id => 45 }
  43.       # * Regexp: Will destroy all the matched fragments, example:
  44.       #     %r{pages//d*/notes}
  45.       #   Ensure you do not specify start and finish in the regex (^$) because
  46.       #   the actual filename matched looks like ./cache/filename/path.cache
  47.       #   Regexp expiration is only supported on caches that can iterate over
  48.       #   all keys (unlike memcached).
  49.       def expire_fragment(key, options = nil)
  50.         return unless cache_configured?
  51.         key = key.is_a?(Regexp) ? key : fragment_cache_key(key)
  52.         if key.is_a?(Regexp)
  53.           self.class.benchmark "Expired fragments matching: #{key.source}" do
  54.             cache_store.delete_matched(key, options) #支持正则表达式失效策略
  55.           end
  56.         else
  57.           self.class.benchmark "Expired fragment: #{key}" do
  58.             cache_store.delete(key, options) #支持一般的key失效策略
  59.           end
  60.         end
  61.       end
调用的时候
我们提前看看View里面的:
  1.       def cache(name = {}, options = nil, &block)
  2.         handler = Template.handler_class_for_extension(current_render_extension.to_sym)
  3.         handler.new(@controller).cache_fragment(block, name, options)
  4.       end
  1.       def cache_fragment(block, name = {}, options = nil)
  2.         @view.fragment_for(block, name, options) do
  3.           eval('xml.target!', block.binding)
  4.         end
  5.       end


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值