diff options
3 files changed, 30 insertions, 5 deletions
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 7faf3cd8c6..9c2619dc3d 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -393,9 +393,14 @@ class CollectionCacheController < ActionController::Base @customers = [Customer.new('david', 1)] render partial: 'customers/commented_customer', collection: @customers, as: :customer, cached: true end + + def index_with_callable_cache_key + @customers = [Customer.new('david', 1)] + render partial: 'customers/customer', collection: @customers, cached: -> customer { 'cached_david' } + end end -class AutomaticCollectionCacheTest < ActionController::TestCase +class CollectionCacheTest < ActionController::TestCase def setup super @controller = CollectionCacheController.new @@ -438,6 +443,11 @@ class AutomaticCollectionCacheTest < ActionController::TestCase assert_equal 1, @controller.partial_rendered_times end + def test_caching_with_callable_cache_key + get :index_with_callable_cache_key + assert_customer_cached 'cached_david', 'david, 1' + end + private def assert_customer_cached(key, content) assert_match content, diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb index 4eaaa239e2..e28b02f015 100644 --- a/actionview/lib/action_view/helpers/cache_helper.rb +++ b/actionview/lib/action_view/helpers/cache_helper.rb @@ -130,9 +130,10 @@ module ActionView # # When rendering a collection of objects that each use the same partial, a `cached` # option can be passed. + # # For collections rendered such: # - # <%= render partial: 'notifications/notification', collection: @notifications, cached: true %> + # <%= render partial: 'projects/project', collection: @projects, cached: true %> # # The `cached: true` will make Action View's rendering read several templates # from cache at once instead of one call per template. @@ -142,13 +143,21 @@ module ActionView # Works great alongside individual template fragment caching. # For instance if the template the collection renders is cached like: # - # # notifications/_notification.html.erb - # <% cache notification do %> + # # projects/_project.html.erb + # <% cache project do %> # <%# ... %> # <% end %> # # Any collection renders will find those cached templates when attempting # to read multiple templates at once. + # + # If your collection cache depends on multiple sources (try to avoid this to keep things simple), + # you can name all these dependencies as part of a block that returns an array: + # + # <%= render partial: 'projects/project', collection: @projects, cached: -> project { [ project, current_user ] } %> + # + # This will include both records as part of the cache key and updating either of them will + # expire the cache. def cache(name = {}, options = {}, &block) if controller.respond_to?(:perform_caching) && controller.perform_caching name_options = options.slice(:skip_digest, :virtual_path) 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 f7deba94ce..1fbe209200 100644 --- a/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb +++ b/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb @@ -25,9 +25,15 @@ module ActionView end end + def callable_cache_key? + @options[:cached].respond_to?(:call) + end + def collection_by_cache_keys + seed = callable_cache_key? ? @options[:cached] : ->(i) { i } + @collection.each_with_object({}) do |item, hash| - hash[expanded_cache_key(item)] = item + hash[expanded_cache_key(seed.call(item))] = item end end |