# frozen_string_literal: truerequire'active_model/serializer/field'require'active_model/serializer/association'moduleActiveModelclassSerializer# Holds all the meta-data about an association as it was specified in the# ActiveModel::Serializer class.## @example# class PostSerializer < ActiveModel::Serializer# has_one :author, serializer: AuthorSerializer# belongs_to :boss, type: :users, foreign_key: :boss_id# has_many :comments# has_many :comments, key: :last_comments do# object.comments.last(1)# end# has_many :secret_meta_data, if: :is_admin?## has_one :blog do |serializer|# meta count: object.roles.count# serializer.cached_blog# end## private## def cached_blog# cache_store.fetch("cached_blog:#{object.updated_at}") do# Blog.find(object.blog_id)# end# end## def is_admin?# current_user.admin?# end# end## Specifically, the association 'comments' is evaluated two different ways:# 1) as 'comments' and named 'comments'.# 2) as 'object.comments.last(1)' and named 'last_comments'.## PostSerializer._reflections # =># # {# # author: HasOneReflection.new(:author, serializer: AuthorSerializer),# # comments: HasManyReflection.new(:comments)# # last_comments: HasManyReflection.new(:comments, { key: :last_comments }, #<Block>)# # secret_meta_data: HasManyReflection.new(:secret_meta_data, { if: :is_admin? })# # }## So you can inspect reflections in your Adapters.classReflection<Fieldattr_reader:foreign_key,:typedefinitialize(*)superoptions[:links]={}options[:include_data_setting]=Serializer.config.include_data_defaultoptions[:meta]=nil@type=options.fetch(:type)doclass_name=options.fetch(:class_name,name.to_s.camelize.singularize)class_name.underscore.pluralize.to_symend@foreign_key=options.fetch(:foreign_key)doifcollection?"#{name.to_s.singularize}_ids".to_symelse"#{name}_id".to_symendendend# @api public# @example# has_one :blog do# include_data false# link :self, 'a link'# link :related, 'another link'# link :self, '//example.com/link_author/relationships/bio'# id = object.profile.id# link :related do# "//example.com/profiles/#{id}" if id != 123# end# link :related do# ids = object.likes.map(&:id).join(',')# href "//example.com/likes/#{ids}"# meta ids: ids# end# enddeflink(name,value=nil,&block)options[:links][name]=block_given??block:value:nilend# @api public# @example# has_one :blog do# include_data false# meta(id: object.blog.id)# meta liked: object.likes.any?# link :self do# href object.blog.id.to_s# meta(id: object.blog.id)# enddefmeta(value=nil,&block)options[:meta]=block_given??block:value:nilend# @api public# @example# has_one :blog do# include_data false# link :self, 'a link'# link :related, 'another link'# end## has_one :blog do# include_data false# link :self, 'a link'# link :related, 'another link'# end## belongs_to :reviewer do# meta name: 'Dan Brown'# include_data true# end## has_many :tags, serializer: TagSerializer do# link :self, '//example.com/link_author/relationships/tags'# include_data :if_sideloaded# enddefinclude_data(value=true)options[:include_data_setting]=value:nilenddefcollection?falseenddefinclude_data?(include_slice)include_data_setting=options[:include_data_setting]caseinclude_data_settingwhen:if_sideloadedtheninclude_slice.key?(options.fetch(:key,name))whentruethentruewhenfalsethenfalseelsefailArgumentError,"Unknown include_data_setting '#{include_data_setting.inspect}'"endend# @param serializer [ActiveModel::Serializer]# @yield [ActiveModel::Serializer]# @return [:nil, associated resource or resource collection]defvalue(serializer,include_slice)# NOTE(BF): This method isn't thread-safe because the _reflections class attribute is not thread-safe# Therefore, when we build associations from reflections, we dup the entire reflection instance.# Better solutions much appreciated!@object=serializer.object@scope=serializer.scopeblock_value=instance_exec(serializer,&block)ifblockreturnunlessinclude_data?(include_slice)ifblock&&block_value!=:nilblock_valueelseserializer.read_attribute_for_serialization(name)endend# @api privatedefforeign_key_on:relatedend# Build association. This method is used internally to# build serializer's association by its reflection.## @param [Serializer] parent_serializer for given association# @param [Hash{Symbol => Object}] parent_serializer_options## @example# # Given the following serializer defined:# class PostSerializer < ActiveModel::Serializer# has_many :comments, serializer: CommentSummarySerializer# end## # Then you instantiate your serializer# post_serializer = PostSerializer.new(post, foo: 'bar') ## # to build association for comments you need to get reflection# comments_reflection = PostSerializer._reflections.detect { |r| r.name == :comments }# # and #build_association# comments_reflection.build_association(post_serializer, foo: 'bar')## @api privatedefbuild_association(parent_serializer,parent_serializer_options,include_slice={})association_options={parent_serializer: parent_serializer,parent_serializer_options: parent_serializer_options,include_slice: include_slice}Association.new(self,association_options)endprotected# used in instance execattr_accessor:object,:scopeendendend