Skip to content

Instantly share code, notes, and snippets.

@gazay
Created December 28, 2011 04:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gazay/1526231 to your computer and use it in GitHub Desktop.
Save gazay/1526231 to your computer and use it in GitHub Desktop.
Creating Fragment class for controller caching
# rails/actionpack/lib/action_view/cache_helper.rb
private
# TODO: Create an object that has caching read/write on it
def fragment_for(name = {}, options = nil, &block) #:nodoc:
if fragment = controller.find_fragment(name, options)
fragment.read
else
# VIEW TODO: Make #capture usable outside of ERB
# This dance is needed because Builder can't use capture
pos = output_buffer.length
yield
output_safe = output_buffer.html_safe?
content = output_buffer.slice!(pos..-1)
if output_safe
self.output_buffer = output_buffer.class.new(output_buffer)
end
if fragment = controller.create_fragment(name, content, options)
fragment.write
end
end
end
# rails/actionpack/lib/action_controller/caching/fragments.rb
module Fragments
# Given a key (as described in <tt>expire_fragment</tt>), 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 <tt>views/</tt> 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
# Find fragment from the location signified by
# <tt>key</tt> and creates new Fragment object
# (or find object in controller.fragments)
def find_fragment(key, options = nil)
return unless cache_configured?
# If we can keep fragment objects in controller instance
# here will be something like that:
# return self.fragments[key] if self.fragments[key]
if fragment_exist?(key, options)
key = fragment_cache_key(key)
ActionController::Caching::Fragment.new(key, options)
end
end
# Check if a cached fragment from the location signified by
# <tt>key</tt> exists (see <tt>expire_fragment</tt> for acceptable formats)
def fragment_exist?(key, options = nil)
return unless cache_configured?
key = fragment_cache_key(key)
instrument_fragment_cache :exist_fragment?, key do
cache_store.exist?(key, options)
end
end
# Create new Fragment object
# (and add it to controller.fragments)
def create_fragment(key, content, options = nil)
return unless cache_configured?
content = content.to_str
fragmented_key = fragment_cache_key(key)
ActionController::Caching::Fragment.new(fragmented_key, options, content)
# And if we can keep it in controller:
# self.fragments[key] = fragment
end
# Removes fragments from the cache.
#
# +key+ can take one of three forms:
#
# * String - This would normally take the form of a path, like
# <tt>pages/45/notes</tt>.
# * Hash - Treated as an implicit call to +url_for+, like
# <tt>{:controller => "pages", :action => "notes", :id => 45}</tt>
# * Regexp - Will remove any fragment that matches, so
# <tt>%r{pages/\d*/notes}</tt> might remove all notes. Make sure you
# don't use anchors in the regex (<tt>^</tt> or <tt>$</tt>) because
# the actual filename matched looks like
# <tt>./cache/filename/path.cache</tt>. Note: Regexp expiration is
# only supported on caches that can iterate over all keys (unlike
# memcached).
#
# +options+ is passed through to the cache store's <tt>delete</tt>
# method (or <tt>delete_matched</tt>, for Regexp keys.)
def expire_fragment(key, options = nil)
return unless cache_configured?
key = fragment_cache_key(key) unless key.is_a?(Regexp)
message = nil
instrument_fragment_cache :expire_fragment, key do
if key.is_a?(Regexp)
cache_store.delete_matched(key, options)
else
cache_store.delete(key, options)
end
end
end
end
class Fragment
# Fragment class using for working with cache entries
attr_reader :key, :options, :content
def initialize(key, options = nil, content = nil)
@key = key
@options = options
@content = content
end
# Reads a cached fragment from the location signified by <tt>key</tt>
def read
if content
content
else
instrument_fragment_cache :read_fragment, key do
result = cache_store.read(key, options)
@content = result.respond_to?(:html_safe) ? result.html_safe : result
end
end
end
# Writes <tt>content</tt> of the current Fragment object
# to the location signified by <tt>key</tt>
def write
instrument_fragment_cache :write_fragment, key do
cache_store.write(key, content, options)
end
content
end
# Expires current Fragment object
def expire
instrument_fragment_cache :expire_fragment, key do
cache_store.delete(key, options)
end
end
def instrument_fragment_cache(name, key)
ActiveSupport::Notifications.instrument("#{name}.action_controller", :key => key){ yield }
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment