Skip to content

Commit

Permalink
Added conditional support to caches_action [José Valim] [#166 state:r…
Browse files Browse the repository at this point in the history
…esolved]
  • Loading branch information
josh committed May 14, 2008
1 parent bca8751 commit 7708650
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 31 deletions.
37 changes: 19 additions & 18 deletions actionpack/lib/action_controller/caching/actions.rb
Expand Up @@ -9,7 +9,7 @@ module Caching
# class ListsController < ApplicationController
# before_filter :authenticate, :except => :public
# caches_page :public
# caches_action :show, :feed
# 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
Expand All @@ -27,15 +27,19 @@ module Caching
# 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 to pass a Proc that specifies when the action should be cached.
#
# 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 :show, :cache_path => Proc.new { |controller|
# controller.params[:user_id] ?
# 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
#
module Actions
def self.included(base) #:nodoc:
base.extend(ClassMethods)
Expand All @@ -49,7 +53,8 @@ module ClassMethods
# See ActionController::Caching::Actions for details.
def caches_action(*actions)
return unless cache_configured?
around_filter(ActionCacheFilter.new(*actions))
options = actions.extract_options!
around_filter(ActionCacheFilter.new(:cache_path => options.delete(:cache_path)), {:only => actions}.merge(options))
end
end

Expand All @@ -67,16 +72,12 @@ def expire_action(options = {})
end

class ActionCacheFilter #:nodoc:
def initialize(*actions, &block)
@options = actions.extract_options!
@actions = Set.new(actions)
def initialize(options, &block)
@options = options
end

def before(controller)
return unless @actions.include?(controller.action_name.intern)

cache_path = ActionCachePath.new(controller, path_options_for(controller, @options))

if cache = controller.read_fragment(cache_path.path)
controller.rendered_action_cache = true
set_content_type!(controller, cache_path.extension)
Expand All @@ -88,7 +89,7 @@ def before(controller)
end

def after(controller)
return if !@actions.include?(controller.action_name.intern) || controller.rendered_action_cache || !caching_allowed(controller)
return if controller.rendered_action_cache || !caching_allowed(controller)
controller.write_fragment(controller.action_cache_path.path, controller.response.body)
end

Expand All @@ -105,33 +106,33 @@ def caching_allowed(controller)
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.
Expand All @@ -140,4 +141,4 @@ def extract_extension(file_path)
end
end
end
end
end
80 changes: 67 additions & 13 deletions actionpack/test/controller/caching_test.rb
Expand Up @@ -6,6 +6,7 @@
FILE_STORE_PATH = File.join(File.dirname(__FILE__), '/../temp/', CACHE_DIR)
ActionController::Base.page_cache_directory = FILE_STORE_PATH
ActionController::Base.cache_store = :file_store, FILE_STORE_PATH
ActionController::Base.view_paths = [ File.dirname(__FILE__) + '/../fixtures/' ]

class PageCachingTestController < ActionController::Base
caches_page :ok, :no_content, :if => Proc.new { |c| !c.request.format.json? }
Expand Down Expand Up @@ -128,7 +129,7 @@ def test_should_cache_ok_at_custom_path
end
end
end

def test_page_caching_conditional_options
@request.env['HTTP_ACCEPT'] = 'application/json'
get :ok
Expand All @@ -151,12 +152,15 @@ def page_cached?(action)


class ActionCachingTestController < ActionController::Base
caches_action :index, :redirected, :forbidden
caches_action :index, :redirected, :forbidden, :if => Proc.new { |c| !c.request.format.json? }
caches_action :show, :cache_path => 'http://test.host/custom/show'
caches_action :edit, :cache_path => Proc.new { |c| c.params[:id] ? "http://test.host/#{c.params[:id]};edit" : "http://test.host/edit" }
caches_action :with_layout

layout 'talk_from_action.erb'

def index
@cache_this = Time.now.to_f.to_s
@cache_this = MockTime.now.to_f.to_s
render :text => @cache_this
end

Expand All @@ -169,14 +173,26 @@ def forbidden
headers["Status"] = "403 Forbidden"
end

def with_layout
@cache_this = MockTime.now.to_f.to_s
render :text => @cache_this, :layout => true
end

alias_method :show, :index
alias_method :edit, :index
alias_method :destroy, :index

def expire
expire_action :controller => 'action_caching_test', :action => 'index'
render :nothing => true
end
end

class MockTime < Time
# Let Time spicy to assure that Time.now != Time.now
def to_f
super+rand
end
end

class ActionCachingMockController
Expand Down Expand Up @@ -223,6 +239,36 @@ def test_simple_action_cache
assert_equal cached_time, @response.body
end

def test_simple_action_not_cached
get :destroy
cached_time = content_to_cache
assert_equal cached_time, @response.body
assert_cache_does_not_exist 'hostname.com/action_caching_test/destroy'
reset!

get :destroy
assert_not_equal cached_time, @response.body
end

def test_action_cache_with_layout
get :with_layout
cached_time = content_to_cache
assert_not_equal cached_time, @response.body
assert_cache_exists 'hostname.com/action_caching_test/with_layout'
reset!

get :with_layout
assert_not_equal cached_time, @response.body

assert_equal @response.body, read_fragment('hostname.com/action_caching_test/with_layout')
end

def test_action_cache_conditional_options
@request.env['HTTP_ACCEPT'] = 'application/json'
get :index
assert_cache_does_not_exist 'hostname.com/action_caching_test'
end

def test_action_cache_with_custom_cache_path
get :show
cached_time = content_to_cache
Expand Down Expand Up @@ -350,9 +396,22 @@ def reset!
end

def assert_cache_exists(path)
full_path = File.join(FILE_STORE_PATH, "views", path + '.cache')
full_path = cache_path(path)
assert File.exist?(full_path), "#{full_path.inspect} does not exist."
end

def assert_cache_does_not_exist(path)
full_path = cache_path(path)
assert !File.exist?(full_path), "#{full_path.inspect} should not exist."
end

def cache_path(path)
File.join(FILE_STORE_PATH, 'views', path + '.cache')
end

def read_fragment(path)
@controller.read_fragment(path)
end
end

class FragmentCachingTestController < ActionController::Base
Expand Down Expand Up @@ -516,7 +575,7 @@ class FunctionalFragmentCachingTest < Test::Unit::TestCase
def setup
ActionController::Base.perform_caching = true
@store = ActiveSupport::Cache::MemoryStore.new
ActionController::Base.cache_store = @store
ActionController::Base.cache_store = @store
@controller = FunctionalCachingController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
Expand All @@ -529,26 +588,21 @@ def test_fragment_caching
This bit's fragment cached
CACHED
assert_equal expected_body, @response.body

assert_equal "This bit's fragment cached", @store.read('views/test.host/functional_caching/fragment_cached')
end

def test_fragment_caching_in_partials
get :html_fragment_cached_with_partial
assert_response :success
assert_match /Fragment caching in a partial/, @response.body
assert_match "Fragment caching in a partial", @store.read('views/test.host/functional_caching/html_fragment_cached_with_partial')
end

def test_fragment_caching_in_rjs_partials
xhr :get, :js_fragment_cached_with_partial
assert_response :success
assert_match /Fragment caching in a partial/, @response.body
assert_match "Fragment caching in a partial", @store.read('views/test.host/functional_caching/js_fragment_cached_with_partial')
end
end





0 comments on commit 7708650

Please sign in to comment.