diff --git a/lib/active_model/serializer/adapter/attributes.rb b/lib/active_model/serializer/adapter/attributes.rb index 52fec33fd..5d6d0b717 100644 --- a/lib/active_model/serializer/adapter/attributes.rb +++ b/lib/active_model/serializer/adapter/attributes.rb @@ -24,18 +24,31 @@ def fragment_cache(cached_hash, non_cached_hash) private def serializable_hash_for_collection(options) - if options[:batch_cache].blank? && ActiveModelSerializers.config.cache_store.present? - keys = CachedSerializer.object_cache_keys(serializer, @include_tree) - if keys.present? - values = ActiveModelSerializers.config.cache_store.read_multi(*keys) - - options.merge!(batch_cache: values) - end - end + options.merge!(batch_cache(options)) serializer.map { |s| Attributes.new(s, instance_options).serializable_hash(options) } end + # Read cache from cache_store, and set those into options[:batch_cache] + # @return [Hash] a batch of cache + def batch_cache(options) + return {} if options[:batch_cache].present? || ActiveModelSerializers.config.cache_store.blank? + + keys = CachedSerializer.object_cache_keys(serializer, @include_tree) + + return {} if keys.blank? + + { batch_cache: ActiveModelSerializers.config.cache_store.read_multi(*keys) } + end + + # Get attributes from options[:batch_cache]. + # @return [Hash] cached attributes + def batch_cache_for(cached_serializer, options) + return unless options[:batch_cache].present? + + options[:batch_cache][cached_serializer.cache_key] + end + def serializable_hash_for_single_resource(options) resource = resource_object_for(options) relationships = resource_relationships(options) @@ -65,15 +78,11 @@ def include_meta(json) end def resource_object_for(options) - if options[:batch_cache].present? - cache_key = CachedSerializer.new(serializer).cache_key - - value = options[:batch_cache][cache_key] - - return value if value.present? - end + cached_serializer = CachedSerializer.new(serializer) + batch_cached_attributes = batch_cache_for(cached_serializer, options) + return batch_cached_attributes if batch_cached_attributes.present? - cache_check(serializer) do + cached_serializer.cache_check(self) do serializer.attributes(options[:fields]) end end diff --git a/lib/active_model/serializer/adapter/cached_serializer.rb b/lib/active_model/serializer/adapter/cached_serializer.rb index 3b976471a..358942bf7 100644 --- a/lib/active_model/serializer/adapter/cached_serializer.rb +++ b/lib/active_model/serializer/adapter/cached_serializer.rb @@ -28,10 +28,12 @@ def fragment_cached? end def cache_key + return @cache_key if defined?(@cache_key) + parts = [] parts << object_cache_key parts << @klass._cache_digest unless @klass._cache_options && @klass._cache_options[:skip_digest] - parts.join('/') + @cache_key = parts.join('/') end def object_cache_key @@ -40,7 +42,10 @@ def object_cache_key (@klass._cache_key) ? "#{@klass._cache_key}/#{@cached_serializer.object.id}-#{object_time_safe}" : @cached_serializer.object.cache_key end - # collection_serializer with the include_tree + # find all cache_key for the collection_serializer + # @param collection_serializer + # @param include_tree + # @return [Array] all cache_key of collection_serializer def self.object_cache_keys(serializers, include_tree) cache_keys = [] @@ -58,9 +63,10 @@ def self.object_cache_keys(serializers, include_tree) end end - cache_keys.compact + cache_keys.compact.uniq end + # @return [String, nil] the cache_key of the serializer or nil def self.object_cache_key(serializer) return unless serializer.present? && serializer.object.present? diff --git a/test/serializers/cache_test.rb b/test/serializers/cache_test.rb index 0cd9a6741..e0fae5862 100644 --- a/test/serializers/cache_test.rb +++ b/test/serializers/cache_test.rb @@ -155,12 +155,30 @@ def test_object_cache_keys actual = Serializer::Adapter::CachedSerializer.object_cache_keys(serializer, include_tree) - assert_equal actual.size, 6 + assert_equal actual.size, 3 assert actual.any? { |key| key == 'comment/1' } assert actual.any? { |key| key =~ %r{post/post-\d+} } assert actual.any? { |key| key =~ %r{writer/author-\d+} } end + def test_batch_cache + serializer = CollectionSerializer.new([@comment, @comment]) + + Timecop.freeze(Time.now) do + render_object_with_cache(@comment) + + batch_cache = ActiveModel::Serializer::Adapter::Attributes.new(serializer).send(:batch_cache, {})[:batch_cache] + + assert_equal batch_cache[@comment.cache_key], Comment.new(id: 1, body: 'ZOMG A COMMENT').attributes + assert_equal batch_cache[@comment.post.cache_key], Post.new(id: 'post', title: 'New Post', body: 'Body').attributes + + writer = @comment.post.blog.writer + writer_cache_key = "writer/#{writer.id}-#{writer.updated_at.strftime("%Y%m%d%H%M%S%9N")}" + + assert_equal batch_cache[writer_cache_key], Author.new(id: 'author', name: 'Joao M. D. Moura').attributes + end + end + def test_serializer_file_path_on_nix path = '/Users/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb' caller_line = "#{path}:1:in `'"