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



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

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



51
52
53
# File 'lib/active_model/serializer/reflection.rb', line 51

def foreign_key
  @foreign_key
end

#typeObject (readonly)

Returns the value of attribute type



51
52
53
# File 'lib/active_model/serializer/reflection.rb', line 51

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


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

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)


136
137
138
# File 'lib/active_model/serializer/reflection.rb', line 136

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.



171
172
173
# File 'lib/active_model/serializer/reflection.rb', line 171

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


131
132
133
134
# File 'lib/active_model/serializer/reflection.rb', line 131

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

#include_data?(include_slice) ⇒ Boolean

Returns:

  • (Boolean)


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

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


88
89
90
91
# File 'lib/active_model/serializer/reflection.rb', line 88

def link(name, value = nil)
  options[:links][name] = block_given? ? Proc.new : value
  :nil
end

#meta(value = nil) ⇒ 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


103
104
105
106
# File 'lib/active_model/serializer/reflection.rb', line 103

def meta(value = nil)
  options[:meta] = block_given? ? Proc.new : value
  :nil
end

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

Parameters:

Yields:

Returns:

  • (:nil, associated resource or resource collection)


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

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