require'thread_safe'require'jsonapi/include_directive'require'active_model/serializer/collection_serializer'require'active_model/serializer/array_serializer'require'active_model/serializer/error_serializer'require'active_model/serializer/errors_serializer'require'active_model/serializer/concerns/associations'require'active_model/serializer/concerns/attributes'require'active_model/serializer/concerns/caching'require'active_model/serializer/concerns/configuration'require'active_model/serializer/concerns/links'require'active_model/serializer/concerns/meta'require'active_model/serializer/concerns/type'require'active_model/serializer/fieldset'require'active_model/serializer/lint'# ActiveModel::Serializer is an abstract class that is# reified when subclassed to decorate a resource.moduleActiveModelclassSerializer# @see #serializable_hash for more details on these valid keys.SERIALIZABLE_HASH_VALID_KEYS=[:only,:except,:methods,:include,:root].freezeextendActiveSupport::Autoloadautoload:Adapterautoload:NullincludeConfigurationincludeAssociationsincludeAttributesincludeCachingincludeLinksincludeMetaincludeType# @param resource [ActiveRecord::Base, ActiveModelSerializers::Model]# @return [ActiveModel::Serializer]# Preferentially returns# 1. resource.serializer# 2. ArraySerializer when resource is a collection# 3. options[:serializer]# 4. lookup serializer when resource is a Classdefself.serializer_for(resource,options={})ifresource.respond_to?(:serializer_class)resource.serializer_classelsifresource.respond_to?(:to_ary)config.collection_serializerelseoptions.fetch(:serializer){get_serializer_for(resource.class,options[:namespace])}endend# @see ActiveModelSerializers::Adapter.lookup# Deprecateddefself.adapterActiveModelSerializers::Adapter.lookup(config.adapter)endclass<<selfextendActiveModelSerializers::Deprecatedeprecate:adapter,'ActiveModelSerializers::Adapter.configured_adapter'end# @api privatedefself.serializer_lookup_chain_for(klass,namespace=nil)lookups=ActiveModelSerializers.config.serializer_lookup_chainArray[*lookups].flat_mapdo|lookup|lookup.call(klass,self,namespace)end.compactend# Used to cache serializer name => serializer class# when looked up by Serializer.get_serializer_for.defself.serializers_cache@serializers_cache||=ThreadSafe::Cache.newend# @api private# Find a serializer from a class and caches the lookup.# Preferentially returns:# 1. class name appended with "Serializer"# 2. try again with superclass, if present# 3. nildefself.get_serializer_for(klass,namespace=nil)returnnilunlessconfig.serializer_lookup_enabledcache_key=ActiveSupport::Cache.expand_cache_key(klass,namespace)serializers_cache.fetch_or_store(cache_key)do# NOTE(beauby): When we drop 1.9.3 support we can lazify the map for perfs.lookup_chain=serializer_lookup_chain_for(klass,namespace)serializer_class=lookup_chain.map(&:safe_constantize).find{|x|x&&x<ActiveModel::Serializer}ifserializer_classserializer_classelsifklass.superclassget_serializer_for(klass.superclass)endendend# @api privatedefself.include_directive_from_options(options)ifoptions[:include_directive]options[:include_directive]elsifoptions[:include]JSONAPI::IncludeDirective.new(options[:include],allow_wildcard: true)elseActiveModelSerializers.default_include_directiveendend# @api privatedefself.serialization_adapter_instance@serialization_adapter_instance||=ActiveModelSerializers::Adapter::Attributesendattr_accessor:object,:root,:scope# `scope_name` is set as :current_user by default in the controller.# If the instance does not have a method named `scope_name`, it# defines the method so that it calls the +scope+.definitialize(object,options={})self.object=objectself.instance_options=optionsself.root=instance_options[:root]self.scope=instance_options[:scope]returnif!(scope_name=instance_options[:scope_name])||respond_to?(scope_name)define_singleton_methodscope_name,->{scope}enddefsuccess?trueend# @return [Hash] containing the attributes and first level# associations, similar to how ActiveModel::Serializers::JSON is used# in ActiveRecord::Base.## TODO: Include <tt>ActiveModel::Serializers::JSON</tt>.# So that the below is true:# @param options [nil, Hash] The same valid options passed to `serializable_hash`# (:only, :except, :methods, and :include).## See# https://github.com/rails/rails/blob/v5.0.0.beta2/activemodel/lib/active_model/serializers/json.rb#L17-L101# https://github.com/rails/rails/blob/v5.0.0.beta2/activemodel/lib/active_model/serialization.rb#L85-L123# https://github.com/rails/rails/blob/v5.0.0.beta2/activerecord/lib/active_record/serialization.rb#L11-L17# https://github.com/rails/rails/blob/v5.0.0.beta2/activesupport/lib/active_support/core_ext/object/json.rb#L147-L162## @example# # The :only and :except options can be used to limit the attributes included, and work# # similar to the attributes method.# serializer.as_json(only: [:id, :name])# serializer.as_json(except: [:id, :created_at, :age])## # To include the result of some method calls on the model use :methods:# serializer.as_json(methods: :permalink)## # To include associations use :include:# serializer.as_json(include: :posts)# # Second level and higher order associations work as well:# serializer.as_json(include: { posts: { include: { comments: { only: :body } }, only: :title } })defserializable_hash(adapter_options=nil,options={},adapter_instance=self.class.serialization_adapter_instance)adapter_options||={}options[:include_directive]||=ActiveModel::Serializer.include_directive_from_options(adapter_options)cached_attributes=adapter_options[:cached_attributes]||={}resource=fetch_attributes(options[:fields],cached_attributes,adapter_instance)relationships=resource_relationships(adapter_options,options,adapter_instance)resource.merge(relationships)endaliasto_hashserializable_hashaliasto_hserializable_hash# @see #serializable_hash# TODO: When moving attributes adapter logic here, @see #serializable_hash# So that the below is true:# @param options [nil, Hash] The same valid options passed to `as_json`# (:root, :only, :except, :methods, and :include).# The default for `root` is nil.# The default value for include_root is false. You can change it to true if the given# JSON string includes a single root node.defas_json(adapter_opts=nil)serializable_hash(adapter_opts)end# Used by adapter as resource root.defjson_keyroot||_type||object.class.model_name.to_s.underscoreenddefread_attribute_for_serialization(attr)ifrespond_to?(attr)send(attr)elseobject.read_attribute_for_serialization(attr)endend# @api privatedefresource_relationships(adapter_options,options,adapter_instance)relationships={}include_directive=options.fetch(:include_directive)associations(include_directive).eachdo|association|adapter_opts=adapter_options.merge(include_directive: include_directive[association.key])relationships[association.key]||=relationship_value_for(association,adapter_opts,adapter_instance)endrelationshipsend# @api privatedefrelationship_value_for(association,adapter_options,adapter_instance)returnassociation.options[:virtual_value]ifassociation.options[:virtual_value]association_serializer=association.serializerassociation_object=association_serializer&&association_serializer.objectreturnunlessassociation_objectrelationship_value=association_serializer.serializable_hash(adapter_options,{},adapter_instance)ifassociation.options[:polymorphic]&&relationship_valuepolymorphic_type=association_object.class.name.underscorerelationship_value={type: polymorphic_type,polymorphic_type.to_sym=>relationship_value}endrelationship_valueendprotectedattr_accessor:instance_optionsendend