From 2a9ad9ccbc706e546bf02ec95f864944e7d7983b Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 3 Jan 2008 21:05:12 +0000 Subject: Moved the caching stores from ActionController::Caching::Fragments::* to ActiveSupport::Cache::*. If you're explicitly referring to a store, like ActionController::Caching::Fragments::MemoryStore, you need to update that reference with ActiveSupport::Cache::MemoryStore [DHH] Deprecated ActionController::Base.fragment_cache_store for ActionController::Base.cache_store [DHH] All fragment cache keys are now by default prefixed with the 'views/' namespace [DHH] Added ActiveRecord::Base.cache_key to make it easier to cache Active Records in combination with the new ActiveSupport::Cache::* libraries [DHH] Added ActiveSupport::Gzip.decompress/compress(source) as an easy wrapper for Zlib [Tobias Luetke] Included MemCache-Client to make the improved ActiveSupport::Cache::MemCacheStore work out of the box [Bob Cottrell, Eric Hodel] Added config.cache_store to environment options to control the default cache store (default is FileStore if tmp/cache is present, otherwise MemoryStore is used) [DHH] git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@8546 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- .../lib/action_controller/caching/fragments.rb | 153 +++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 actionpack/lib/action_controller/caching/fragments.rb (limited to 'actionpack/lib/action_controller/caching/fragments.rb') diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb new file mode 100644 index 0000000000..868af19780 --- /dev/null +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -0,0 +1,153 @@ +module ActionController #:nodoc: + module Caching + # Fragment caching is used for caching various blocks within templates without caching the entire action as a whole. This is useful when + # certain elements of an action change frequently or depend on complicated state while other parts rarely change or can be shared amongst multiple + # parties. The caching is doing using the cache helper available in the Action View. A template with caching might look something like: + # + # Hello <%= @name %> + # <% cache do %> + # All the topics in the system: + # <%= render :partial => "topic", :collection => Topic.find(:all) %> + # <% end %> + # + # This cache will bind to the name of the action that called it, so if this code was part of the view for the topics/list action, you would + # be able to invalidate it using expire_fragment(:controller => "topics", :action => "list"). + # + # This default behavior is of limited use if you need to cache multiple fragments per action or if the action itself is cached using + # caches_action, so we also have the option to qualify the name of the cached fragment with something like: + # + # <% cache(:action => "list", :action_suffix => "all_topics") do %> + # + # That would result in a name such as "/topics/list/all_topics", avoiding conflicts with the action cache and with any fragments that use a + # different suffix. Note that the URL doesn't have to really exist or be callable - the url_for system is just used to generate unique + # cache names that we can refer to when we need to expire the cache. + # + # The expiration call for this example is: + # + # expire_fragment(:controller => "topics", :action => "list", :action_suffix => "all_topics") + module Fragments + def self.included(base) #:nodoc: + base.class_eval do + class << self + def fragment_cache_store=(store_option) #:nodoc: + ActiveSupport::Deprecation.warn('The fragment_cache_store= method is now use cache_store=') + self.cache_store = store_option + end + + def fragment_cache_store #:nodoc: + ActiveSupport::Deprecation.warn('The fragment_cache_store method is now use cache_store') + cache_store + end + end + + def fragment_cache_store=(store_option) #:nodoc: + ActiveSupport::Deprecation.warn('The fragment_cache_store= method is now use cache_store=') + self.cache_store = store_option + end + + def fragment_cache_store #:nodoc: + ActiveSupport::Deprecation.warn('The fragment_cache_store method is now use cache_store') + cache_store + end + end + end + + # Given a key (as described in expire_fragment), returns a key suitable for use in reading, + # writing, or expiring a cached fragment. If the key is a hash, the generated key is the return + # value of url_for on that hash (without the protocol). All keys are prefixed with "views/" and uses + # ActiveSupport::Cache.expand_cache_key for the expansion. + def fragment_cache_key(key) + ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views) + end + + def fragment_for(block, name = {}, options = nil) #:nodoc: + unless perform_caching then block.call; return end + + buffer = yield + + if cache = read_fragment(name, options) + buffer.concat(cache) + else + pos = buffer.length + block.call + write_fragment(name, buffer[pos..-1], options) + end + end + + # Called by CacheHelper#cache + def cache_rxml_fragment(block, name = {}, options = nil) #:nodoc: + fragment_for(block, name, options) do + eval('xml.target!', block.binding) + end + end + + # Called by CacheHelper#cache + def cache_rjs_fragment(block, name = {}, options = nil) #:nodoc: + fragment_for(block, name, options) do + begin + debug_mode, ActionView::Base.debug_rjs = ActionView::Base.debug_rjs, false + eval('page.to_s', block.binding) + ensure + ActionView::Base.debug_rjs = debug_mode + end + end + end + + # Called by CacheHelper#cache + def cache_erb_fragment(block, name = {}, options = nil) #:nodoc: + fragment_for(block, name, options) do + eval(ActionView::Base.erb_variable, block.binding) + end + end + + # Writes content to the location signified by key (see expire_fragment for acceptable formats) + def write_fragment(key, content, options = nil) + return unless cache_configured? + + key = fragment_cache_key(key) + + self.class.benchmark "Cached fragment miss: #{key}" do + cache_store.write(key, content, options) + end + + content + end + + # Reads a cached fragment from the location signified by key (see expire_fragment for acceptable formats) + def read_fragment(key, options = nil) + return unless cache_configured? + + key = fragment_cache_key(key) + + self.class.benchmark "Cached fragment hit: #{key}" do + cache_store.read(key, options) + end + end + + # Name can take one of three forms: + # * String: This would normally take the form of a path like "pages/45/notes" + # * Hash: Is treated as an implicit call to url_for, like { :controller => "pages", :action => "notes", :id => 45 } + # * Regexp: Will destroy all the matched fragments, example: + # %r{pages/\d*/notes} + # Ensure you do not specify start and finish in the regex (^$) because + # the actual filename matched looks like ./cache/filename/path.cache + # Regexp expiration is only supported on caches that can iterate over + # all keys (unlike memcached). + def expire_fragment(key, options = nil) + return unless cache_configured? + + key = key.is_a?(Regexp) ? key : fragment_cache_key(key) + + if key.is_a?(Regexp) + self.class.benchmark "Expired fragments matching: #{key.source}" do + cache_store.delete_matched(key, options) + end + else + self.class.benchmark "Expired fragment: #{key}" do + cache_store.delete(key, options) + end + end + end + end + end +end \ No newline at end of file -- cgit v1.2.3