1- # TODO(BF): refactor file to be smaller
2- # rubocop:disable Metrics/ModuleLength
31module ActiveModel
42 class Serializer
53 UndefinedCacheKey = Class . new ( StandardError )
@@ -9,10 +7,9 @@ module Caching
97 included do
108 with_options instance_writer : false , instance_reader : false do |serializer |
119 serializer . class_attribute :_cache # @api private : the cache store
12- serializer . class_attribute :_fragmented # @api private : @see ::fragmented
1310 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
1613 serializer . class_attribute :_cache_options # @api private : used by CachedSerializer, passed to _cache.fetch
1714 # _cache_options include:
1815 # expires_in
@@ -21,7 +18,7 @@ module Caching
2118 # race_condition_ttl
2219 # Passed to ::_cache as
2320 # 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)
2522 serializer . class_attribute :_cache_digest_file_path # @api private : Derived at inheritance
2623 end
2724 end
@@ -71,19 +68,15 @@ def _skip_digest?
7168 _cache_options && _cache_options [ :skip_digest ]
7269 end
7370
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+ }
8780 end
8881
8982 # Enables a serializer to be automatically cached
@@ -208,118 +201,65 @@ def object_cache_key(serializer, adapter_instance)
208201 end
209202 end
210203
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?
213207 key = cache_key ( adapter_instance )
214208 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 )
217211 end
218212 end
213+ elsif serializer_class . fragment_cache_enabled?
214+ fetch_attributes_fragment ( adapter_instance )
219215 else
220- cache_check ( adapter_instance ) do
221- attributes ( fields )
222- end
216+ attributes ( fields , true )
223217 end
224218 end
225219
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
229223 yield
230224 end
231- elsif self . class . fragment_cache_enabled?
232- fetch_fragment_cache ( adapter_instance )
233225 else
234226 yield
235227 end
236228 end
237229
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
283251
284252 # Merge both results
285253 adapter_instance . fragment_cache ( cached_hash , non_cached_hash )
286254 end
287255
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-
316256 def cache_key ( adapter_instance )
317257 return @cache_key if defined? ( @cache_key )
318258
319259 parts = [ ]
320260 parts << object_cache_key
321261 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?
323263 @cache_key = parts . join ( '/' )
324264 end
325265
@@ -328,15 +268,18 @@ def cache_key(adapter_instance)
328268 def object_cache_key
329269 if object . respond_to? ( :cache_key )
330270 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 ] ) )
332272 object_time_safe = object . updated_at
333273 object_time_safe = object_time_safe . strftime ( '%Y%m%d%H%M%S%9N' ) if object_time_safe . respond_to? ( :strftime )
334274 "#{ serializer_cache_key } /#{ object . id } -#{ object_time_safe } "
335275 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'"
337277 end
338278 end
279+
280+ def serializer_class
281+ @serializer_class ||= self . class
282+ end
339283 end
340284 end
341285end
342- # rubocop:enable Metrics/ModuleLength
0 commit comments