1
- # TODO(BF): refactor file to be smaller
2
- # rubocop:disable Metrics/ModuleLength
3
1
module ActiveModel
4
2
class Serializer
5
3
UndefinedCacheKey = Class . new ( StandardError )
@@ -9,10 +7,9 @@ module Caching
9
7
included do
10
8
with_options instance_writer : false , instance_reader : false do |serializer |
11
9
serializer . class_attribute :_cache # @api private : the cache store
12
- serializer . class_attribute :_fragmented # @api private : @see ::fragmented
13
10
serializer . class_attribute :_cache_key # @api private : when present, is first item in cache_key. Ignored if the serializable object defines #cache_key.
14
- serializer . class_attribute :_cache_only # @api private : when fragment caching, whitelists cached_attributes . Cannot combine with except
15
- serializer . class_attribute :_cache_except # @api private : when fragment caching, blacklists cached_attributes . Cannot combine with only
11
+ serializer . class_attribute :_cache_only # @api private : when fragment caching, whitelists fetch_attributes . Cannot combine with except
12
+ serializer . class_attribute :_cache_except # @api private : when fragment caching, blacklists fetch_attributes . Cannot combine with only
16
13
serializer . class_attribute :_cache_options # @api private : used by CachedSerializer, passed to _cache.fetch
17
14
# _cache_options include:
18
15
# expires_in
@@ -21,7 +18,7 @@ module Caching
21
18
# race_condition_ttl
22
19
# Passed to ::_cache as
23
20
# serializer.cache_store.fetch(cache_key, @klass._cache_options)
24
- # Passed as second argument to serializer.cache_store.fetch(cache_key, self.class ._cache_options)
21
+ # Passed as second argument to serializer.cache_store.fetch(cache_key, serializer_class ._cache_options)
25
22
serializer . class_attribute :_cache_digest_file_path # @api private : Derived at inheritance
26
23
end
27
24
end
@@ -71,19 +68,15 @@ def _skip_digest?
71
68
_cache_options && _cache_options [ :skip_digest ]
72
69
end
73
70
74
- def cached_attributes
75
- _cache_only ? _cache_only : _attributes - _cache_except
76
- end
77
-
78
- def non_cached_attributes
79
- _attributes - cached_attributes
80
- end
81
-
82
- # @api private
83
- # Used by FragmentCache on the CachedSerializer
84
- # to call attribute methods on the fragmented cached serializer.
85
- def fragmented ( serializer )
86
- self . _fragmented = serializer
71
+ def fragmented_attributes
72
+ cached = _cache_only ? _cache_only : _attributes - _cache_except
73
+ cached = cached . map! { |field | _attributes_keys . fetch ( field , field ) }
74
+ non_cached = _attributes - cached
75
+ non_cached = non_cached . map! { |field | _attributes_keys . fetch ( field , field ) }
76
+ {
77
+ cached : cached ,
78
+ non_cached : non_cached
79
+ }
87
80
end
88
81
89
82
# Enables a serializer to be automatically cached
@@ -208,118 +201,65 @@ def object_cache_key(serializer, adapter_instance)
208
201
end
209
202
end
210
203
211
- def cached_attributes ( fields , cached_attributes , adapter_instance )
212
- if self . class . cache_enabled?
204
+ ### INSTANCE METHODS
205
+ def fetch_attributes ( fields , cached_attributes , adapter_instance )
206
+ if serializer_class . cache_enabled?
213
207
key = cache_key ( adapter_instance )
214
208
cached_attributes . fetch ( key ) do
215
- cache_check ( adapter_instance ) do
216
- attributes ( fields )
209
+ serializer_class . cache_store . fetch ( key , serializer_class . _cache_options ) do
210
+ attributes ( fields , true )
217
211
end
218
212
end
213
+ elsif serializer_class . fragment_cache_enabled?
214
+ fetch_attributes_fragment ( adapter_instance )
219
215
else
220
- cache_check ( adapter_instance ) do
221
- attributes ( fields )
222
- end
216
+ attributes ( fields , true )
223
217
end
224
218
end
225
219
226
- def cache_check ( adapter_instance )
227
- if self . class . cache_enabled?
228
- self . class . cache_store . fetch ( cache_key ( adapter_instance ) , self . class . _cache_options ) do
220
+ def fetch ( adapter_instance , cache_options = serializer_class . _cache_options )
221
+ if serializer_class . cache_store
222
+ serializer_class . cache_store . fetch ( cache_key ( adapter_instance ) , cache_options ) do
229
223
yield
230
224
end
231
- elsif self . class . fragment_cache_enabled?
232
- fetch_fragment_cache ( adapter_instance )
233
225
else
234
226
yield
235
227
end
236
228
end
237
229
238
- # 1. Create a CachedSerializer and NonCachedSerializer from the serializer class
239
- # 2. Serialize the above two with the given adapter
240
- # 3. Pass their serializations to the adapter +::fragment_cache+
241
- #
242
- # It will split the serializer into two, one that will be cached and one that will not
243
- #
244
- # Given a resource name
245
- # 1. Dynamically creates a CachedSerializer and NonCachedSerializer
246
- # for a given class 'name'
247
- # 2. Call
248
- # CachedSerializer.cache(serializer._cache_options)
249
- # CachedSerializer.fragmented(serializer)
250
- # NonCachedSerializer.cache(serializer._cache_options)
251
- # 3. Build a hash keyed to the +cached+ and +non_cached+ serializers
252
- # 4. Call +cached_attributes+ on the serializer class and the above hash
253
- # 5. Return the hash
254
- #
255
- # @example
256
- # When +name+ is <tt>User::Admin</tt>
257
- # creates the Serializer classes (if they don't exist).
258
- # CachedUser_AdminSerializer
259
- # NonCachedUser_AdminSerializer
260
- #
261
- # Given a hash of its cached and non-cached serializers
262
- # 1. Determine cached attributes from serializer class options
263
- # 2. Add cached attributes to cached Serializer
264
- # 3. Add non-cached attributes to non-cached Serializer
265
- def fetch_fragment_cache ( adapter_instance )
266
- serializer_class_name = self . class . name . gsub ( '::' . freeze , '_' . freeze )
267
- self . class . _cache_options ||= { }
268
- self . class . _cache_options [ :key ] = self . class . _cache_key if self . class . _cache_key
269
-
270
- cached_serializer = _get_or_create_fragment_cached_serializer ( serializer_class_name )
271
- cached_hash = ActiveModelSerializers ::SerializableResource . new (
272
- object ,
273
- serializer : cached_serializer ,
274
- adapter : adapter_instance . class
275
- ) . serializable_hash
276
-
277
- non_cached_serializer = _get_or_create_fragment_non_cached_serializer ( serializer_class_name )
278
- non_cached_hash = ActiveModelSerializers ::SerializableResource . new (
279
- object ,
280
- serializer : non_cached_serializer ,
281
- adapter : adapter_instance . class
282
- ) . serializable_hash
230
+ # 1. Determine cached fields from serializer class options
231
+ # 2. Get non_cached_fields and fetch cache_fields
232
+ # 3. Merge the two hashes using adapter_instance#fragment_cache
233
+ def fetch_attributes_fragment ( adapter_instance )
234
+ serializer_class . _cache_options ||= { }
235
+ serializer_class . _cache_options [ :key ] = serializer_class . _cache_key if serializer_class . _cache_key
236
+ fields = serializer_class . fragmented_attributes
237
+
238
+ non_cached_fields = fields [ :non_cached ] . dup
239
+ non_cached_hash = attributes ( non_cached_fields , true )
240
+ include_directive = JSONAPI ::IncludeDirective . new ( non_cached_fields - non_cached_hash . keys )
241
+ non_cached_hash . merge! resource_relationships ( { } , { include_directive : include_directive } , adapter_instance )
242
+
243
+ cached_fields = fields [ :cached ] . dup
244
+ key = cache_key ( adapter_instance )
245
+ cached_hash =
246
+ serializer_class . cache_store . fetch ( key , serializer_class . _cache_options ) do
247
+ hash = attributes ( cached_fields , true )
248
+ include_directive = JSONAPI ::IncludeDirective . new ( cached_fields - hash . keys )
249
+ hash . merge! resource_relationships ( { } , { include_directive : include_directive } , adapter_instance )
250
+ end
283
251
284
252
# Merge both results
285
253
adapter_instance . fragment_cache ( cached_hash , non_cached_hash )
286
254
end
287
255
288
- def _get_or_create_fragment_cached_serializer ( serializer_class_name )
289
- cached_serializer = _get_or_create_fragment_serializer "Cached#{ serializer_class_name } "
290
- cached_serializer . cache ( self . class . _cache_options )
291
- cached_serializer . type ( self . class . _type )
292
- cached_serializer . fragmented ( self )
293
- self . class . cached_attributes . each do |attribute |
294
- options = self . class . _attributes_keys [ attribute ] || { }
295
- cached_serializer . attribute ( attribute , options )
296
- end
297
- cached_serializer
298
- end
299
-
300
- def _get_or_create_fragment_non_cached_serializer ( serializer_class_name )
301
- non_cached_serializer = _get_or_create_fragment_serializer "NonCached#{ serializer_class_name } "
302
- non_cached_serializer . type ( self . class . _type )
303
- non_cached_serializer . fragmented ( self )
304
- self . class . non_cached_attributes . each do |attribute |
305
- options = self . class . _attributes_keys [ attribute ] || { }
306
- non_cached_serializer . attribute ( attribute , options )
307
- end
308
- non_cached_serializer
309
- end
310
-
311
- def _get_or_create_fragment_serializer ( name )
312
- return Object . const_get ( name ) if Object . const_defined? ( name )
313
- Object . const_set ( name , Class . new ( ActiveModel ::Serializer ) )
314
- end
315
-
316
256
def cache_key ( adapter_instance )
317
257
return @cache_key if defined? ( @cache_key )
318
258
319
259
parts = [ ]
320
260
parts << object_cache_key
321
261
parts << adapter_instance . cache_key
322
- parts << self . class . _cache_digest unless self . class . _skip_digest?
262
+ parts << serializer_class . _cache_digest unless serializer_class . _skip_digest?
323
263
@cache_key = parts . join ( '/' )
324
264
end
325
265
@@ -328,15 +268,18 @@ def cache_key(adapter_instance)
328
268
def object_cache_key
329
269
if object . respond_to? ( :cache_key )
330
270
object . cache_key
331
- elsif ( serializer_cache_key = ( self . class . _cache_key || self . class . _cache_options [ :key ] ) )
271
+ elsif ( serializer_cache_key = ( serializer_class . _cache_key || serializer_class . _cache_options [ :key ] ) )
332
272
object_time_safe = object . updated_at
333
273
object_time_safe = object_time_safe . strftime ( '%Y%m%d%H%M%S%9N' ) if object_time_safe . respond_to? ( :strftime )
334
274
"#{ serializer_cache_key } /#{ object . id } -#{ object_time_safe } "
335
275
else
336
- fail UndefinedCacheKey , "#{ object . class } must define #cache_key, or the 'key:' option must be passed into '#{ self . class } .cache'"
276
+ fail UndefinedCacheKey , "#{ object . class } must define #cache_key, or the 'key:' option must be passed into '#{ serializer_class } .cache'"
337
277
end
338
278
end
279
+
280
+ def serializer_class
281
+ @serializer_class ||= self . class
282
+ end
339
283
end
340
284
end
341
285
end
342
- # rubocop:enable Metrics/ModuleLength
0 commit comments