module AmsLazyRelationships::Core::LazyDigMethod

Provides `lazy_dig` as an instance method for serializers, in order to make possible to dig relationships in depth just like `Hash#dig` do, keeping the laziness and N+1-free evaluation.

Public Instance Methods

lazy_dig(*relation_names) click to toggle source

@param relation_names [Array<Symbol>] the sequence of relation names

to dig through.

@return [ActiveRecord::Base, Array<ActiveRecord::Base>, nil] ActiveRecord

objects found by digging through the sequence of nested relationships.
Singular or plural nature of returned value depends from the
singular/plural nature of the chain of relation_names.

@example

class AuthorSerializer < BaseSerializer
  lazy_belongs_to :address
  lazy_has_many :rewards
end

class BlogPostSerializer < BaseSerializer
  lazy_belongs_to :author

  attribute :author_address do
    # returns single AR object or nil
    lazy_dig(:author, :address)&.full_address
  end

  attribute :author_rewards do
    # returns an array of AR objects
    lazy_dig(:author, :rewards).map(&:description)
  end
end
# File lib/ams_lazy_relationships/core/lazy_dig_method.rb, line 34
def lazy_dig(*relation_names)
  relationships = {
    multiple: false,
    data: [{
      serializer: self.class,
      object: object
    }]
  }

  relation_names.each do |relation_name|
    lazy_dig_relationship!(relation_name, relationships)
  end

  objects = relationships[:data].map { |r| r[:object] }

  relationships[:multiple] ? objects : objects.first
end

Private Instance Methods

lazy_dig_next_objects!(relation_name, serializer, object) click to toggle source
# File lib/ams_lazy_relationships/core/lazy_dig_method.rb, line 71
def lazy_dig_next_objects!(relation_name, serializer, object)
  serializer&.send(
    :load_lazy_relationship,
    relation_name,
    object
  )
end
lazy_dig_next_relationships!(relation_name, serializer, next_objects) click to toggle source
# File lib/ams_lazy_relationships/core/lazy_dig_method.rb, line 79
def lazy_dig_next_relationships!(relation_name, serializer, next_objects)
  Array.wrap(next_objects).map do |next_object|
    next_serializer = serializer.send(
      :lazy_serializer_for,
      next_object,
      relation_name: relation_name
    )

    {
      serializer: next_serializer,
      object: next_object
    }
  end
end
lazy_dig_relationship!(relation_name, relationships) click to toggle source
# File lib/ams_lazy_relationships/core/lazy_dig_method.rb, line 54
def lazy_dig_relationship!(relation_name, relationships)
  relationships[:data].map! do |data|
    serializer = data[:serializer]
    object = data[:object]

    next_objects = lazy_dig_next_objects!(relation_name, serializer, object)
    next unless next_objects

    relationships[:multiple] ||= next_objects.respond_to?(:to_ary)

    lazy_dig_next_relationships!(relation_name, serializer, next_objects)
  end

  relationships[:data].flatten!
  relationships[:data].compact!
end