moduleActiveModelclassSerializerUndefinedCacheKey=Class.new(StandardError)moduleCachingextendActiveSupport::Concernincludeddowith_optionsinstance_writer: false,instance_reader: falsedo|serializer|serializer.class_attribute:_cache# @api private : the cache storeserializer.class_attribute:_cache_key# @api private : when present, is first item in cache_key. Ignored if the serializable object defines #cache_key.serializer.class_attribute:_cache_only# @api private : when fragment caching, whitelists fetch_attributes. Cannot combine with exceptserializer.class_attribute:_cache_except# @api private : when fragment caching, blacklists fetch_attributes. Cannot combine with onlyserializer.class_attribute:_cache_options# @api private : used by CachedSerializer, passed to _cache.fetch# _cache_options include:# expires_in# compress# force# race_condition_ttl# Passed to ::_cache as# serializer.cache_store.fetch(cache_key, @klass._cache_options)# Passed as second argument to serializer.cache_store.fetch(cache_key, serializer_class._cache_options)serializer.class_attribute:_cache_digest_file_path# @api private : Derived at inheritanceendend# Matches# "c:/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb:1:in `<top (required)>'"# AND# "/c/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb:1:in `<top (required)>'"# AS# c/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rbCALLER_FILE=/
\A # start of string
.+ # file path (one or more characters)
(?= # stop previous match when
:\d+ # a colon is followed by one or more digits
:in # followed by a colon followed by in
)
/xmoduleClassMethodsdefinherited(base)caller_line=caller[1]base._cache_digest_file_path=caller_linesuperenddef_cache_digestreturn@_cache_digestifdefined?(@_cache_digest)@_cache_digest=digest_caller_file(_cache_digest_file_path)end# Hashes contents of file for +_cache_digest+defdigest_caller_file(caller_line)serializer_file_path=caller_line[CALLER_FILE]serializer_file_contents=IO.read(serializer_file_path)Digest::MD5.hexdigest(serializer_file_contents)rescueTypeError,Errno::ENOENTwarn<<-EOF.strip_heredoc
Cannot digest non-existent file: '#{caller_line}'.
Please set `::_cache_digest` of the serializer
if you'd like to cache it.
EOF''.freezeenddef_skip_digest?_cache_options&&_cache_options[:skip_digest]end# @api private# maps attribute value to explicit key name# @see Serializer::attribute# @see Serializer::fragmented_attributesdef_attributes_keys_attributes_data.each_with_object({})do|(key,attr),hash|nextifkey==attr.namehash[attr.name]={key: key}endenddeffragmented_attributescached=_cache_only?_cache_only:_attributes-_cache_exceptcached=cached.map!{|field|_attributes_keys.fetch(field,field)}non_cached=_attributes-cachednon_cached=non_cached.map!{|field|_attributes_keys.fetch(field,field)}{cached: cached,non_cached: non_cached}end# Enables a serializer to be automatically cached## Sets +::_cache+ object to <tt>ActionController::Base.cache_store</tt># when Rails.configuration.action_controller.perform_caching## @param options [Hash] with valid keys:# cache_store : @see ::_cache# key : @see ::_cache_key# only : @see ::_cache_only# except : @see ::_cache_except# skip_digest : does not include digest in cache_key# all else : @see ::_cache_options## @example# class PostSerializer < ActiveModel::Serializer# cache key: 'post', expires_in: 3.hours# attributes :title, :body## has_many :comments# end## @todo require less code comments. See# https://github.com/rails-api/active_model_serializers/pull/1249#issuecomment-146567837defcache(options={})self._cache=options.delete(:cache_store)||ActiveModelSerializers.config.cache_store||ActiveSupport::Cache.lookup_store(:null_store)self._cache_key=options.delete(:key)self._cache_only=options.delete(:only)self._cache_except=options.delete(:except)self._cache_options=options.empty??nil:optionsend# Value is from ActiveModelSerializers.config.perform_caching. Is used to# globally enable or disable all serializer caching, just like# Rails.configuration.action_controller.perform_caching, which is its# default value in a Rails application.# @return [true, false]# Memoizes value of config first time it is called with a non-nil value.# rubocop:disable Style/ClassVarsdefperform_cachingreturn@@perform_cachingifdefined?(@@perform_caching)&&!@@perform_caching.nil?@@perform_caching=ActiveModelSerializers.config.perform_cachingendaliasperform_caching?perform_caching# rubocop:enable Style/ClassVars# The canonical method for getting the cache store for the serializer.## @return [nil] when _cache is not set (i.e. when `cache` has not been called)# @return [._cache] when _cache is not the NullStore# @return [ActiveModelSerializers.config.cache_store] when _cache is the NullStore.# This is so we can use `cache` being called to mean the serializer should be cached# even if ActiveModelSerializers.config.cache_store has not yet been set.# That means that when _cache is the NullStore and ActiveModelSerializers.config.cache_store# is configured, `cache_store` becomes `ActiveModelSerializers.config.cache_store`.# @return [nil] when _cache is the NullStore and ActiveModelSerializers.config.cache_store is nil.defcache_storereturnnilif_cache.nil?return_cacheif_cache.class!=ActiveSupport::Cache::NullStoreifActiveModelSerializers.config.cache_storeself._cache=ActiveModelSerializers.config.cache_storeelsenilendenddefcache_enabled?perform_caching?&&cache_store&&!_cache_only&&!_cache_exceptenddeffragment_cache_enabled?perform_caching?&&cache_store&&(_cache_only&&!_cache_except||!_cache_only&&_cache_except)end# Read cache from cache_store# @return [Hash]# Used in CollectionSerializer to set :cached_attributesdefcache_read_multi(collection_serializer,adapter_instance,include_directive)return{}ifActiveModelSerializers.config.cache_store.blank?keys=object_cache_keys(collection_serializer,adapter_instance,include_directive)return{}ifkeys.blank?ActiveModelSerializers.config.cache_store.read_multi(*keys)end# Find all cache_key for the collection_serializer# @param serializers [ActiveModel::Serializer::CollectionSerializer]# @param adapter_instance [ActiveModelSerializers::Adapter::Base]# @param include_directive [JSONAPI::IncludeDirective]# @return [Array] all cache_key of collection_serializerdefobject_cache_keys(collection_serializer,adapter_instance,include_directive)cache_keys=[]collection_serializer.eachdo|serializer|cache_keys<<object_cache_key(serializer,adapter_instance)serializer.associations(include_directive).eachdo|association|# TODO(BF): Process relationship without evaluating lazy_associationassociation_serializer=association.lazy_association.serializerifassociation_serializer.respond_to?(:each)association_serializer.eachdo|sub_serializer|cache_keys<<object_cache_key(sub_serializer,adapter_instance)endelsecache_keys<<object_cache_key(association_serializer,adapter_instance)endendendcache_keys.compact.uniqend# @return [String, nil] the cache_key of the serializer or nildefobject_cache_key(serializer,adapter_instance)returnunlessserializer.present?&&serializer.object.present?(serializer.class.cache_enabled?||serializer.class.fragment_cache_enabled?)?serializer.cache_key(adapter_instance):nilendend### INSTANCE METHODSdeffetch_attributes(fields,cached_attributes,adapter_instance)key=cache_key(adapter_instance)cached_attributes.fetch(key)dofetch(adapter_instance,serializer_class._cache_options,key)doattributes(fields,true)endendenddeffetch(adapter_instance,cache_options=serializer_class._cache_options,key=nil)ifserializer_class.cache_storekey||=cache_key(adapter_instance)serializer_class.cache_store.fetch(key,cache_options)doyieldendelseyieldendend# 1. Determine cached fields from serializer class options# 2. Get non_cached_fields and fetch cache_fields# 3. Merge the two hashes using adapter_instance#fragment_cachedeffetch_attributes_fragment(adapter_instance,cached_attributes={})serializer_class._cache_options||={}serializer_class._cache_options[:key]=serializer_class._cache_keyifserializer_class._cache_keyfields=serializer_class.fragmented_attributesnon_cached_fields=fields[:non_cached].dupnon_cached_hash=attributes(non_cached_fields,true)include_directive=JSONAPI::IncludeDirective.new(non_cached_fields-non_cached_hash.keys)non_cached_hash.merge!associations_hash({},{include_directive: include_directive},adapter_instance)cached_fields=fields[:cached].dupkey=cache_key(adapter_instance)cached_hash=cached_attributes.fetch(key)dofetch(adapter_instance,serializer_class._cache_options,key)dohash=attributes(cached_fields,true)include_directive=JSONAPI::IncludeDirective.new(cached_fields-hash.keys)hash.merge!associations_hash({},{include_directive: include_directive},adapter_instance)endend# Merge both resultsadapter_instance.fragment_cache(cached_hash,non_cached_hash)enddefcache_key(adapter_instance)return@cache_keyifdefined?(@cache_key)parts=[]parts<<object_cache_keyparts<<adapter_instance.cache_keyparts<<serializer_class._cache_digestunlessserializer_class._skip_digest?@cache_key=expand_cache_key(parts)enddefexpand_cache_key(parts)ActiveSupport::Cache.expand_cache_key(parts)end# Use object's cache_key if available, else derive a key from the object# Pass the `key` option to the `cache` declaration or override this method to customize the cache keydefobject_cache_keyifobject.respond_to?(:cache_key)object.cache_keyelsif(serializer_cache_key=(serializer_class._cache_key||serializer_class._cache_options[:key]))object_time_safe=object.updated_atobject_time_safe=object_time_safe.strftime('%Y%m%d%H%M%S%9N')ifobject_time_safe.respond_to?(:strftime)"#{serializer_cache_key}/#{object.id}-#{object_time_safe}"elsefailUndefinedCacheKey,"#{object.class} must define #cache_key, or the 'key:' option must be passed into '#{serializer_class}.cache'"endenddefserializer_class@serializer_class||=self.classendendendend