Skip to content

Commit c90c9b2

Browse files
committed
Merge pull request rails#20538 from repinel/fix-render-caching-issue
Fix cache issue when different partials use the same collection
2 parents aba43b7 + da16745 commit c90c9b2

File tree

4 files changed

+36
-16
lines changed

4 files changed

+36
-16
lines changed

actionview/CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
* Always attach the template digest to the cache key for collection caching
2+
even when `virtual_path` is not available from the view context.
3+
Which could happen if the rendering was done directly in the controller
4+
and not in a template.
5+
6+
Fixes #20535
7+
8+
*Roque Pinel*
9+
110
* Improve detection of partial templates eligible for collection caching,
211
now allowing multi-line comments at the beginning of the template file.
312

actionview/lib/action_view/helpers/cache_helper.rb

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ module CacheHelper
137137
# The automatic cache multi read can be turned off like so:
138138
#
139139
# <%= render @notifications, cache: false %>
140-
def cache(name = {}, options = nil, &block)
140+
def cache(name = {}, options = {}, &block)
141141
if controller.respond_to?(:perform_caching) && controller.perform_caching
142142
safe_concat(fragment_for(cache_fragment_name(name, options), options, &block))
143143
else
@@ -153,7 +153,7 @@ def cache(name = {}, options = nil, &block)
153153
# <b>All the topics on this project</b>
154154
# <%= render project.topics %>
155155
# <% end %>
156-
def cache_if(condition, name = {}, options = nil, &block)
156+
def cache_if(condition, name = {}, options = {}, &block)
157157
if condition
158158
cache(name, options, &block)
159159
else
@@ -169,22 +169,23 @@ def cache_if(condition, name = {}, options = nil, &block)
169169
# <b>All the topics on this project</b>
170170
# <%= render project.topics %>
171171
# <% end %>
172-
def cache_unless(condition, name = {}, options = nil, &block)
172+
def cache_unless(condition, name = {}, options = {}, &block)
173173
cache_if !condition, name, options, &block
174174
end
175175

176176
# This helper returns the name of a cache key for a given fragment cache
177-
# call. By supplying skip_digest: true to cache, the digestion of cache
177+
# call. By supplying +skip_digest:+ true to cache, the digestion of cache
178178
# fragments can be manually bypassed. This is useful when cache fragments
179179
# cannot be manually expired unless you know the exact key which is the
180180
# case when using memcached.
181-
def cache_fragment_name(name = {}, options = nil)
182-
skip_digest = options && options[:skip_digest]
183-
181+
#
182+
# The digest will be generated using +virtual_path:+ if it is provided.
183+
#
184+
def cache_fragment_name(name = {}, skip_digest: nil, virtual_path: nil)
184185
if skip_digest
185186
name
186187
else
187-
fragment_name_with_digest(name)
188+
fragment_name_with_digest(name, virtual_path)
188189
end
189190
end
190191

@@ -198,10 +199,11 @@ def fragment_cache_key(key)
198199

199200
private
200201

201-
def fragment_name_with_digest(name) #:nodoc:
202-
if @virtual_path
202+
def fragment_name_with_digest(name, virtual_path) #:nodoc:
203+
virtual_path ||= @virtual_path
204+
if virtual_path
203205
names = Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name)
204-
digest = Digestor.digest name: @virtual_path, finder: lookup_context, dependencies: view_cache_dependencies
206+
digest = Digestor.digest name: virtual_path, finder: lookup_context, dependencies: view_cache_dependencies
205207

206208
[ *names, digest ]
207209
else

actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def collection_by_cache_keys
5151
end
5252

5353
def expanded_cache_key(key)
54-
key = @view.fragment_cache_key(@view.cache_fragment_name(key))
54+
key = @view.fragment_cache_key(@view.cache_fragment_name(key, virtual_path: @template.virtual_path))
5555
key.frozen? ? key.dup : key # #read_multi & #write may require mutability, Dalli 2.6.0.
5656
end
5757

actionview/test/template/render_test.rb

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ class TestController < ActionController::Base
77
module RenderTestCases
88
def setup_view(paths)
99
@assigns = { :secret => 'in the sauce' }
10-
@view = ActionView::Base.new(paths, @assigns)
10+
@view = Class.new(ActionView::Base) do
11+
def view_cache_dependencies; end
12+
end.new(paths, @assigns)
13+
1114
@controller_view = TestController.new.view_context
1215

1316
# Reload and register danish language for testing
@@ -616,7 +619,7 @@ class CachedCustomer < Customer; end
616619

617620
test "with custom key" do
618621
customer = Customer.new("david")
619-
key = ActionController::Base.new.fragment_cache_key([customer, 'key'])
622+
key = cache_key([customer, 'key'], "test/_customer")
620623

621624
ActionView::PartialRenderer.collection_cache.write(key, 'Hello')
622625

@@ -626,7 +629,7 @@ class CachedCustomer < Customer; end
626629

627630
test "automatic caching with inferred cache name" do
628631
customer = CachedCustomer.new("david")
629-
key = ActionController::Base.new.fragment_cache_key(customer)
632+
key = cache_key(customer, "test/_cached_customer")
630633

631634
ActionView::PartialRenderer.collection_cache.write(key, 'Cached')
632635

@@ -636,11 +639,17 @@ class CachedCustomer < Customer; end
636639

637640
test "automatic caching with as name" do
638641
customer = CachedCustomer.new("david")
639-
key = ActionController::Base.new.fragment_cache_key(customer)
642+
key = cache_key(customer, "test/_cached_customer_as")
640643

641644
ActionView::PartialRenderer.collection_cache.write(key, 'Cached')
642645

643646
assert_equal "Cached",
644647
@view.render(partial: "test/cached_customer_as", collection: [customer], as: :buyer)
645648
end
649+
650+
private
651+
def cache_key(names, virtual_path)
652+
digest = ActionView::Digestor.digest name: virtual_path, finder: @view.lookup_context, dependencies: []
653+
@view.fragment_cache_key([ *Array(names), digest ])
654+
end
646655
end

0 commit comments

Comments
 (0)