123456789_123456789_123456789_123456789_123456789_

Class: Mongoid::Association::Referenced::HasMany::Enumerable

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, Forwardable
Instance Chain:
self, Enumerable
Inherits: Object
Defined in: lib/mongoid/association/referenced/has_many/enumerable.rb

Overview

This class is the wrapper for all referenced associations that have a target that can be a criteria or array of _loaded documents. This handles both cases or a combination of the two.

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(target, base = nil, association = nil) ⇒ Enumerable

Initialize the new enumerable either with a criteria or an array.

Examples:

Initialize the enumerable with a criteria.

Enumberable.new(Post.where(:person_id => id))

Initialize the enumerable with an array.

Enumerable.new([ post ])

Parameters:

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 262

def initialize(target, base = nil, association = nil)
  @_base = base
  @_association = association
  if target.is_a?(Criteria)
    @_added, @executed, @_loaded, @_unloaded = {}, false, {}, target
  else
    @_added, @executed = {}, true
    @_loaded = target.inject({}) do |_target, doc|
      _target[doc._id] = doc if doc
      _target
    end
  end
end

Instance Attribute Details

#_added (rw)

The three main instance variables are collections of documents.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 21

attr_accessor :_added, :_loaded, :_unloaded

#_added Documents that have been appended.(Documents that have been appended.) (rw)

The three main instance variables are collections of documents.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 21

attr_accessor :_added, :_loaded, :_unloaded

#_loaded (rw)

The three main instance variables are collections of documents.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 21

attr_accessor :_added, :_loaded, :_unloaded

#_loaded Persisted documents that have been _loaded.(Persisted documents that have been _loaded.) (rw)

The three main instance variables are collections of documents.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 21

attr_accessor :_added, :_loaded, :_unloaded

#_loaded?true | false (rw)

Has the enumerable been _loaded? This will be true if the criteria has been executed or we manually load the entire thing.

Examples:

Is the enumerable _loaded?

enumerable._loaded?

Returns:

  • (true | false)

    If the enumerable has been _loaded.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 353

def _loaded?
  !!@executed
end

#_unloaded (rw)

The three main instance variables are collections of documents.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 21

attr_accessor :_added, :_loaded, :_unloaded

#_unloaded A criteria representing persisted docs.(A criteria representing persisted docs.) (rw)

The three main instance variables are collections of documents.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 21

attr_accessor :_added, :_loaded, :_unloaded

#empty?true | false (readonly)

Is the enumerable empty? Will determine if the count is zero based on whether or not it is _loaded.

Examples:

Is the enumerable empty?

enumerable.empty?

Returns:

  • (true | false)

    If the enumerable is empty.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 193

def empty?
  if _loaded?
    in_memory.empty?
  else
    _added.empty? && !_unloaded.exists?
  end
end

Instance Method Details

#<<(document) ⇒ Document Also known as: #push

Append a document to the enumerable.

Examples:

Append the document.

enumerable << document

Parameters:

  • document (Document)

    The document to append.

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 60

def <<(document)
  _added[document._id] = document
  self
end

#==(other) ⇒ true | false

Check if the enumerable is equal to the other object.

Examples:

Check equality.

enumerable == []

Parameters:

  • other (Enumerable)

    The other enumerable.

Returns:

  • (true | false)

    If the objects are equal.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 33

def ==(other)
  return false unless other.respond_to?(:entries)
  entries == other.entries
end

#===(other) ⇒ true | false

Check equality of the enumerable against the provided object for case statements.

Examples:

Check case equality.

enumerable === Array

Parameters:

  • other (Object)

    The object to check.

Returns:

  • (true | false)

    If the objects are equal in a case.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 47

def ===(other)
  return false unless other.respond_to?(:entries)
  entries === other.entries
end

#any?(*args) ⇒ true | false

Returns whether the association has any documents, optionally subject to the provided filters.

This method returns true if the association has any persisted documents and if it has any not yet persisted documents.

If the association is already loaded, this method inspects the loaded documents and does not query the database. If the association is not loaded, the argument-less and block-less version does not load the association; the other versions (that delegate to Enumerable) may or may not load the association completely depending on whether it is iterated to completion.

This method can take a parameter and a block. The behavior with either the parameter or the block is delegated to the standard library Enumerable module.

Note that when Enumerable’s any? method is invoked with both a block and a pattern, it only uses the pattern.

Parameters:

  • *args (Object...)

    The condition that documents must satisfy. See Enumerable documentation for details.

Returns:

  • (true | false)

    If the association has any documents.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 225

def any?(*args)
  return super if args.any? || block_given?

  !empty?
end

#as_json(options = {}) ⇒ Hash

Send #as_json to the entries, without encoding.

Examples:

Get the enumerable as json.

enumerable.as_json

Parameters:

  • options (Hash) (defaults to: {})

    Optional parameters.

Returns:

  • (Hash)

    The entries all _loaded as a hash.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 452

def as_json(options = {})
  entries.as_json(options)
end

#clearArray<Document>

Clears out all the documents in this enumerable. If passed a block it will yield to each document that is in memory.

Examples:

Clear out the enumerable.

enumerable.clear

Clear out the enumerable with a block.

enumerable.clear do |doc|
  doc.unbind
end

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 79

def clear
  if block_given?
    in_memory { |doc| yield(doc) }
  end
  _loaded.clear and _added.clear
end

#cloneArray<Document>

Note:

This loads all documents into memory.

Clones each document in the enumerable.

Examples:

Clone the enumerable.

enumerable.clone

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 94

def clone
  collect { |doc| doc.clone }
end

#delete(document) {|doc| ... } ⇒ Document

Delete the supplied document from the enumerable.

Examples:

Delete the document.

enumerable.delete(document)

Parameters:

  • document (Document)

    The document to delete.

Yields:

  • (doc)

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 106

def delete(document)
  doc = (_loaded.delete(document._id) || _added.delete(document._id))
  unless doc
    if _unloaded && _unloaded.where(_id: document._id).exists?
      yield(document) if block_given?
      return document
    end
  end
  yield(doc) if block_given?
  doc
end

#delete_if(&block) ⇒ Array<Document>

Note:

This operation loads all documents from the database.

Deletes every document in the enumerable for where the block returns true.

Examples:

Delete all matching documents.

enumerable.delete_if do |doc|
  dod._id == _id
end

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 129

def delete_if(&block)
  load_all!
  deleted = in_memory.select(&block)
  deleted.each do |doc|
    _loaded.delete(doc._id)
    _added.delete(doc._id)
  end
  self
end

#eachtrue

Iterating over this enumerable has to handle a few different scenarios.

If the enumerable has its criteria _loaded into memory then it yields to all the _loaded docs and all the _added docs.

If the enumerable has not _loaded the criteria then it iterates over the cursor while loading the documents and then iterates over the _added docs.

If no block is passed then it returns an enumerator containing all docs.

Examples:

Iterate over the enumerable.

enumerable.each do |doc|
  puts doc
end

return an enumerator containing all the docs

a = enumerable.each

Returns:

  • (true)

    That the enumerable is now _loaded.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 162

def each
  unless block_given?
    return to_enum
  end
  if _loaded?
    _loaded.each_pair do |id, doc|
      document = _added.delete(doc._id) || doc
      set_base(document)
      yield(document)
    end
  else
    unloaded_documents.each do |doc|
      document = _added.delete(doc._id) || _loaded.delete(doc._id) || doc
      _loaded[document._id] = document
      set_base(document)
      yield(document)
    end
  end
  _added.each_pair do |id, doc|
    yield(doc)
  end
  @executed = true
end

#first(limit = nil) ⇒ Document

Note:

Automatically adding a sort on _id when no other sort is defined on the criteria has the potential to cause bad performance issues. If you experience unexpected poor performance when using #first or #last, use #take instead. Be aware that #take won’t guarantee order.

Get the first document in the enumerable. Will check the persisted documents first. Does not load the entire enumerable.

Examples:

Get the first document.

enumerable.first

Parameters:

  • limit (Integer) (defaults to: nil)

    The number of documents to return.

Returns:

  • (Document)

    The first document found.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 246

def first(limit = nil)
  _loaded.try(:values).try(:first) ||
      _added[(ul = _unloaded.try(:first, limit)).try(:_id)] ||
      ul ||
      _added.values.try(:first)
end

#in_memoryArray<Document>

Note:

When passed a block it yields to each document.

Return all the documents in the enumerable that have been _loaded or _added.

Examples:

Get the in memory docs.

enumerable.in_memory

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 309

def in_memory
  docs = (_loaded.values + _added.values)
  docs.each do |doc|
    yield(doc) if block_given?
  end
end

#include?(doc) ⇒ true | false

Does the target include the provided document?

Examples:

Does the target include the document?

enumerable.include?(document)

Parameters:

  • doc (Document)

    The document to check.

Returns:

  • (true | false)

    If the document is in the target.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 284

def include?(doc)
  return super unless _unloaded
  _unloaded.where(_id: doc._id).exists? || _added.has_key?(doc._id)
end

#inspectString

Inspection will just inspect the entries for nice array-style printing.

Examples:

Inspect the enumerable.

enumerable.inspect

Returns:

  • (String)

    The inspected enum.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 296

def inspect
  entries.inspect
end

#last(limit = nil) ⇒ Document

Note:

Automatically adding a sort on _id when no other sort is defined on the criteria has the potential to cause bad performance issues. If you experience unexpected poor performance when using #first or #last, use #take instead. Be aware that #take won’t guarantee order.

Get the last document in the enumerable. Will check the new documents first. Does not load the entire enumerable.

Examples:

Get the last document.

enumerable.last

Parameters:

  • limit (Integer) (defaults to: nil)

    The number of documents to return.

Returns:

  • (Document)

    The last document found.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 331

def last(limit = nil)
  _added.values.try(:last) ||
      _loaded.try(:values).try(:last) ||
      _added[(ul = _unloaded.try(:last, limit)).try(:_id)] ||
      ul
end

#length

Alias for #size.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 430

alias :length :size

#load_all!true

Loads all the documents in the enumerable from the database.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 344

alias :load_all! :entries

#marshal_dumpArray<Object>

Provides the data needed to Marshal.dump an enumerable proxy.

Examples:

Dump the proxy.

Marshal.dump(proxy)

Returns:

  • (Array<Object>)

    The dumped data.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 363

def marshal_dump
  [_added, _loaded, _unloaded, @executed]
end

#marshal_load(data) ⇒ Array<Object>

Loads the data needed to Marshal.load an enumerable proxy.

Examples:

Load the proxy.

Marshal.load(proxy)

Returns:

  • (Array<Object>)

    The dumped data.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 373

def marshal_load(data)
  @_added, @_loaded, @_unloaded, @executed = data
end

#push(document)

Alias for #<<.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 65

alias :push :<<

#resetfalse

Reset the enumerable back to its persisted state.

Examples:

Reset the enumerable.

enumerable.reset

Returns:

  • (false)

    Always false.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 383

def reset
  _loaded.clear
  _added.clear
  @executed = false
end

#reset_unloaded(criteria)

Resets the underlying unloaded criteria object with a new one. Used my HABTM associations to keep the underlying array in sync.

Examples:

Reset the unloaded documents.

enumerable.reset_unloaded(criteria)

Parameters:

  • criteria (Criteria)

    The criteria to replace with.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 396

def reset_unloaded(criteria)
  @_unloaded = criteria if _unloaded.is_a?(Criteria)
end

#respond_to?(name, include_private = false) ⇒ true | false

Does this enumerable respond to the provided method?

Examples:

Does the enumerable respond to the method?

enumerable.respond_to?(:sum)

Parameters:

  • name (String | Symbol)

    The name of the method.

  • include_private (true | false) (defaults to: false)

    Whether to include private methods.

Returns:

  • (true | false)

    Whether the enumerable responds.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 410

def respond_to?(name, include_private = false)
  [].respond_to?(name, include_private) || super
end

#set_base(document) (private)

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 470

def set_base(document)
  if @_association.is_a?(Referenced::HasMany)
    document.set_relation(@_association.inverse, @_base) if @_association
  end
end

#sizeInteger Also known as: #length

Gets the total size of this enumerable. This is a combination of all the persisted and unpersisted documents.

Examples:

Get the size.

enumerable.size

Returns:

  • (Integer)

    The size of the enumerable.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 421

def size
  count = (_unloaded ? _unloaded.count : _loaded.count)
  if count.zero?
    count + _added.count
  else
    count + _added.values.count { |d| d.new_record? }
  end
end

#to_json(options = {}) ⇒ String

Send #to_json to the entries.

Examples:

Get the enumerable as json.

enumerable.to_json

Parameters:

  • options (Hash) (defaults to: {})

    Optional parameters.

Returns:

  • (String)

    The entries all _loaded as a string.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 440

def to_json(options = {})
  entries.to_json(options)
end

#uniqArray<Document>

Note:

This operation loads all documents from the database.

Return all the unique documents in the enumerable.

Examples:

Get all the unique documents.

enumerable.uniq

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 464

def uniq
  entries.uniq
end

#unloaded_documents (private)

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 480

def unloaded_documents
  if unsatisfiable_criteria?(_unloaded.selector)
    []
  else
    _unloaded
  end
end

#unsatisfiable_criteria?(selector) ⇒ true | false (private)

Checks whether conditions in the given hash are known to be unsatisfiable, i.e. querying with this hash will always return no documents.

This method only handles condition shapes that ::Mongoid itself uses when it builds association queries. Return value true indicates the condition always produces an empty document set. Note however that return value false is not a guarantee that the condition won’t produce an empty document set.

Examples:

Unsatisfiable conditions

unsatisfiable_criteria?({'_id' => {'$in' => []}})
# => true

Conditions which may be satisfiable

unsatisfiable_criteria?({'_id' => '123'})
# => false

Conditions which are unsatisfiable that this method does not handle

unsatisfiable_criteria?({'foo' => {'$in' => []}})
# => false

Parameters:

  • selector (Hash)

    The conditions to check.

Returns:

  • (true | false)

    Whether hash contains known unsatisfiable conditions.

[ GitHub ]

  
# File 'lib/mongoid/association/referenced/has_many/enumerable.rb', line 513

def unsatisfiable_criteria?(selector)
  unsatisfiable_criteria = { '_id' => { '$in' => [] } }
  return true if selector == unsatisfiable_criteria
  return false unless selector.length == 1 && selector.keys == %w[$and]

  value = selector.values.first
  value.is_a?(Array) && value.any? do |sub_value|
    sub_value.is_a?(Hash) && unsatisfiable_criteria?(sub_value)
  end
end