From 7a62d3177754cfc58dd3b5ddcf7cc3448799b3a9 Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Wed, 6 May 2015 00:27:19 +0300 Subject: [PATCH] Added serializer file digest to the cache_key Fixes #901 --- lib/active_model/serializer.rb | 3 +++ lib/active_model/serializer/adapter.rb | 7 +++++++ test/fixtures/poro.rb | 21 ++++++++++++++------- test/serializers/cache_test.rb | 18 ++++++++++++++---- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index 2abeebce7..317114328 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -19,6 +19,7 @@ class << self attr_accessor :_cache_only attr_accessor :_cache_except attr_accessor :_cache_options + attr_accessor :_cache_digest end def self.inherited(base) @@ -26,6 +27,8 @@ def self.inherited(base) base._attributes_keys = {} base._associations = {} base._urls = [] + serializer_file = File.open(caller.first[/^[^:]+/]) + base._cache_digest = Digest::MD5.hexdigest(serializer_file.read) end def self.attributes(*attrs) diff --git a/lib/active_model/serializer/adapter.rb b/lib/active_model/serializer/adapter.rb index a1e4c39f3..6b2c1225d 100644 --- a/lib/active_model/serializer/adapter.rb +++ b/lib/active_model/serializer/adapter.rb @@ -63,6 +63,13 @@ def is_fragment_cached? end def cache_key + parts = [] + parts << object_cache_key + parts << @klass._cache_digest unless @klass._cache_options && @klass._cache_options[:skip_digest] + parts.join("/") + end + + def object_cache_key (@klass._cache_key) ? "#{@klass._cache_key}/#{@cached_serializer.object.id}-#{@cached_serializer.object.updated_at}" : @cached_serializer.object.cache_key end diff --git a/test/fixtures/poro.rb b/test/fixtures/poro.rb index b66395c37..10f692083 100644 --- a/test/fixtures/poro.rb +++ b/test/fixtures/poro.rb @@ -1,4 +1,6 @@ class Model + FILE_DIGEST = Digest::MD5.hexdigest(File.open(__FILE__).read) + def initialize(hash={}) @attributes = hash end @@ -7,6 +9,10 @@ def cache_key "#{self.class.name.downcase}/#{self.id}-#{self.updated_at}" end + def cache_key_with_digest + "#{cache_key}/#{FILE_DIGEST}" + end + def updated_at @attributes[:updated_at] ||= DateTime.now.to_time.to_i end @@ -64,7 +70,7 @@ class ProfilePreviewSerializer < ActiveModel::Serializer Bio = Class.new(Model) Blog = Class.new(Model) Role = Class.new(Model) -User = Class.new(Model) +User = Class.new(Model) Location = Class.new(Model) Place = Class.new(Model) @@ -72,7 +78,7 @@ module Spam; end Spam::UnrelatedLink = Class.new(Model) PostSerializer = Class.new(ActiveModel::Serializer) do - cache key:'post', expires_in: 0.1 + cache key:'post', expires_in: 0.1, skip_digest: true attributes :id, :title, :body has_many :comments @@ -99,7 +105,7 @@ def self.root_name end CommentSerializer = Class.new(ActiveModel::Serializer) do - cache expires_in: 1.day + cache expires_in: 1.day, skip_digest: true attributes :id, :body belongs_to :post @@ -111,7 +117,7 @@ def custom_options end AuthorSerializer = Class.new(ActiveModel::Serializer) do - cache key:'writer' + cache key:'writer', skip_digest: true attributes :id, :name has_many :posts, embed: :ids @@ -120,7 +126,7 @@ def custom_options end RoleSerializer = Class.new(ActiveModel::Serializer) do - cache only: [:name] + cache only: [:name], skip_digest: true attributes :id, :name, :description, :slug def slug @@ -137,7 +143,7 @@ def slug end LocationSerializer = Class.new(ActiveModel::Serializer) do - cache only: [:place] + cache only: [:place], skip_digest: true attributes :id, :lat, :lng belongs_to :place @@ -154,13 +160,14 @@ def place end BioSerializer = Class.new(ActiveModel::Serializer) do - cache except: [:content] + cache except: [:content], skip_digest: true attributes :id, :content, :rating belongs_to :author end BlogSerializer = Class.new(ActiveModel::Serializer) do + cache key: 'blog' attributes :id, :name belongs_to :writer diff --git a/test/serializers/cache_test.rb b/test/serializers/cache_test.rb index 89c62a212..eb0f9e0d4 100644 --- a/test/serializers/cache_test.rb +++ b/test/serializers/cache_test.rb @@ -5,10 +5,10 @@ class CacheTest < Minitest::Test def setup ActionController::Base.cache_store.clear @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') - @blog = Blog.new(id: 999, name: "Custom blog") @post = Post.new(title: 'New Post', body: 'Body') @bio = Bio.new(id: 1, content: 'AMS Contributor') @author = Author.new(name: 'Joao M. D. Moura') + @blog = Blog.new(id: 999, name: "Custom blog", writer: @author, articles: []) @role = Role.new(name: 'Great Author') @location = Location.new(lat: '-23.550520', lng: '-46.633309') @place = Place.new(name: 'Amazing Place') @@ -30,6 +30,7 @@ def setup @post_serializer = PostSerializer.new(@post) @author_serializer = AuthorSerializer.new(@author) @comment_serializer = CommentSerializer.new(@comment) + @blog_serializer = BlogSerializer.new(@blog) end def test_cache_definition @@ -56,9 +57,9 @@ def test_default_cache_key_fallback end def test_cache_options_definition - assert_equal({expires_in: 0.1}, @post_serializer.class._cache_options) - assert_equal(nil, @author_serializer.class._cache_options) - assert_equal({expires_in: 1.day}, @comment_serializer.class._cache_options) + assert_equal({expires_in: 0.1, skip_digest: true}, @post_serializer.class._cache_options) + assert_equal(nil, @blog_serializer.class._cache_options) + assert_equal({expires_in: 1.day, skip_digest: true}, @comment_serializer.class._cache_options) end def test_fragment_cache_definition @@ -115,6 +116,15 @@ def test_fragment_fetch_with_virtual_associations assert_equal({place: 'Nowhere'}, ActionController::Base.cache_store.fetch(@location.cache_key)) end + def test_uses_file_digest_in_cahe_key + blog = render_object_with_cache(@blog) + assert_equal(@blog_serializer.attributes, ActionController::Base.cache_store.fetch(@blog.cache_key_with_digest)) + end + + def _cache_digest_definition + assert_equal(::Model::FILE_DIGEST, @post_serializer.class._cache_digest) + end + private def render_object_with_cache(obj) serializer_class = ActiveModel::Serializer.serializer_for(obj)