aboutsummaryrefslogtreecommitdiffstats
path: root/actionview
diff options
context:
space:
mode:
authorRoque Pinel <repinel@gmail.com>2015-06-12 15:30:04 -0400
committerRoque Pinel <repinel@gmail.com>2015-06-28 20:41:59 -0500
commitda1674576d01f8fe3ad3b448a4c3fa636bcce106 (patch)
tree5de420942c36f327b435761fba41a0f37ac80995 /actionview
parentaba43b7da1f45465fc3753225e5c73046342d4ec (diff)
downloadrails-da1674576d01f8fe3ad3b448a4c3fa636bcce106.tar.gz
rails-da1674576d01f8fe3ad3b448a4c3fa636bcce106.tar.bz2
rails-da1674576d01f8fe3ad3b448a4c3fa636bcce106.zip
Fix cache issue when different partials use the same collection
Adds the `virtual_path` option to `cache_fragment_name` so it can be provided when needed. That allows `cache_collection_render` to get the appropriate cache key with the digest generated based on the template and prevent collision with other templates that cache the same collection.
Diffstat (limited to 'actionview')
-rw-r--r--actionview/CHANGELOG.md9
-rw-r--r--actionview/lib/action_view/helpers/cache_helper.rb24
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb2
-rw-r--r--actionview/test/template/render_test.rb17
4 files changed, 36 insertions, 16 deletions
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index fe47fb47fd..04c0850fba 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,3 +1,12 @@
+* Always attach the template digest to the cache key for collection caching
+ even when `virtual_path` is not available from the view context.
+ Which could happen if the rendering was done directly in the controller
+ and not in a template.
+
+ Fixes #20535
+
+ *Roque Pinel*
+
* Improve detection of partial templates eligible for collection caching,
now allowing multi-line comments at the beginning of the template file.
diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb
index 72e2aa1807..8945575860 100644
--- a/actionview/lib/action_view/helpers/cache_helper.rb
+++ b/actionview/lib/action_view/helpers/cache_helper.rb
@@ -137,7 +137,7 @@ module ActionView
# The automatic cache multi read can be turned off like so:
#
# <%= render @notifications, cache: false %>
- def cache(name = {}, options = nil, &block)
+ def cache(name = {}, options = {}, &block)
if controller.respond_to?(:perform_caching) && controller.perform_caching
safe_concat(fragment_for(cache_fragment_name(name, options), options, &block))
else
@@ -153,7 +153,7 @@ module ActionView
# <b>All the topics on this project</b>
# <%= render project.topics %>
# <% end %>
- def cache_if(condition, name = {}, options = nil, &block)
+ def cache_if(condition, name = {}, options = {}, &block)
if condition
cache(name, options, &block)
else
@@ -169,22 +169,23 @@ module ActionView
# <b>All the topics on this project</b>
# <%= render project.topics %>
# <% end %>
- def cache_unless(condition, name = {}, options = nil, &block)
+ def cache_unless(condition, name = {}, options = {}, &block)
cache_if !condition, name, options, &block
end
# This helper returns the name of a cache key for a given fragment cache
- # call. By supplying skip_digest: true to cache, the digestion of cache
+ # call. By supplying +skip_digest:+ true to cache, the digestion of cache
# fragments can be manually bypassed. This is useful when cache fragments
# cannot be manually expired unless you know the exact key which is the
# case when using memcached.
- def cache_fragment_name(name = {}, options = nil)
- skip_digest = options && options[:skip_digest]
-
+ #
+ # The digest will be generated using +virtual_path:+ if it is provided.
+ #
+ def cache_fragment_name(name = {}, skip_digest: nil, virtual_path: nil)
if skip_digest
name
else
- fragment_name_with_digest(name)
+ fragment_name_with_digest(name, virtual_path)
end
end
@@ -198,10 +199,11 @@ module ActionView
private
- def fragment_name_with_digest(name) #:nodoc:
- if @virtual_path
+ def fragment_name_with_digest(name, virtual_path) #:nodoc:
+ virtual_path ||= @virtual_path
+ if virtual_path
names = Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name)
- digest = Digestor.digest name: @virtual_path, finder: lookup_context, dependencies: view_cache_dependencies
+ digest = Digestor.digest name: virtual_path, finder: lookup_context, dependencies: view_cache_dependencies
[ *names, digest ]
else
diff --git a/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb b/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
index c8268e226e..1147963882 100644
--- a/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
@@ -51,7 +51,7 @@ module ActionView
end
def expanded_cache_key(key)
- key = @view.fragment_cache_key(@view.cache_fragment_name(key))
+ key = @view.fragment_cache_key(@view.cache_fragment_name(key, virtual_path: @template.virtual_path))
key.frozen? ? key.dup : key # #read_multi & #write may require mutability, Dalli 2.6.0.
end
diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb
index 27bbb9b6c1..9c2c9507b7 100644
--- a/actionview/test/template/render_test.rb
+++ b/actionview/test/template/render_test.rb
@@ -7,7 +7,10 @@ end
module RenderTestCases
def setup_view(paths)
@assigns = { :secret => 'in the sauce' }
- @view = ActionView::Base.new(paths, @assigns)
+ @view = Class.new(ActionView::Base) do
+ def view_cache_dependencies; end
+ end.new(paths, @assigns)
+
@controller_view = TestController.new.view_context
# Reload and register danish language for testing
@@ -616,7 +619,7 @@ class CachedCollectionViewRenderTest < CachedViewRenderTest
test "with custom key" do
customer = Customer.new("david")
- key = ActionController::Base.new.fragment_cache_key([customer, 'key'])
+ key = cache_key([customer, 'key'], "test/_customer")
ActionView::PartialRenderer.collection_cache.write(key, 'Hello')
@@ -626,7 +629,7 @@ class CachedCollectionViewRenderTest < CachedViewRenderTest
test "automatic caching with inferred cache name" do
customer = CachedCustomer.new("david")
- key = ActionController::Base.new.fragment_cache_key(customer)
+ key = cache_key(customer, "test/_cached_customer")
ActionView::PartialRenderer.collection_cache.write(key, 'Cached')
@@ -636,11 +639,17 @@ class CachedCollectionViewRenderTest < CachedViewRenderTest
test "automatic caching with as name" do
customer = CachedCustomer.new("david")
- key = ActionController::Base.new.fragment_cache_key(customer)
+ key = cache_key(customer, "test/_cached_customer_as")
ActionView::PartialRenderer.collection_cache.write(key, 'Cached')
assert_equal "Cached",
@view.render(partial: "test/cached_customer_as", collection: [customer], as: :buyer)
end
+
+ private
+ def cache_key(names, virtual_path)
+ digest = ActionView::Digestor.digest name: virtual_path, finder: @view.lookup_context, dependencies: []
+ @view.fragment_cache_key([ *Array(names), digest ])
+ end
end