class Mongoid::Association::Embedded::EmbedsMany::Proxy

Transparent proxy for embeds_many associations. An instance of this class is returned when calling the association getter method on the parent document. This class inherits from Mongoid::Association::Proxy and forwards most of its methods to the target of the association, i.e. the array of child documents.

Attributes

_unscoped[RW]

Public Class Methods

new(base, target, association) click to toggle source

Instantiate a new embeds_many association.

@example Create the new association.

Many.new(person, addresses, association)

@param [ Document ] base The document this association hangs off of. @param [ Array<Document> ] target The child documents of the association. @param [ Mongoid::Association::Relatable ] association The association metadata.

@return [ Many ] The proxy.

Calls superclass method Mongoid::Association::Proxy::new
# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 67
def initialize(base, target, association)
  super do
    _target.each_with_index do |doc, index|
      integrate(doc)
      doc._index = index
    end
    update_attributes_hash
    @_unscoped = _target.dup
    @_target = scope(_target)
  end
end

Public Instance Methods

<<(*args) click to toggle source

Appends a document or array of documents to the association. Will set the parent and update the index in the process.

@example Append a document.

person.addresses << address

@example Push a document.

person.addresses.push(address)

@param [ Document… ] *args Any number of documents.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 89
def <<(*args)
  docs = args.flatten
  return unless docs.any?
  return concat(docs) if docs.size > 1

  docs.first.tap do |doc|
    append(doc)
    doc.save if persistable? && !_assigning?
  end

  self
end
Also aliased as: push
_remove(document) click to toggle source

Removes a single document from the collection *in memory only*. It will not persist the change.

@param [ Document ] document The document to delete.

@api private

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 236
def _remove(document)
  _target.delete_one(document)
  _unscoped.delete_one(document)
  update_attributes_hash
  reindex
end
as_document() click to toggle source

Get this association as as its representation in the database.

@example Convert the association to an attributes hash.

person.addresses.as_document

@return [ Array<Hash> ] The association as stored in the db.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 110
def as_document
  as_attributes.collect { |attrs| BSON::Document.new(attrs) }
end
build(attributes = {}, type = nil) { |doc| ... } click to toggle source

Builds a new document in the association and appends it to the target. Takes an optional type if you want to specify a subclass.

@example Build a new document on the association.

person.people.build(:name => "Bozo")

@param [ Hash ] attributes The attributes to build the document with. @param [ Class ] type Optional class to build the document with.

@return [ Document ] The new document.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 138
def build(attributes = {}, type = nil)
  Factory.execute_build(type || _association.klass, attributes, execute_callbacks: false).tap do |doc|
    append(doc)
    doc.apply_post_processed_defaults
    yield doc if block_given?
    doc.run_pending_callbacks
    doc.run_callbacks(:build) { doc }
    _base._reset_memoized_descendants!
  end
end
Also aliased as: new
clear() click to toggle source

Clear the association. Will delete the documents from the db if they are already persisted.

If the host document is not persisted but its _id matches a persisted document, calling clear on an association will remove the association’s documents from the database even though the set of documents in the application (as loaded in the host) is different from what is in the database, and the host may not contain any persisted documents in the association either.

@example Clear the association.

person.addresses.clear

@return [ self ] The empty association.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 165
def clear
  batch_clear(_target.dup)
  update_attributes_hash
  self
end
concat(docs) click to toggle source

Appends an array of documents to the association. Performs a batch insert of the documents instead of persisting one at a time.

@example Concat with other documents.

person.addresses.concat([ address_one, address_two ])

@param [ Array<Document> ] docs The docs to add.

@return [ Array<Document> ] The documents.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 123
def concat(docs)
  batch_insert(docs) unless docs.empty?
  self
end
count(*args, &block) click to toggle source

Returns a count of the number of documents in the association that have actually been persisted to the database.

Use size if you want the total number of documents.

If args or block are present, count will delegate to the count method on target and will include both persisted and non-persisted documents.

@example Get the count of persisted documents.

person.addresses.count

@example Get the count of all documents matching a block.

person.addresses.count { |a| a.country == "FR" }

@example Use persisted? inside block to count persisted documents.

person.addresses.count { |a| a.persisted? && a.country == "FR" }

@param [ Object… ] *args Args to delegate to the target.

@return [ Integer ] The total number of persisted embedded docs, as

flagged by the #persisted? method.
# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 193
def count(*args, &block)
  return _target.count(*args, &block) if args.any? || block

  _target.count(&:persisted?)
end
delete(document) click to toggle source

Delete the supplied document from the target. This method is proxied in order to reindex the array after the operation occurs.

@example Delete the document from the association.

person.addresses.delete(address)

@param [ Document ] document The document to be deleted.

@return [ Document | nil ] The deleted document or nil if nothing deleted.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 208
def delete(document)
  execute_callbacks_around(:remove, document) do
    _target.delete_one(document).tap do |doc|
      if doc && !_binding?
        _unscoped.delete_one(doc)
        if _assigning?
          _base.add_atomic_pull(doc)
        else
          doc.delete(suppress: true)
          unbind_one(doc)
        end
        update_attributes_hash
      end
      reindex
    end
  end
end
Also aliased as: delete_one
delete_all(conditions = {}) click to toggle source

Delete all the documents in the association without running callbacks.

@example Delete all documents from the association.

person.addresses.delete_all

@example Conditionally delete documents from the association.

person.addresses.delete_all({ :street => "Bond" })

@param [ Hash ] conditions Conditions on which documents to delete.

@return [ Integer ] The number of documents deleted.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 254
def delete_all(conditions = {})
  remove_all(conditions, :delete)
end
delete_if() { |doc| ... } click to toggle source

Delete all the documents for which the provided block returns true.

@example Delete the matching documents.

person.addresses.delete_if do |doc|
  doc.state == "GA"
end

@return [ EmbedsMany::Proxy | Enumerator ] The proxy or an

enumerator if no block was provided.
Calls superclass method
# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 267
def delete_if
  return super unless block_given?

  _target.dup.each { |doc| delete(doc) if yield doc }

  self
end
delete_one(document)

Mongoid::Extensions::Array defines Array#delete_one, so we need to make sure that method behaves reasonably on proxies, too.

Alias for: delete
destroy_all(conditions = {}) click to toggle source

Destroy all the documents in the association whilst running callbacks.

@example Destroy all documents from the association.

person.addresses.destroy_all

@example Conditionally destroy documents from the association.

person.addresses.destroy_all({ :street => "Bond" })

@param [ Hash ] conditions Conditions on which documents to destroy.

@return [ Integer ] The number of documents destroyed.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 286
def destroy_all(conditions = {})
  remove_all(conditions, :destroy)
end
exists?(id_or_conditions = :none) click to toggle source

Determine if any documents in this association exist in the database.

@example Are there persisted documents?

person.posts.exists?

@param [ :none | nil | false | Hash | Object ] id_or_conditions

When :none (the default), returns true if any persisted
documents exist in the association. When nil or false, this
will always return false. When a Hash is given, this queries
the documents in the association for those that match the given
conditions, and returns true if any match which have been
persisted. Any other argument is interpreted as an id, and
queries for the existence of persisted documents in the
association with a matching _id.

@return [ true | false ] True if persisted documents exist, false if not.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 306
def exists?(id_or_conditions = :none)
  case id_or_conditions
  when :none then _target.any?(&:persisted?)
  when nil, false then false
  when Hash then where(id_or_conditions).any?(&:persisted?)
  else where(_id: id_or_conditions).any?(&:persisted?)
  end
end
find(...) click to toggle source

Finds a document in this association through several different methods.

This method delegates to +Mongoid::Criteria#find+. If this method is not given a block, it returns one or many documents for the provided _id values.

If this method is given a block, it returns the first document of those found by the current Criteria object for which the block returns a truthy value.

@example Find a document by its id.

person.addresses.find(BSON::ObjectId.new)

@example Find documents for multiple ids.

person.addresses.find([ BSON::ObjectId.new, BSON::ObjectId.new ])

@example Finds the first matching document using a block.

person.addresses.find { |addr| addr.state == 'CA' }

@param [ Object… ] *args Various arguments. @param &block Optional block to pass. @yield [ Object ] Yields each enumerable element to the block.

@return [ Document | Array<Document> | nil ] A document or matching documents.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 340
def find(...)
  criteria.find(...)
end
new(attributes = {}, type = nil)
Alias for: build
pop(count = nil) click to toggle source

Pop documents off the association. This can be a single document or multiples, and will automatically persist the changes.

@example Pop a single document.

relation.pop

@example Pop multiple documents.

relation.pop(3)

@param [ Integer ] count The number of documents to pop, or 1 if not

provided.

@return [ Document | Array<Document> | nil ] The popped document(s).

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 365
def pop(count = nil)
  return [] if count&.zero?

  docs = _target.last(count || 1).each { |doc| delete(doc) }
  (count.nil? || docs.empty?) ? docs.first : docs
end
push(*args)
Alias for: <<
shift(count = nil) click to toggle source

Shift documents off the association. This can be a single document or multiples, and will automatically persist the changes.

@example Shift a single document.

relation.shift

@example Shift multiple documents.

relation.shift(3)

@param [ Integer ] count The number of documents to shift, or 1 if not

provided.

@return [ Document | Array<Document> | nil ] The shifted document(s).

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 385
def shift(count = nil)
  return [] if count&.zero?

  docs = _target.first(count || 1).each { |doc| delete(doc) }
  (count.nil? || docs.empty?) ? docs.first : docs
end
substitute(docs) click to toggle source

Substitutes the supplied target documents for the existing documents in the relation.

@example Substitute the association’s target.

person.addresses.substitute([ address ])

@param [ Array<Document> | Array<Hash> ] docs The replacement docs.

@return [ Many ] The proxied association.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 401
def substitute(docs)
  batch_replace(docs)
  update_attributes_hash
  self
end
unscoped() click to toggle source

Return the association with all previous scoping removed. This is the exact representation of the docs in the database.

@example Get the unscoped documents.

person.addresses.unscoped

@return [ Criteria ] The unscoped association.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 414
def unscoped
  criterion = klass.unscoped
  criterion.embedded = true
  criterion.documents = _unscoped.delete_if(&:marked_for_destruction?)
  criterion
end

Private Instance Methods

append(document) click to toggle source

Appends the document to the target array, updating the index on the document at the same time.

@example Append to the document.

relation.append(document)

@param [ Document ] document The document to append to the target.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 436
def append(document)
  execute_callback :before_add, document
  _target.push(*scope([ document ])) unless object_already_related?(document)
  _unscoped.push(document)
  integrate(document)
  update_attributes_hash
  document._index = _unscoped.size - 1
  execute_callback :after_add, document
end
as_attributes() click to toggle source

Returns a list of attributes hashes for each document.

@return [ Array<Hash> ] The list of attributes hashes

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 560
def as_attributes
  _unscoped.map { |doc| doc.send(:as_attributes) }
end
binding() click to toggle source

Instantiate the binding associated with this association.

@example Create the binding.

relation.binding([ address ])

@return [ Binding ] The many binding.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 452
def binding
  Binding.new(_base, _target, _association)
end
criteria() click to toggle source

Returns the Criteria object for the target class with its documents set to the list of target documents in the association.

@return [ Criteria ] A new criteria.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 460
def criteria
  _association.criteria(_base, _target)
end
integrate(document) click to toggle source

Integrate the document into the association. will set its metadata and attempt to bind the inverse.

@example Integrate the document.

relation.integrate(document)

@param [ Document ] document The document to integrate.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 471
def integrate(document)
  characterize_one(document)
  bind_one(document)
end
method_missing(name, *args, &block) click to toggle source
# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 490
               def method_missing(name, *args, &block)
  return super if _target.respond_to?(name)

  klass.send(:with_scope, criteria) do
    criteria.public_send(name, *args, &block)
  end
end
persistable?() click to toggle source

Are we able to persist this association?

@example Can we persist the association?

relation.persistable?

@return [ true | false ] If the association is persistable.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 505
def persistable?
  _base.persisted? && !_binding?
end
reindex() click to toggle source

Reindex all the target elements. This is useful when performing operations on the proxied target directly and the indices need to match that on the database side.

@example Reindex the association.

person.addresses.reindex
# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 515
def reindex
  _unscoped.each_with_index do |doc, index|
    doc._index = index
  end
end
remove_all(conditions = {}, method = :delete) click to toggle source

Remove all documents from the association, either with a delete or a destroy depending on what this was called through.

@example Destroy documents from the association.

relation.remove_all({ :num => 1 }, true)

@param [ Hash ] conditions Conditions to filter by. @param [ true | false ] method :delete or :destroy.

@return [ Integer ] The number of documents removed.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 549
def remove_all(conditions = {}, method = :delete)
  criteria = where(conditions || {})
  criteria.size.tap do
    batch_remove(criteria, method)
    update_attributes_hash
  end
end
scope(docs) click to toggle source

Apply the association ordering and default scoping (defined on association’s target class) to the provided documents.

@example Apply scoping.

person.addresses.scope(target)

@param [ Array<Document> ] docs The documents to scope.

@return [ Array<Document> ] The scoped docs.

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 530
def scope(docs)
  return docs unless _association.order || _association.klass.default_scoping?

  crit = _association.klass.order_by(_association.order)
  crit.embedded = true
  crit.documents = docs
  crit.entries
end
update_attributes_hash() click to toggle source

Update the _base’s attributes hash with the _target’s attributes

@api private

# File lib/mongoid/association/embedded/embeds_many/proxy.rb, line 567
def update_attributes_hash
  if _target.empty?
    _base.attributes.delete(_association.store_as)
  else
    _base.attributes.merge!(_association.store_as => _target.map(&:attributes))
  end
end