Skip to content

Commit eda2d7d

Browse files
authored
Merge pull request rails#35145 from st0012/fix-35114
Fix partial caching ignore repeated items issue
2 parents 464d625 + e8688dd commit eda2d7d

File tree

3 files changed

+43
-13
lines changed

3 files changed

+43
-13
lines changed

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

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ def cache_collection_render(instrumentation_payload, view, template)
1717
# Result is a hash with the key represents the
1818
# key used for cache lookup and the value is the item
1919
# on which the partial is being rendered
20-
keyed_collection = collection_by_cache_keys(view, template)
20+
keyed_collection, ordered_keys = collection_by_cache_keys(view, template)
2121

2222
# Pull all partials from cache
2323
# Result is a hash, key matches the entry in
2424
# `keyed_collection` where the cache was retrieved and the
2525
# value is the value that was present in the cache
26-
cached_partials = collection_cache.read_multi(*keyed_collection.keys)
26+
cached_partials = collection_cache.read_multi(*keyed_collection.keys)
2727
instrumentation_payload[:cache_hits] = cached_partials.size
2828

2929
# Extract the items for the keys that are not found
@@ -40,11 +40,15 @@ def cache_collection_render(instrumentation_payload, view, template)
4040
rendered_partials = @collection.empty? ? [] : yield
4141

4242
index = 0
43-
fetch_or_cache_partial(cached_partials, template, order_by: keyed_collection.each_key) do
43+
keyed_partials = fetch_or_cache_partial(cached_partials, template, order_by: keyed_collection.each_key) do
4444
# This block is called once
4545
# for every cache miss while preserving order.
4646
rendered_partials[index].tap { index += 1 }
4747
end
48+
49+
ordered_keys.map do |key|
50+
keyed_partials[key]
51+
end
4852
end
4953

5054
def callable_cache_key?
@@ -56,8 +60,10 @@ def collection_by_cache_keys(view, template)
5660

5761
digest_path = view.digest_path_from_template(template)
5862

59-
@collection.each_with_object({}) do |item, hash|
60-
hash[expanded_cache_key(seed.call(item), view, template, digest_path)] = item
63+
@collection.each_with_object([{}, []]) do |item, (hash, ordered_keys)|
64+
key = expanded_cache_key(seed.call(item), view, template, digest_path)
65+
ordered_keys << key
66+
hash[key] = item
6167
end
6268
end
6369

@@ -82,15 +88,16 @@ def expanded_cache_key(key, view, template, digest_path)
8288
# If the partial is not already cached it will also be
8389
# written back to the underlying cache store.
8490
def fetch_or_cache_partial(cached_partials, template, order_by:)
85-
order_by.map do |cache_key|
86-
if content = cached_partials[cache_key]
87-
build_rendered_template(content, template)
88-
else
89-
yield.tap do |rendered_partial|
90-
collection_cache.write(cache_key, rendered_partial.body)
91-
end
91+
order_by.each_with_object({}) do |cache_key, hash|
92+
hash[cache_key] =
93+
if content = cached_partials[cache_key]
94+
build_rendered_template(content, template)
95+
else
96+
yield.tap do |rendered_partial|
97+
collection_cache.write(cache_key, rendered_partial.body)
98+
end
99+
end
92100
end
93-
end
94101
end
95102
end
96103
end
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<%= cached_set.first %> | <%= cached_set.second %> | <%= cached_set.third %> | <%= cached_set.fourth %> | <%= cached_set.fifth %>

actionview/test/template/render_test.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,28 @@ def b.to_partial_path; "test/partial_iteration_2"; end
811811
end
812812
end
813813

814+
test "collection caching with repeated collection" do
815+
sets = [
816+
[1, 2, 3, 4, 5],
817+
[1, 2, 3, 4, 4],
818+
[1, 2, 3, 4, 5],
819+
[1, 2, 3, 4, 4],
820+
[1, 2, 3, 4, 6]
821+
]
822+
823+
result = @view.render(partial: "test/cached_set", collection: sets, cached: true)
824+
825+
splited_result = result.split("\n")
826+
assert_equal 5, splited_result.count
827+
assert_equal [
828+
"1 | 2 | 3 | 4 | 5",
829+
"1 | 2 | 3 | 4 | 4",
830+
"1 | 2 | 3 | 4 | 5",
831+
"1 | 2 | 3 | 4 | 4",
832+
"1 | 2 | 3 | 4 | 6"
833+
], splited_result
834+
end
835+
814836
private
815837
def cache_key(*names, virtual_path)
816838
digest = ActionView::Digestor.digest name: virtual_path, format: :html, finder: @view.lookup_context, dependencies: []

0 commit comments

Comments
 (0)