module Mongoid::Traversable

Mixin module included in Mongoid::Document to provide behavior around traversing the document graph.

Attributes

discriminator_mapping[RW]

@api private

@return [ Hash<String, Class> ] The current mapping of discriminator_values to classes

Public Class Methods

add_discriminator_mapping(value, klass = self) click to toggle source

Add a discriminator mapping to the parent class. This mapping is used when receiving a document to identify its class.

@param [ String ] value The discriminator_value that was just set @param [ Class ] The class the discriminator_value was set on

@api private

# File lib/mongoid/traversable.rb, line 182
def self.add_discriminator_mapping(value, klass = self)
  self.discriminator_mapping ||= {}
  self.discriminator_mapping[value] = klass
  superclass.add_discriminator_mapping(value, klass) if hereditary?
end
get_discriminator_mapping(value) click to toggle source

Get the discriminator mapping from the parent class. This method returns nil if there is no mapping for the given value.

@param [ String ] value The discriminator_value to retrieve

@return [ Class | nil ] klass The class corresponding to the given discriminator_value. If

the value is not in the mapping, this method returns nil.

@api private

# File lib/mongoid/traversable.rb, line 197
def self.get_discriminator_mapping(value)
  self.discriminator_mapping[value] if self.discriminator_mapping
end

Public Instance Methods

_children(reset: false) click to toggle source

Get all child Documents to this Document

@return [ Array<Document> ] All child documents in the hierarchy.

@api private

# File lib/mongoid/traversable.rb, line 207
def _children(reset: false)
  # See discussion above for the `_parent` method, as to why the variable
  # here needs to have two underscores.
  #
  # rubocop:disable Naming/MemoizedInstanceVariableName
  if reset
    @__children = nil
  else
    @__children ||= collect_children
  end
  # rubocop:enable Naming/MemoizedInstanceVariableName
end
_descendants(reset: false) click to toggle source

Get all descendant Documents of this Document recursively. This is used when calling update persistence operations from the root document, where changes in the entire tree need to be determined. Note that persistence from the embedded documents will always be preferred, since they are optimized calls… This operation can get expensive in domains with large hierarchies.

@return [ Array<Document> ] All descendant documents in the hierarchy.

@api private

# File lib/mongoid/traversable.rb, line 230
def _descendants(reset: false)
  # See discussion above for the `_parent` method, as to why the variable
  # here needs to have two underscores.
  #
  # rubocop:disable Naming/MemoizedInstanceVariableName
  if reset
    @__descendants = nil
  else
    @__descendants ||= collect_descendants
  end
  # rubocop:enable Naming/MemoizedInstanceVariableName
end
_parent() click to toggle source

Retrieves the parent document of this document.

@return [ Mongoid::Document | nil ] the parent document

@api private

# File lib/mongoid/traversable.rb, line 74
def _parent
  @__parent || nil
end
_parent=(document) click to toggle source

Sets the parent document of this document.

@param [ Mongoid::Document | nil ] document the document to set as

the parent document.

@returns [ Mongoid::Document ] The parent document.

@api private

# File lib/mongoid/traversable.rb, line 86
def _parent=(document)
  @__parent = document
end
_reset_memoized_descendants!() click to toggle source

Resets the memoized descendants on the object. Called internally when an embedded array changes size.

@return [ nil ] nil.

@api private

# File lib/mongoid/traversable.rb, line 357
def _reset_memoized_descendants!
  _parent&._reset_memoized_descendants!
  _children reset: true
  _descendants reset: true
end
_root() click to toggle source

Return the root document in the object graph. If the current document is the root object in the graph it will return self.

@example Get the root document in the hierarchy.

document._root

@return [ Document ] The root document in the hierarchy.

# File lib/mongoid/traversable.rb, line 370
def _root
  object = self
  object = object._parent while object._parent
  object
end
_root?() click to toggle source

Is this document the root document of the hierarchy?

@example Is the document the root?

document._root?

@return [ true | false ] If the document is the root.

# File lib/mongoid/traversable.rb, line 382
def _root?
  _parent ? false : true
end
collect_children() click to toggle source

Collect all the children of this document.

@return [ Array<Document> ] The children.

@api private

# File lib/mongoid/traversable.rb, line 248
def collect_children
  [].tap do |children|
    embedded_relations.each_pair do |name, _association|
      without_autobuild do
        child = send(name)
        children.concat(Array.wrap(child)) if child
      end
    end
  end
end
collect_descendants() click to toggle source

Collect all the descendants of this document.

@return [ Array<Document> ] The descendants.

@api private

# File lib/mongoid/traversable.rb, line 264
def collect_descendants
  children = []
  to_expand = _children
  expanded = {}

  until to_expand.empty?
    expanding = to_expand
    to_expand = []
    expanding.each do |child|
      next if expanded[child]

      # Don't mark expanded if _id is nil, since documents are compared by
      # their _ids, multiple embedded documents with nil ids will compare
      # equally, and some documents will not be expanded.
      expanded[child] = true if child._id
      children << child
      to_expand += child._children
    end
  end

  children
end
flag_descendants_persisted() click to toggle source

Marks all descendants as being persisted.

@return [ Array<Document> ] The flagged descendants.

# File lib/mongoid/traversable.rb, line 290
def flag_descendants_persisted
  _descendants.each do |child|
    child.new_record = false
  end
end
hereditary?() click to toggle source

Determines if the document is a subclass of another document.

@example Check if the document is a subclass

Square.new.hereditary?

@return [ true | false ] True if hereditary, false if not.

# File lib/mongoid/traversable.rb, line 302
def hereditary?
  self.class.hereditary?
end
parentize(document) click to toggle source

Sets up a child/parent association. This is used for newly created objects so they can be properly added to the graph.

@example Set the parent document.

document.parentize(parent)

@param [ Document ] document The parent document.

@return [ Document ] The parent document.

# File lib/mongoid/traversable.rb, line 315
def parentize(document)
  self._parent = document
end
remove_child(child) click to toggle source

Remove a child document from this parent. If an embeds one then set to nil, otherwise remove from the embeds many.

This is called from the RemoveEmbedded persistence command.

@example Remove the child.

document.remove_child(child)

@param [ Document ] child The child (embedded) document to remove.

# File lib/mongoid/traversable.rb, line 328
def remove_child(child)
  name = child.association_name
  if child.embedded_one?
    attributes.delete(child._association.store_as)
    remove_ivar(name)
  else
    relation = send(name)
    relation._remove(child)
  end
end
reset_persisted_descendants() click to toggle source

After descendants are persisted we can call this to move all their changes and flag them as persisted in one call.

@return [ Array<Document> ] The descendants.

# File lib/mongoid/traversable.rb, line 343
def reset_persisted_descendants
  _descendants.each do |child|
    child.move_changes
    child.new_record = false
  end
  _reset_memoized_descendants!
end