Skip to content

Commit 82da04d

Browse files
author
Yohan Robert
committed
Merge pull request #1478 from bf4/caching_fix
[FIX] Serializers can now be defined *before* Rails initializes and cache store will be correctly set
2 parents 912daa9 + dd60a37 commit 82da04d

File tree

5 files changed

+234
-4
lines changed

5 files changed

+234
-4
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ Features:
2424
- [#1340](https://github.com/rails-api/active_model_serializers/pull/1340) Add support for resource-level meta. (@beauby)
2525

2626
Fixes:
27+
- [#1478](https://github.com/rails-api/active_model_serializers/pull/1478) Cache store will now be correctly set when serializers are
28+
loaded *before* Rails initializes. (@bf4)
2729
- [#1570](https://github.com/rails-api/active_model_serializers/pull/1570) Fixed pagination issue with last page size. (@bmorrall)
2830
- [#1516](https://github.com/rails-api/active_model_serializers/pull/1516) No longer return a nil href when only
2931
adding meta to a relationship link. (@groyoh)

lib/active_model/serializer/caching.rb

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module Caching
55

66
included do
77
with_options instance_writer: false, instance_reader: false do |serializer|
8-
serializer.class_attribute :_cache # @api private : the cache object
8+
serializer.class_attribute :_cache # @api private : the cache store
99
serializer.class_attribute :_fragmented # @api private : @see ::fragmented
1010
serializer.class_attribute :_cache_key # @api private : when present, is first item in cache_key
1111
serializer.class_attribute :_cache_only # @api private : when fragment caching, whitelists cached_attributes. Cannot combine with except
@@ -71,6 +71,7 @@ def fragmented(serializer)
7171
# when Rails.configuration.action_controller.perform_caching
7272
#
7373
# @params options [Hash] with valid keys:
74+
# cache_store : @see ::_cache
7475
# key : @see ::_cache_key
7576
# only : @see ::_cache_only
7677
# except : @see ::_cache_except
@@ -88,12 +89,58 @@ def fragmented(serializer)
8889
# @todo require less code comments. See
8990
# https://github.com/rails-api/active_model_serializers/pull/1249#issuecomment-146567837
9091
def cache(options = {})
91-
self._cache = ActiveModelSerializers.config.cache_store if ActiveModelSerializers.config.perform_caching
92+
self._cache =
93+
options.delete(:cache_store) ||
94+
ActiveModelSerializers.config.cache_store ||
95+
ActiveSupport::Cache.lookup_store(:null_store)
9296
self._cache_key = options.delete(:key)
9397
self._cache_only = options.delete(:only)
9498
self._cache_except = options.delete(:except)
9599
self._cache_options = options.empty? ? nil : options
96100
end
101+
102+
# Value is from ActiveModelSerializers.config.perform_caching. Is used to
103+
# globally enable or disable all serializer caching, just like
104+
# Rails.configuration.action_controller.perform_caching, which is its
105+
# default value in a Rails application.
106+
# @return [true, false]
107+
# Memoizes value of config first time it is called with a non-nil value.
108+
# rubocop:disable Style/ClassVars
109+
def perform_caching
110+
return @@perform_caching if defined?(@@perform_caching) && !@@perform_caching.nil?
111+
@@perform_caching = ActiveModelSerializers.config.perform_caching
112+
end
113+
alias perform_caching? perform_caching
114+
# rubocop:enable Style/ClassVars
115+
116+
# The canonical method for getting the cache store for the serializer.
117+
#
118+
# @return [nil] when _cache is not set (i.e. when `cache` has not been called)
119+
# @return [._cache] when _cache is not the NullStore
120+
# @return [ActiveModelSerializers.config.cache_store] when _cache is the NullStore.
121+
# This is so we can use `cache` being called to mean the serializer should be cached
122+
# even if ActiveModelSerializers.config.cache_store has not yet been set.
123+
# That means that when _cache is the NullStore and ActiveModelSerializers.config.cache_store
124+
# is configured, `cache_store` becomes `ActiveModelSerializers.config.cache_store`.
125+
# @return [nil] when _cache is the NullStore and ActiveModelSerializers.config.cache_store is nil.
126+
def cache_store
127+
return nil if _cache.nil?
128+
return _cache if _cache.class != ActiveSupport::Cache::NullStore
129+
if ActiveModelSerializers.config.cache_store
130+
self._cache = ActiveModelSerializers.config.cache_store
131+
else
132+
nil
133+
end
134+
end
135+
136+
def cache_enabled?
137+
perform_caching? && cache_store && !_cache_only && !_cache_except
138+
end
139+
140+
def fragment_cache_enabled?
141+
perform_caching? && cache_store &&
142+
(_cache_only && !_cache_except || !_cache_only && _cache_except)
143+
end
97144
end
98145
end
99146
end

lib/active_model_serializers/cached_serializer.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ def cache_check(adapter_instance)
1818
end
1919

2020
def cached?
21-
@klass._cache && !@klass._cache_only && !@klass._cache_except
21+
@klass.cache_enabled?
2222
end
2323

2424
def fragment_cached?
25-
@klass._cache && (@klass._cache_only && !@klass._cache_except || !@klass._cache_only && @klass._cache_except)
25+
@klass.fragment_cache_enabled?
2626
end
2727

2828
def cache_key

test/cache_test.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ def setup
3434
@blog_serializer = BlogSerializer.new(@blog)
3535
end
3636

37+
def test_explicit_cache_store
38+
default_store = Class.new(ActiveModel::Serializer) do
39+
cache
40+
end
41+
explicit_store = Class.new(ActiveModel::Serializer) do
42+
cache cache_store: ActiveSupport::Cache::FileStore
43+
end
44+
45+
assert ActiveSupport::Cache::MemoryStore, ActiveModelSerializers.config.cache_store
46+
assert ActiveSupport::Cache::MemoryStore, default_store.cache_store
47+
assert ActiveSupport::Cache::FileStore, explicit_store.cache_store
48+
end
49+
3750
def test_inherited_cache_configuration
3851
inherited_serializer = Class.new(PostSerializer)
3952

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# Execute this test in isolation
2+
require 'support/isolated_unit'
3+
4+
class CachingConfigurationTest < ActiveSupport::TestCase
5+
include ActiveSupport::Testing::Isolation
6+
7+
setup do
8+
require 'rails'
9+
# AMS needs to be required before Rails.application is initialized for
10+
# Railtie's to fire in Rails.application.initialize!
11+
# (and make_basic_app initializes the app)
12+
require 'active_model_serializers'
13+
# Create serializers before Rails.application.initialize!
14+
# To ensure we're testing that the cache settings depend on
15+
# the Railtie firing, not on the ActionController being loaded.
16+
create_serializers
17+
end
18+
19+
def create_serializers
20+
@cached_serializer = Class.new(ActiveModel::Serializer) do
21+
cache skip_digest: true
22+
attributes :id, :name, :title
23+
end
24+
@fragment_cached_serializer = Class.new(ActiveModel::Serializer) do
25+
cache only: :id
26+
attributes :id, :name, :title
27+
end
28+
@non_cached_serializer = Class.new(ActiveModel::Serializer) do
29+
attributes :id, :name, :title
30+
end
31+
end
32+
33+
class PerformCachingTrue < CachingConfigurationTest
34+
setup do
35+
# Let's make that Rails app and initialize it!
36+
make_basic_app do |app|
37+
app.config.action_controller.perform_caching = true
38+
app.config.action_controller.cache_store = ActiveSupport::Cache.lookup_store(:memory_store)
39+
end
40+
end
41+
42+
test 'it sets perform_caching to true on AMS.config and serializers' do
43+
assert Rails.configuration.action_controller.perform_caching
44+
assert ActiveModelSerializers.config.perform_caching
45+
assert ActiveModel::Serializer.perform_caching?
46+
assert @cached_serializer.perform_caching?
47+
assert @non_cached_serializer.perform_caching?
48+
assert @fragment_cached_serializer.perform_caching?
49+
end
50+
51+
test 'it sets the AMS.config.cache_store to the controller cache_store' do
52+
assert_equal controller_cache_store, ActiveSupport::Cache::MemoryStore
53+
assert_equal controller_cache_store, ActiveModelSerializers.config.cache_store.class
54+
end
55+
56+
test 'it sets the cached serializer cache_store to the ActionController::Base.cache_store' do
57+
assert_equal ActiveSupport::Cache::NullStore, @cached_serializer._cache.class
58+
assert_equal controller_cache_store, @cached_serializer.cache_store.class
59+
assert_equal ActiveSupport::Cache::MemoryStore, @cached_serializer._cache.class
60+
end
61+
62+
test 'the cached serializer has cache_enabled?' do
63+
assert @cached_serializer.cache_enabled?
64+
end
65+
66+
test 'the cached serializer does not have fragment_cache_enabled?' do
67+
refute @cached_serializer.fragment_cache_enabled?
68+
end
69+
70+
test 'the non-cached serializer cache_store is nil' do
71+
assert_equal nil, @non_cached_serializer._cache
72+
assert_equal nil, @non_cached_serializer.cache_store
73+
assert_equal nil, @non_cached_serializer._cache
74+
end
75+
76+
test 'the non-cached serializer does not have cache_enabled?' do
77+
refute @non_cached_serializer.cache_enabled?
78+
end
79+
80+
test 'the non-cached serializer does not have fragment_cache_enabled?' do
81+
refute @non_cached_serializer.fragment_cache_enabled?
82+
end
83+
84+
test 'it sets the fragment cached serializer cache_store to the ActionController::Base.cache_store' do
85+
assert_equal ActiveSupport::Cache::NullStore, @fragment_cached_serializer._cache.class
86+
assert_equal controller_cache_store, @fragment_cached_serializer.cache_store.class
87+
assert_equal ActiveSupport::Cache::MemoryStore, @fragment_cached_serializer._cache.class
88+
end
89+
90+
test 'the fragment cached serializer does not have cache_enabled?' do
91+
refute @fragment_cached_serializer.cache_enabled?
92+
end
93+
94+
test 'the fragment cached serializer has fragment_cache_enabled?' do
95+
assert @fragment_cached_serializer.fragment_cache_enabled?
96+
end
97+
end
98+
99+
class PerformCachingFalse < CachingConfigurationTest
100+
setup do
101+
# Let's make that Rails app and initialize it!
102+
make_basic_app do |app|
103+
app.config.action_controller.perform_caching = false
104+
app.config.action_controller.cache_store = ActiveSupport::Cache.lookup_store(:memory_store)
105+
end
106+
end
107+
108+
test 'it sets perform_caching to false on AMS.config and serializers' do
109+
refute Rails.configuration.action_controller.perform_caching
110+
refute ActiveModelSerializers.config.perform_caching
111+
refute ActiveModel::Serializer.perform_caching?
112+
refute @cached_serializer.perform_caching?
113+
refute @non_cached_serializer.perform_caching?
114+
refute @fragment_cached_serializer.perform_caching?
115+
end
116+
117+
test 'it sets the AMS.config.cache_store to the controller cache_store' do
118+
assert_equal controller_cache_store, ActiveSupport::Cache::MemoryStore
119+
assert_equal controller_cache_store, ActiveModelSerializers.config.cache_store.class
120+
end
121+
122+
test 'it sets the cached serializer cache_store to the ActionController::Base.cache_store' do
123+
assert_equal ActiveSupport::Cache::NullStore, @cached_serializer._cache.class
124+
assert_equal controller_cache_store, @cached_serializer.cache_store.class
125+
assert_equal ActiveSupport::Cache::MemoryStore, @cached_serializer._cache.class
126+
end
127+
128+
test 'the cached serializer does not have cache_enabled?' do
129+
refute @cached_serializer.cache_enabled?
130+
end
131+
132+
test 'the cached serializer does not have fragment_cache_enabled?' do
133+
refute @cached_serializer.fragment_cache_enabled?
134+
end
135+
136+
test 'the non-cached serializer cache_store is nil' do
137+
assert_equal nil, @non_cached_serializer._cache
138+
assert_equal nil, @non_cached_serializer.cache_store
139+
assert_equal nil, @non_cached_serializer._cache
140+
end
141+
142+
test 'the non-cached serializer does not have cache_enabled?' do
143+
refute @non_cached_serializer.cache_enabled?
144+
end
145+
146+
test 'the non-cached serializer does not have fragment_cache_enabled?' do
147+
refute @non_cached_serializer.fragment_cache_enabled?
148+
end
149+
150+
test 'it sets the fragment cached serializer cache_store to the ActionController::Base.cache_store' do
151+
assert_equal ActiveSupport::Cache::NullStore, @fragment_cached_serializer._cache.class
152+
assert_equal controller_cache_store, @fragment_cached_serializer.cache_store.class
153+
assert_equal ActiveSupport::Cache::MemoryStore, @fragment_cached_serializer._cache.class
154+
end
155+
156+
test 'the fragment cached serializer does not have cache_enabled?' do
157+
refute @fragment_cached_serializer.cache_enabled?
158+
end
159+
160+
test 'the fragment cached serializer does not have fragment_cache_enabled?' do
161+
refute @fragment_cached_serializer.fragment_cache_enabled?
162+
end
163+
end
164+
165+
def controller_cache_store
166+
ActionController::Base.cache_store.class
167+
end
168+
end

0 commit comments

Comments
 (0)