moduleActiveModelclassSerializerUndefinedCacheKey=Class.new(StandardError)moduleCachingextendActiveSupport::Concernincludeddowith_optionsinstance_writer: false,instance_reader: falsedo|serializer|serializer.class_attribute:_cache# @api private : the cache storeserializer.class_attribute:_fragmented# @api private : @see ::fragmentedserializer.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 cached_attributes. Cannot combine with exceptserializer.class_attribute:_cache_except# @api private : when fragment caching, blacklists cached_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, self.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)supercaller_line=caller[1]base._cache_digest_file_path=caller_lineenddef_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]enddefcached_attributes_cache_only?_cache_only:_attributes-_cache_exceptenddefnon_cached_attributes_attributes-cached_attributesend# @api private# Used by FragmentCache on the CachedSerializer# to call attribute methods on the fragmented cached serializer.deffragmented(serializer)self._fragmented=serializerend# 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]defcache_read_multi(collection_serializer,adapter_instance,include_tree)return{}ifActiveModelSerializers.config.cache_store.blank?keys=object_cache_keys(collection_serializer,adapter_instance,include_tree)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_tree [ActiveModel::Serializer::IncludeTree]# @return [Array] all cache_key of collection_serializerdefobject_cache_keys(collection_serializer,adapter_instance,include_tree)cache_keys=[]collection_serializer.eachdo|serializer|cache_keys<<object_cache_key(serializer,adapter_instance)serializer.associations(include_tree).eachdo|association|ifassociation.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.cache_key(adapter_instance):nilendend# Get attributes from @cached_attributes# @return [Hash] cached attributes# def cached_attributes(fields, adapter_instance)defcached_fields(fields,adapter_instance)cache_check(adapter_instance)doattributes(fields)endenddefcache_check(adapter_instance)ifself.class.cache_enabled?self.class.cache_store.fetch(cache_key(adapter_instance),self.class._cache_options)doyieldendelsifself.class.fragment_cache_enabled?fetch_fragment_cache(adapter_instance)elseyieldendend# 1. Create a CachedSerializer and NonCachedSerializer from the serializer class# 2. Serialize the above two with the given adapter# 3. Pass their serializations to the adapter +::fragment_cache+## It will split the serializer into two, one that will be cached and one that will not## Given a resource name# 1. Dynamically creates a CachedSerializer and NonCachedSerializer# for a given class 'name'# 2. Call# CachedSerializer.cache(serializer._cache_options)# CachedSerializer.fragmented(serializer)# NonCachedSerializer.cache(serializer._cache_options)# 3. Build a hash keyed to the +cached+ and +non_cached+ serializers# 4. Call +cached_attributes+ on the serializer class and the above hash# 5. Return the hash## @example# When +name+ is <tt>User::Admin</tt># creates the Serializer classes (if they don't exist).# CachedUser_AdminSerializer# NonCachedUser_AdminSerializer## Given a hash of its cached and non-cached serializers# 1. Determine cached attributes from serializer class options# 2. Add cached attributes to cached Serializer# 3. Add non-cached attributes to non-cached Serializerdeffetch_fragment_cache(adapter_instance)serializer_class_name=self.class.name.gsub('::'.freeze,'_'.freeze)self.class._cache_options||={}self.class._cache_options[:key]=self.class._cache_keyifself.class._cache_keycached_serializer=_get_or_create_fragment_cached_serializer(serializer_class_name)cached_hash=ActiveModelSerializers::SerializableResource.new(object,serializer: cached_serializer,adapter: adapter_instance.class).serializable_hashnon_cached_serializer=_get_or_create_fragment_non_cached_serializer(serializer_class_name)non_cached_hash=ActiveModelSerializers::SerializableResource.new(object,serializer: non_cached_serializer,adapter: adapter_instance.class).serializable_hash# Merge both resultsadapter_instance.fragment_cache(cached_hash,non_cached_hash)enddef_get_or_create_fragment_cached_serializer(serializer_class_name)cached_serializer=_get_or_create_fragment_serializer"Cached#{serializer_class_name}"cached_serializer.cache(self.class._cache_options)cached_serializer.type(self.class._type)cached_serializer.fragmented(self)self.class.cached_attributes.eachdo|attribute|options=self.class._attributes_keys[attribute]||{}cached_serializer.attribute(attribute,options)endcached_serializerenddef_get_or_create_fragment_non_cached_serializer(serializer_class_name)non_cached_serializer=_get_or_create_fragment_serializer"NonCached#{serializer_class_name}"non_cached_serializer.type(self.class._type)non_cached_serializer.fragmented(self)self.class.non_cached_attributes.eachdo|attribute|options=self.class._attributes_keys[attribute]||{}non_cached_serializer.attribute(attribute,options)endnon_cached_serializerenddef_get_or_create_fragment_serializer(name)returnObject.const_get(name)ifObject.const_defined?(name)Object.const_set(name,Class.new(ActiveModel::Serializer))enddefcache_key(adapter_instance)return@cache_keyifdefined?(@cache_key)parts=[]parts<<object_cache_keyparts<<adapter_instance.cached_nameparts<<self.class._cache_digestunlessself.class._skip_digest?@cache_key=parts.join('/')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=(self.class._cache_key||self.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 '#{self.class}.cache'"endendendendend