class Mongoid::Association::Referenced::HasMany::Proxy

This class defines the behavior for all associations that are a one-to-many between documents in different collections.

Public Class Methods

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

Instantiate a new references_many association. Will set the foreign key and the base on the inverse object.

@example Create the new association.

Referenced::Many.new(base, target, association)

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

@since 2.0.0.beta.1

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 222
def initialize(base, target, association)
  enum = HasMany::Enumerable.new(target, base, association)
  init(base, enum, association) do
    raise_mixed if klass.embedded? && !klass.cyclic?
  end
end

Private Class Methods

eager_loader(association, docs) click to toggle source
# File lib/mongoid/association/referenced/has_many/proxy.rb, line 567
def eager_loader(association, docs)
  Eager.new(association, docs)
end
embedded?() click to toggle source

Returns true if the association is an embedded one. In this case always false.

@example Is this association embedded?

Referenced::Many.embedded?

@return [ false ] Always false.

@since 2.0.0.rc.1

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 580
def embedded?
  false
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.posts << post

@example Push a document.

person.posts.push(post)

@example Concat with other documents.

person.posts.concat([ post_one, post_two ])

@param [ Document, Array<Document> ] args Any number of documents.

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

@since 2.0.0.beta.1

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 34
def <<(*args)
  docs = args.flatten
  return concat(docs) if docs.size > 1
  if doc = docs.first
    append(doc)
    doc.save if persistable? && !_assigning? && !doc.validated?
  end
  self
end
Also aliased as: push
build(attributes = {}, type = nil) { |doc| ... } click to toggle source

Build a new document from the attributes and append it to this association without saving.

@example Build a new document on the association.

person.posts.build(:title => "A new post")

@param [ Hash ] attributes The attributes of the new document. @param [ Class ] type The optional subclass to build.

@return [ Document ] The new document.

@since 2.0.0.beta.1

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 80
def build(attributes = {}, type = nil)
  doc = Factory.build(type || klass, attributes)
  append(doc)
  doc.apply_post_processed_defaults
  yield(doc) if block_given?
  doc.run_callbacks(:build) { doc }
  doc
end
Also aliased as: new
clear()
Alias for: purge
concat(documents) 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.posts.concat([ post_one, post_two ])

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

@return [ Array<Document> ] The documents.

@since 2.4.0

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 57
def concat(documents)
  docs, inserts = [], []
  documents.each do |doc|
    next unless doc
    append(doc)
    save_or_delay(doc, docs, inserts) if persistable?
  end
  persist_delayed(docs, inserts)
  self
end
delete(document) click to toggle source

Delete the document from the association. This will set the foreign key on the document to nil. If the dependent options on the association are :delete_all or :destroy the appropriate removal will occur.

@example Delete the document.

person.posts.delete(post)

@param [ Document ] document The document to remove.

@return [ Document ] The matching document.

@since 2.1.0

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 103
def delete(document)
  execute_callback :before_remove, document
  _target.delete(document) do |doc|
    if doc
      unbind_one(doc)
      cascade!(doc) if !_assigning?
    end
    execute_callback :after_remove, doc
  end
end
delete_all(conditions = nil) click to toggle source

Deletes all related documents from the database given the supplied conditions.

@example Delete all documents in the association.

person.posts.delete_all

@example Conditonally delete all documents in the association.

person.posts.delete_all({ :title => "Testing" })

@param [ Hash ] conditions Optional conditions to delete with.

@return [ Integer ] The number of documents deleted.

@since 2.0.0.beta.1

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 128
def delete_all(conditions = nil)
  remove_all(conditions, :delete_all)
end
destroy_all(conditions = nil) click to toggle source

Destroys all related documents from the database given the supplied conditions.

@example Destroy all documents in the association.

person.posts.destroy_all

@example Conditonally destroy all documents in the association.

person.posts.destroy_all({ :title => "Testing" })

@param [ Hash ] conditions Optional conditions to destroy with.

@return [ Integer ] The number of documents destroyd.

@since 2.0.0.beta.1

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 146
def destroy_all(conditions = nil)
  remove_all(conditions, :destroy_all)
end
each() { |doc| ... } click to toggle source

Iterate over each document in the association and yield to the provided block.

@note This will load the entire association into memory.

@example Iterate over the documents.

person.posts.each do |post|
  post.save
end

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

@since 2.1.0

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 163
def each
  if block_given?
    _target.each { |doc| yield(doc) }
  else
    to_enum
  end
end
exists?() click to toggle source

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

If the association contains documents but all of the documents exist only in the application, i.e. have not been persisted to the database, this method returns false.

This method queries the database on each invocation even if the association is already loaded into memory.

@example Are there persisted documents?

person.posts.exists?

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

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 184
def exists?
  criteria.exists?
end
find(*args) click to toggle source

Find the matchind document on the association, either based on id or conditions.

@example Find by an id.

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

@example Find by multiple ids.

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

@note This will keep matching documents in memory for iteration

later.

@param [ BSON::ObjectId, Array<BSON::ObjectId> ] args The ids.

@return [ Document, Criteria ] The matching document(s).

@since 2.0.0.beta.1

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 205
def find(*args)
  matching = criteria.find(*args)
  Array(matching).each { |doc| _target.push(doc) }
  matching
end
new(attributes = {}, type = nil)
Alias for: build
nullify() click to toggle source

Removes all associations between the base document and the target documents by deleting the foreign keys and the references, orphaning the target documents in the process.

@example Nullify the association.

person.posts.nullify

@since 2.0.0.rc.1

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 237
def nullify
  criteria.update_all(foreign_key => nil)
  _target.clear do |doc|
    unbind_one(doc)
    doc.changed_attributes.delete(foreign_key)
  end
end
Also aliased as: nullify_all
nullify_all()
Alias for: nullify
purge() click to toggle source

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

@example Clear the association.

person.posts.clear

@return [ Many ] The association emptied.

@since 2.0.0.beta.1

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 256
def purge
  unless _association.destructive?
    nullify
  else
    after_remove_error = nil
    criteria.delete_all
    many = _target.clear do |doc|
      execute_callback :before_remove, doc
      unbind_one(doc)
      doc.destroyed = true
      begin
        execute_callback :after_remove, doc
      rescue => e
        after_remove_error = e
      end
    end
    raise after_remove_error if after_remove_error
    many
  end
end
Also aliased as: clear
push(*args)
Alias for: <<
substitute(replacement) click to toggle source

Substitutes the supplied target documents for the existing documents in the association. If the new target is nil, perform the necessary deletion.

@example Replace the association.

person.posts.substitute([ new_post ])

@param [ Array<Document> ] replacement The replacement target.

@return [ Many ] The association.

@since 2.0.0.rc.1

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 291
def substitute(replacement)
  if replacement
    new_docs, docs = replacement.compact, []
    new_ids = new_docs.map { |doc| doc._id }
    remove_not_in(new_ids)
    new_docs.each do |doc|
      docs.push(doc) if doc.send(foreign_key) != _base.send(_association.primary_key)
    end
    concat(docs)
  else
    purge
  end
  self
end
unscoped() click to toggle source

Get a criteria for the documents without the default scoping applied.

@example Get the unscoped criteria.

person.posts.unscoped

@return [ Criteria ] The unscoped criteria.

@since 2.4.0

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 315
def unscoped
  klass.unscoped.where(foreign_key => _base.send(_association.primary_key))
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 the document to the association.

relation.append(document)

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

@since 2.0.0.rc.1

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 330
def append(document)
  with_add_callbacks(document, already_related?(document)) do
    _target.push(document)
    characterize_one(document)
    bind_one(document)
  end
end
binding() click to toggle source

Instantiate the binding associated with this association.

@example Get the binding.

relation.binding([ address ])

@return [ Binding ] The binding.

@since 2.0.0.rc.1

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 381
def binding
  HasMany::Binding.new(_base, _target, _association)
end
cascade!(document) click to toggle source

Perform the necessary cascade operations for documents that just got deleted or nullified.

@example Cascade the change.

relation.cascade!(document)

@param [ Document ] document The document to cascade on.

@return [ true, false ] If the association is destructive.

@since 2.1.0

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 421
def cascade!(document)
  if persistable?
    case _association.dependent
    when :delete_all
      document.delete
    when :destroy
      document.destroy
    else
      document.save
    end
  end
end
collection() click to toggle source

Get the collection of the association in question.

@example Get the collection of the association.

relation.collection

@return [ Collection ] The collection of the association.

@since 2.0.2

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 393
def collection
  klass.collection
end
criteria() click to toggle source

Returns the criteria object for the target class with its documents set to target.

@example Get a criteria for the association.

relation.criteria

@return [ Criteria ] A new criteria.

@since 2.0.0.beta.1

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 406
def criteria
  @criteria ||= _association.criteria(_base)
end
method_missing(name, *args, &block) click to toggle source
# File lib/mongoid/association/referenced/has_many/proxy.rb, line 446
               def method_missing(name, *args, &block)
  if _target.respond_to?(name)
    _target.send(name, *args, &block)
  else
    klass.send(:with_scope, criteria) do
      criteria.public_send(name, *args, &block)
    end
  end
end
persist_delayed(docs, inserts) click to toggle source

Persist all the delayed batch inserts.

@api private

@example Persist the delayed batch inserts.

relation.persist_delayed([ doc ])

@param [ Array<Document> ] docs The delayed inserts. @param [ Array<Hash> ] inserts The raw insert document.

@since 3.0.0

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 467
def persist_delayed(docs, inserts)
  unless docs.empty?
    collection.insert_many(inserts, session: _session)
    docs.each do |doc|
      doc.new_record = false
      doc.run_after_callbacks(:create, :save) unless _association.autosave?
      doc.post_persist
    end
  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.

@since 2.1.0

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 486
def persistable?
  !_binding? && (_creating? || _base.persisted? && !_building?)
end
remove_all(conditions = nil, method = :delete_all) click to toggle source

Deletes all related documents from the database given the supplied conditions.

@example Delete all documents in the association.

person.posts.delete_all

@example Conditonally delete all documents in the association.

person.posts.delete_all({ :title => "Testing" })

@param [ Hash ] conditions Optional conditions to delete with. @param [ Symbol ] method The deletion method to call.

@return [ Integer ] The number of documents deleted.

@since 2.1.0

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 505
def remove_all(conditions = nil, method = :delete_all)
  selector = conditions || {}
  removed = klass.send(method, selector.merge!(criteria.selector))
  _target.delete_if do |doc|
    if doc._matches?(selector)
      unbind_one(doc) and true
    end
  end
  removed
end
remove_not_in(ids) click to toggle source

Remove all the documents in the proxy that do not have the provided ids.

@example Remove all documents without the ids.

proxy.remove_not_in([ id ])

@param [ Array<Object> ] ids The ids.

@since 2.4.0

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 525
def remove_not_in(ids)
  removed = criteria.not_in(_id: ids)
  if _association.destructive?
    removed.delete_all
  else
    removed.update_all(foreign_key => nil)
  end
  in_memory.each do |doc|
    if !ids.include?(doc._id)
      unbind_one(doc)
      _target.delete(doc)
      if _association.destructive?
        doc.destroyed = true
      end
    end
  end
end
save_or_delay(doc, docs, inserts) click to toggle source

Save a persisted document immediately or delay a new document for batch insert.

@api private

@example Save or delay the document.

relation.save_or_delay(doc, [])

@param [ Document ] doc The document. @param [ Array<Document> ] inserts The inserts.

@since 3.0.0

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 555
def save_or_delay(doc, docs, inserts)
  if doc.new_record? && doc.valid?(:create)
    doc.run_before_callbacks(:save, :create)
    docs.push(doc)
    inserts.push(doc.send(:as_attributes))
  else
    doc.save
  end
end
with_add_callbacks(document, already_related) { || ... } click to toggle source

Execute before/after add callbacks around the block unless the objects already have a persisted association.

@example Execute before/after add callbacks around the block.

relation.with_add_callbacks(document, false)

@param [ Document ] document The document to append to the target. @param [ true, false ] already_related Whether the document is already related

to the target.

@since 5.1.0

# File lib/mongoid/association/referenced/has_many/proxy.rb, line 349
def with_add_callbacks(document, already_related)
  execute_callback :before_add, document unless already_related
  yield
  execute_callback :after_add, document unless already_related
end