Class: ActiveModel::Serializer::Reflection

Inherits:
Field
  • Object
show all
Defined in:
lib/active_model/serializer/reflection.rb

Overview

Holds all the meta-data about an association as it was specified in the ActiveModel::Serializer class.

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.

Examples:

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

Instance Attribute Summary collapse

Attributes inherited from Field

#block, #name, #options

Instance Method Summary collapse

Methods inherited from Field

#excluded?

Constructor Details

#initializeReflection

Returns a new instance of Reflection.



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/active_model/serializer/reflection.rb', line 55

def initialize(*)
  super
  options[:links] = {}
  options[:include_data_setting] = Serializer.config.include_data_default
  options[:meta] = nil
  @type = options.fetch(:type) do
    class_name = options.fetch(:class_name, name.to_s.camelize.singularize)
    class_name.underscore.pluralize.to_sym
  end
  @foreign_key = options.fetch(:foreign_key) do
    if collection?
      "#{name.to_s.singularize}_ids".to_sym
    else
      "#{name}_id".to_sym
    end
  end
end

Instance Attribute Details

#foreign_keyObject (readonly)

Returns the value of attribute foreign_key.



53
54
55
# File 'lib/active_model/serializer/reflection.rb', line 53

def foreign_key
  @foreign_key
end

#typeObject (readonly)

Returns the value of attribute type.



53
54
55
# File 'lib/active_model/serializer/reflection.rb', line 53

def type
  @type
end

Instance Method Details

#build_association(parent_serializer, parent_serializer_options, include_slice = {}) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Build association. This method is used internally to build serializer’s association by its reflection.

Examples:

# 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')

Parameters:

  • parent_serializer (Serializer)

    for given association

  • parent_serializer_options (Hash{Symbol => Object})


197
198
199
200
201
202
203
204
# File 'lib/active_model/serializer/reflection.rb', line 197

def build_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)
end

#collection?Boolean

Returns:

  • (Boolean)


138
139
140
# File 'lib/active_model/serializer/reflection.rb', line 138

def collection?
  false
end

#foreign_key_onObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



173
174
175
# File 'lib/active_model/serializer/reflection.rb', line 173

def foreign_key_on
  :related
end

#include_data(value = true) ⇒ Object

Examples:

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
 end


133
134
135
136
# File 'lib/active_model/serializer/reflection.rb', line 133

def include_data(value = true)
  options[:include_data_setting] = value
  :nil
end

#include_data?(include_slice) ⇒ Boolean

Returns:

  • (Boolean)


142
143
144
145
146
147
148
149
150
# File 'lib/active_model/serializer/reflection.rb', line 142

def include_data?(include_slice)
  include_data_setting = options[:include_data_setting]
  case include_data_setting
  when :if_sideloaded then include_slice.key?(options.fetch(:key, name))
  when true           then true
  when false          then false
  else fail ArgumentError, "Unknown include_data_setting '#{include_data_setting.inspect}'"
  end
end

Examples:

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
end


90
91
92
93
# File 'lib/active_model/serializer/reflection.rb', line 90

def link(name, value = nil, &block)
  options[:links][name] = block_given? ? block : value
  :nil
end

#meta(value = nil, &block) ⇒ Object

Examples:

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)
  end


105
106
107
108
# File 'lib/active_model/serializer/reflection.rb', line 105

def meta(value = nil, &block)
  options[:meta] = block_given? ? block : value
  :nil
end

#value(serializer, include_slice) {|ActiveModel::Serializer| ... } ⇒ :nil, associated resource or resource collection

Parameters:

Yields:

Returns:

  • (:nil, associated resource or resource collection)


155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/active_model/serializer/reflection.rb', line 155

def value(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.scope

  block_value = instance_exec(serializer, &block) if block
  return unless include_data?(include_slice)

  if block && block_value != :nil
    block_value
  else
    serializer.read_attribute_for_serialization(name)
  end
end