Permalink
Browse files

Caching refactoring

  • Loading branch information...
1 parent a288b74 commit 51c24ae3e3c1a260a304042aff5e1a7c56faabfe @wycats wycats committed Oct 29, 2009
@@ -3,26 +3,30 @@
require 'set'
module ActionController #:nodoc:
- # Caching is a cheap way of speeding up slow applications by keeping the result of calculations, renderings, and database calls
- # around for subsequent requests. Action Controller affords you three approaches in varying levels of granularity: Page, Action, Fragment.
+ # Caching is a cheap way of speeding up slow applications by keeping the result of
+ # calculations, renderings, and database calls around for subsequent requests.
+ # Action Controller affords you three approaches in varying levels of granularity:
+ # Page, Action, Fragment.
#
- # You can read more about each approach and the sweeping assistance by clicking the modules below.
- #
- # Note: To turn off all caching and sweeping, set Base.perform_caching = false.
+ # You can read more about each approach and the sweeping assistance by clicking the
+ # modules below.
#
+ # Note: To turn off all caching and sweeping, set
+ # config.action_controller.perform_caching = false.
#
# == Caching stores
#
- # All the caching stores from ActiveSupport::Cache are available to be used as backends for Action Controller caching. This setting only
- # affects action and fragment caching as page caching is always written to disk.
+ # All the caching stores from ActiveSupport::Cache are available to be used as backends
+ # for Action Controller caching. This setting only affects action and fragment caching
+ # as page caching is always written to disk.
#
# Configuration examples (MemoryStore is the default):
#
- # ActionController::Base.cache_store = :memory_store
- # ActionController::Base.cache_store = :file_store, "/path/to/cache/directory"
- # ActionController::Base.cache_store = :drb_store, "druby://localhost:9192"
- # ActionController::Base.cache_store = :mem_cache_store, "localhost"
- # ActionController::Base.cache_store = MyOwnStore.new("parameter")
+ # config.action_controller.cache_store = :memory_store
+ # config.action_controller.cache_store = :file_store, "/path/to/cache/directory"
+ # config.action_controller.cache_store = :drb_store, "druby://localhost:9192"
+ # config.action_controller.cache_store = :mem_cache_store, "localhost"
+ # config.action_controller.cache_store = MyOwnStore.new("parameter")
module Caching
extend ActiveSupport::Concern
@@ -46,8 +50,10 @@ def self.cache_store=(store_option)
@@perform_caching = true
cattr_accessor :perform_caching
+ end
- def self.cache_configured?
+ module ClassMethods
+ def cache_configured?
perform_caching && cache_store
end
end
@@ -2,66 +2,78 @@
module ActionController #:nodoc:
module Caching
- # Action caching is similar to page caching by the fact that the entire output of the response is
- # cached, but unlike page caching, every request still goes through the Action Pack. The key benefit
- # of this is that filters are run before the cache is served, which allows for authentication and other
- # restrictions on whether someone is allowed to see the cache. Example:
+ # Action caching is similar to page caching by the fact that the entire
+ # output of the response is cached, but unlike page caching, every
+ # request still goes through the Action Pack. The key benefit
+ # of this is that filters are run before the cache is served, which
+ # allows for authentication and other restrictions on whether someone
+ # is allowed to see the cache. Example:
#
# class ListsController < ApplicationController
# before_filter :authenticate, :except => :public
# caches_page :public
# caches_action :index, :show, :feed
# end
#
- # In this example, the public action doesn't require authentication, so it's possible to use the faster
- # page caching method. But both the show and feed action are to be shielded behind the authenticate
+ # In this example, the public action doesn't require authentication,
+ # so it's possible to use the faster page caching method. But both
+ # the show and feed action are to be shielded behind the authenticate
# filter, so we need to implement those as action caches.
#
- # Action caching internally uses the fragment caching and an around filter to do the job. The fragment
- # cache is named according to both the current host and the path. So a page that is accessed at
+ # Action caching internally uses the fragment caching and an around
+ # filter to do the job. The fragment cache is named according to both
+ # the current host and the path. So a page that is accessed at
# http://david.somewhere.com/lists/show/1 will result in a fragment named
- # "david.somewhere.com/lists/show/1". This allows the cacher to differentiate between
- # "david.somewhere.com/lists/" and
- # "jamis.somewhere.com/lists/" -- which is a helpful way of assisting the subdomain-as-account-key
- # pattern.
+ # "david.somewhere.com/lists/show/1". This allows the cacher to
+ # differentiate between "david.somewhere.com/lists/" and
+ # "jamis.somewhere.com/lists/" -- which is a helpful way of assisting
+ # the subdomain-as-account-key pattern.
#
- # Different representations of the same resource, e.g. <tt>http://david.somewhere.com/lists</tt> and
+ # Different representations of the same resource, e.g.
+ # <tt>http://david.somewhere.com/lists</tt> and
# <tt>http://david.somewhere.com/lists.xml</tt>
- # are treated like separate requests and so are cached separately. Keep in mind when expiring an
- # action cache that <tt>:action => 'lists'</tt> is not the same as
+ # are treated like separate requests and so are cached separately.
+ # Keep in mind when expiring an action cache that
+ # <tt>:action => 'lists'</tt> is not the same as
# <tt>:action => 'list', :format => :xml</tt>.
#
- # You can set modify the default action cache path by passing a :cache_path option. This will be
- # passed directly to ActionCachePath.path_for. This is handy for actions with multiple possible
- # routes that should be cached differently. If a block is given, it is called with the current
- # controller instance.
+ # You can set modify the default action cache path by passing a
+ # :cache_path option. This will be passed directly to
+ # ActionCachePath.path_for. This is handy for actions with multiple
+ # possible routes that should be cached differently. If a block is
+ # given, it is called with the current controller instance.
#
- # And you can also use :if (or :unless) to pass a Proc that specifies when the action should
- # be cached.
+ # And you can also use :if (or :unless) to pass a Proc that
+ # specifies when the action should be cached.
#
# Finally, if you are using memcached, you can also pass :expires_in.
#
# class ListsController < ApplicationController
# before_filter :authenticate, :except => :public
# caches_page :public
- # caches_action :index, :if => proc { |c| !c.request.format.json? } # cache if is not a JSON request
- # caches_action :show, :cache_path => { :project => 1 }, :expires_in => 1.hour
- # caches_action :feed, :cache_path => proc { |controller|
- # controller.params[:user_id] ?
- # controller.send(:user_list_url, controller.params[:user_id], controller.params[:id]) :
- # controller.send(:list_url, controller.params[:id]) }
+ # caches_action :index, :if => proc do |c|
+ # !c.request.format.json? # cache if is not a JSON request
+ # end
+ #
+ # caches_action :show, :cache_path => { :project => 1 },
+ # :expires_in => 1.hour
+ #
+ # caches_action :feed, :cache_path => proc do |controller|
+ # if controller.params[:user_id]
+ # controller.send(:user_list_url,
+ # controller.params[:user_id], controller.params[:id])
+ # else
+ # controller.send(:list_url, controller.params[:id])
+ # end
+ # end
# end
#
- # If you pass :layout => false, it will only cache your action content. It is useful when your
- # layout has dynamic information.
+ # If you pass :layout => false, it will only cache your action
+ # content. It is useful when your layout has dynamic information.
#
module Actions
extend ActiveSupport::Concern
- included do
- attr_accessor :rendered_action_cache, :action_cache_path
- end
-
module ClassMethods
# Declares that +actions+ should be cached.
# See ActionController::Caching::Actions for details.
@@ -76,11 +88,7 @@ def caches_action(*actions)
end
def _render_cache_fragment(cache, extension, layout)
- self.rendered_action_cache = true
- response.content_type = Mime[extension].to_s if extension
- options = { :text => cache }
- options.merge!(:layout => true) if layout
- render options
+ render :text => cache, :layout => layout, :content_type => Mime[extension || :html]
end
def _save_fragment(name, layout, options)
@@ -90,17 +98,17 @@ def _save_fragment(name, layout, options)
write_fragment(name, content, options)
end
- protected
- def expire_action(options = {})
- return unless cache_configured?
+ protected
+ def expire_action(options = {})
+ return unless cache_configured?
- actions = options[:action]
- if actions.is_a?(Array)
- actions.each {|action| expire_action(options.merge(:action => action)) }
- else
- expire_fragment(ActionCachePath.path_for(self, options, false))
- end
+ actions = options[:action]
+ if actions.is_a?(Array)
+ actions.each {|action| expire_action(options.merge(:action => action)) }
+ else
+ expire_fragment(ActionCachePath.new(self, options, false).path)
end
+ end
class ActionCacheFilter #:nodoc:
def initialize(options, &block)
@@ -109,7 +117,12 @@ def initialize(options, &block)
end
def filter(controller)
- path_options = @cache_path.respond_to?(:call) ? @cache_path.call(controller) : @cache_path
+ path_options = if @cache_path.respond_to?(:call)
+ controller.instance_exec(controller, &@cache_path)
+ else
+ @cache_path
+ end
+
cache_path = ActionCachePath.new(controller, path_options || {})
if cache = controller.read_fragment(cache_path.path, @store_options)
@@ -124,41 +137,25 @@ def filter(controller)
class ActionCachePath
attr_reader :path, :extension
- class << self
- def path_for(controller, options, infer_extension = true)
- new(controller, options, infer_extension).path
- end
- end
-
# If +infer_extension+ is true, the cache path extension is looked up from the request's
# path & format. This is desirable when reading and writing the cache, but not when
# expiring the cache - expire_action should expire the same files regardless of the
# request format.
def initialize(controller, options = {}, infer_extension = true)
if infer_extension
- extract_extension(controller.request)
- options = options.reverse_merge(:format => @extension) if options.is_a?(Hash)
+ @extension = controller.params[:format]
+ options.reverse_merge!(:format => @extension) if options.is_a?(Hash)
end
- path = controller.url_for(options).split('://').last
- normalize!(path)
- add_extension!(path, @extension)
- @path = URI.unescape(path)
+ path = controller.url_for(options).split(%r{://}).last
+ @path = normalize!(path)
end
private
def normalize!(path)
path << 'index' if path[-1] == ?/
- end
-
- def add_extension!(path, extension)
path << ".#{extension}" if extension and !path.ends_with?(extension)
- end
-
- def extract_extension(request)
- # Don't want just what comes after the last '.' to accommodate multi part extensions
- # such as tar.gz.
- @extension = request.path[/^[^.]+\.(.+)$/, 1] || request.cache_format
+ URI.unescape(path)
end
end
end
@@ -70,7 +70,7 @@ def after(controller)
protected
# gets the action cache path for the given options.
def action_path_for(options)
- ActionController::Caching::Actions::ActionCachePath.path_for(controller, options)
+ Actions::ActionCachePath.new(controller, options).path
end
# Retrieve instance variables set in the controller.
@@ -25,9 +25,11 @@ class << self
# cattr_reader :protected_instance_variables
cattr_accessor :protected_instance_variables
- self.protected_instance_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller
- @action_name @before_filter_chain_aborted @action_cache_path @_headers @_params
- @_flash @_response)
+ self.protected_instance_variables = %w(@assigns @performed_redirect @performed_render
+ @variables_added @request_origin @url
+ @parent_controller @action_name
+ @before_filter_chain_aborted @_headers @_params
+ @_flash @_response)
# Indicates whether or not optimise the generated named
# route helper methods
@@ -98,7 +98,7 @@ def content_type
end
def forgery_whitelisted?
- method == :get || xhr? || !(!content_type.nil? && content_type.verify_request?)
+ method == :get || xhr? || content_type.nil? || !content_type.verify_request?
end
def media_type
@@ -205,10 +205,6 @@ def template_format
end
end
- def cache_format
- parameters[:format]
- end
-
# Returns true if the request's "X-Requested-With" header contains
# "XMLHttpRequest". (The Prototype Javascript library sends this header with
# every Ajax request.)
@@ -228,12 +228,16 @@ def url_for(*args)
@mock_url_for
end
+ def params
+ request.parameters
+ end
+
def request
mocked_path = @mock_path
Object.new.instance_eval(<<-EVAL)
def path; '#{@mock_path}' end
def format; 'all' end
- def cache_format; nil end
+ def parameters; {:format => nil}; end
self
EVAL
end
@@ -466,7 +470,7 @@ def test_empty_path_is_normalized
@mock_controller.mock_url_for = 'http://example.org/'
@mock_controller.mock_path = '/'
- assert_equal 'example.org/index', @path_class.path_for(@mock_controller, {})
+ assert_equal 'example.org/index', @path_class.new(@mock_controller, {}).path
end
def test_file_extensions

0 comments on commit 51c24ae

Please sign in to comment.