class Mongoid::Relations::Referenced::ManyToMany

This class defines the behaviour for all relations that are a many-to-many between documents in different collections.

Private Class Methods

builder(base, meta, object) click to toggle source

Return the builder that is responsible for generating the documents that will be used by this relation.

@example Get the builder.

Referenced::ManyToMany.builder(meta, object)

@param [ Document ] base The base document. @param [ Metadata ] meta The metadata of the relation. @param [ Document, Hash ] object A document or attributes to build

with.

@return [ Builder ] A new builder object.

@since 2.0.0.rc.1

# File lib/mongoid/relations/referenced/many_to_many.rb, line 288
def builder(base, meta, object)
  Builders::Referenced::ManyToMany.new(base, meta, object)
end
criteria(metadata, object, type = nil) click to toggle source

Create the standard criteria for this relation given the supplied metadata and object.

@example Get the criteria.

Proxy.criteria(meta, object)

@param [ Metadata ] metadata The relation metadata. @param [ Object ] object The object for the criteria. @param [ Class ] type The criteria class.

@return [ Criteria ] The criteria.

@since 2.1.0

# File lib/mongoid/relations/referenced/many_to_many.rb, line 305
def criteria(metadata, object, type = nil)
  apply_ordering(
    metadata.klass.all_of(
      metadata.primary_key => { "$in" => object || [] }
    ), metadata
  )
end
eager_load_klass() click to toggle source
# File lib/mongoid/relations/referenced/many_to_many.rb, line 313
def eager_load_klass
  Relations::Eager::HasAndBelongsToMany
end
embedded?() click to toggle source

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

@example Is this relation embedded?

Referenced::ManyToMany.embedded?

@return [ false ] Always false.

@since 2.0.0.rc.1

# File lib/mongoid/relations/referenced/many_to_many.rb, line 326
def embedded?
  false
end
foreign_key(name) click to toggle source

Get the foreign key for the provided name.

@example Get the foreign key.

Referenced::ManyToMany.foreign_key(:person)

@param [ Symbol ] name The name.

@return [ String ] The foreign key.

@since 3.0.0

# File lib/mongoid/relations/referenced/many_to_many.rb, line 340
def foreign_key(name)
  "#{name.to_s.singularize}#{foreign_key_suffix}"
end
foreign_key_default() click to toggle source

Get the default value for the foreign key.

@example Get the default.

Referenced::ManyToMany.foreign_key_default

@return [ Array ] Always an empty array.

@since 2.0.0.rc.1

# File lib/mongoid/relations/referenced/many_to_many.rb, line 352
def foreign_key_default
  []
end
foreign_key_suffix() click to toggle source

Returns the suffix of the foreign key field, either “_id” or “_ids”.

@example Get the suffix for the foreign key.

Referenced::ManyToMany.foreign_key_suffix

@return [ String ] “_ids”

@since 2.0.0.rc.1

# File lib/mongoid/relations/referenced/many_to_many.rb, line 364
def foreign_key_suffix
  "_ids"
end
macro() click to toggle source

Returns the macro for this relation. Used mostly as a helper in reflection.

@example Get the macro.

Referenced::ManyToMany.macro

@return [ Symbol ] :has_and_belongs_to_many

# File lib/mongoid/relations/referenced/many_to_many.rb, line 375
def macro
  :has_and_belongs_to_many
end
nested_builder(metadata, attributes, options) click to toggle source

Return the nested builder that is responsible for generating the documents that will be used by this relation.

@example Get the nested builder.

Referenced::ManyToMany.builder(attributes, options)

@param [ Metadata ] metadata The relation metadata. @param [ Hash ] attributes The attributes to build with. @param [ Hash ] options The options for the builder.

@option options [ true, false ] :allow_destroy Can documents be

deleted?

@option options [ Integer ] :limit Max number of documents to

create at once.

@option options [ Proc, Symbol ] :reject_if If documents match this

option then they are ignored.

@option options [ true, false ] :update_only Only existing documents

can be modified.

@return [ NestedBuilder ] A newly instantiated nested builder object.

@since 2.0.0.rc.1

# File lib/mongoid/relations/referenced/many_to_many.rb, line 401
def nested_builder(metadata, attributes, options)
  Builders::NestedAttributes::Many.new(metadata, attributes, options)
end
path(document) click to toggle source

Get the path calculator for the supplied document.

@example Get the path calculator.

Proxy.path(document)

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

@return [ Root ] The root atomic path calculator.

@since 2.1.0

# File lib/mongoid/relations/referenced/many_to_many.rb, line 415
def path(document)
  Mongoid::Atomic::Paths::Root.new(document)
end
stores_foreign_key?() click to toggle source

Tells the caller if this relation is one that stores the foreign key on its own objects.

@example Does this relation store a foreign key?

Referenced::Many.stores_foreign_key?

@return [ true ] Always true.

@since 2.0.0.rc.1

# File lib/mongoid/relations/referenced/many_to_many.rb, line 428
def stores_foreign_key?
  true
end
valid_options() click to toggle source

Get the valid options allowed with this relation.

@example Get the valid options.

Relation.valid_options

@return [ Array<Symbol> ] The valid options.

@since 2.1.0

# File lib/mongoid/relations/referenced/many_to_many.rb, line 440
def valid_options
  [
    :after_add,
    :after_remove,
    :autosave,
    :before_add,
    :before_remove,
    :dependent,
    :foreign_key,
    :index,
    :order,
    :primary_key
  ]
end
validation_default() click to toggle source

Get the default validation setting for the relation. Determines if by default a validates associated will occur.

@example Get the validation default.

Proxy.validation_default

@return [ true, false ] The validation default.

@since 2.1.9

# File lib/mongoid/relations/referenced/many_to_many.rb, line 464
def validation_default
  true
end

Public Instance Methods

<<(*args) click to toggle source

Appends a document or array of documents to the relation. 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/relations/referenced/many_to_many.rb, line 26
def <<(*args)
  docs = args.flatten
  return concat(docs) if docs.size > 1
  if doc = docs.first
    append(doc)
    base.add_to_set(foreign_key => doc.send(__metadata.primary_key))
    if child_persistable?(doc)
      doc.save
    end
  end
  unsynced(base, foreign_key) and 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 relation without saving.

@example Build a new document on the relation.

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

@overload build(attributes = {}, type = nil)

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

@overload build(attributes = {}, type = nil)

@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/relations/referenced/many_to_many.rb, line 90
def build(attributes = {}, type = nil)
  doc = Factory.build(type || klass, attributes)
  base.send(foreign_key).push(doc.id)
  append(doc)
  doc.apply_post_processed_defaults
  unsynced(doc, inverse_foreign_key)
  yield(doc) if block_given?
  doc
end
Also aliased as: new
clear()
Alias for: nullify
concat(documents) click to toggle source

Appends an array of documents to the relation. 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/relations/referenced/many_to_many.rb, line 51
def concat(documents)
  ids, docs, inserts = {}, [], []
  documents.each do |doc|
    next unless doc
    append(doc)
    if persistable? || _creating?
      ids[doc.id] = true
      save_or_delay(doc, docs, inserts)
    else
      existing = base.send(foreign_key)
      unless existing.include?(doc.id)
        existing.push(doc.id) and unsynced(base, foreign_key)
      end
    end
  end
  if persistable? || _creating?
    base.push(foreign_key => ids.keys)
  end
  persist_delayed(docs, inserts)
  self
end
delete(document) click to toggle source

Delete the document from the relation. This will set the foreign key on the document to nil. If the dependent options on the relation are :delete 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/relations/referenced/many_to_many.rb, line 113
def delete(document)
  doc = super
  if doc && persistable?
    base.pull(foreign_key => doc.send(__metadata.primary_key))
    target._unloaded = criteria
    unsynced(base, foreign_key)
  end
  doc
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 relation.

person.preferences.nullify

@since 2.0.0.rc.1

# File lib/mongoid/relations/referenced/many_to_many.rb, line 131
def nullify
  target.each do |doc|
    execute_callback :before_remove, doc
  end
  unless __metadata.forced_nil_inverse?
    criteria.pull(inverse_foreign_key => base.id)
  end
  if persistable?
    base.set(foreign_key => base.send(foreign_key).clear)
  end
  after_remove_error = nil
  many_to_many = target.clear do |doc|
    unbind_one(doc)
    unless __metadata.forced_nil_inverse?
      doc.changed_attributes.delete(inverse_foreign_key)
    end
    begin
      execute_callback :after_remove, doc
    rescue => e
      after_remove_error = e
    end
  end
  raise after_remove_error if after_remove_error
  many_to_many
end
Also aliased as: nullify_all, clear, purge
nullify_all()
Alias for: nullify
purge()
Alias for: nullify
push(*args)
Alias for: <<
substitute(replacement) click to toggle source

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

@example Replace the relation. person.preferences.substitute([ new_post ])

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

@return [ Many ] The relation.

@since 2.0.0.rc.1

# File lib/mongoid/relations/referenced/many_to_many.rb, line 172
def substitute(replacement)
  purge
  unless replacement.blank?
    push(replacement.compact.uniq)
  else
    reset_unloaded
  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.preferences.unscoped

@return [ Criteria ] The unscoped criteria.

@since 2.4.0

# File lib/mongoid/relations/referenced/many_to_many.rb, line 191
def unscoped
  klass.unscoped.any_in(_id: base.send(foreign_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 relation.

relation.append(document)

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

@since 2.0.0.rc.1

# File lib/mongoid/relations/referenced/many_to_many.rb, line 206
def append(document)
  execute_callback :before_add, document
  target.push(document)
  characterize_one(document)
  bind_one(document)
  execute_callback :after_add, document
end
binding() click to toggle source

Instantiate the binding associated with this relation.

@example Get the binding.

relation.binding([ address ])

@return [ Binding ] The binding.

@since 2.0.0.rc.1

# File lib/mongoid/relations/referenced/many_to_many.rb, line 222
def binding
  Bindings::Referenced::ManyToMany.new(base, target, __metadata)
end
child_persistable?(doc) click to toggle source

Determine if the child document should be persisted.

@api private

@example Is the child persistable?

relation.child_persistable?(doc)

@param [ Document ] doc The document.

@return [ true, false ] If the document can be persisted.

@since 3.0.20

# File lib/mongoid/relations/referenced/many_to_many.rb, line 238
def child_persistable?(doc)
  (persistable? || _creating?) &&
    !(doc.persisted? && __metadata.forced_nil_inverse?)
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 relation.

relation.criteria

@return [ Criteria ] A new criteria.

# File lib/mongoid/relations/referenced/many_to_many.rb, line 250
def criteria
  ManyToMany.criteria(__metadata, base.send(foreign_key))
end
unsynced(doc, key) click to toggle source

Flag the base as unsynced with respect to the foreign key.

@api private

@example Flag as unsynced.

relation.unsynced(doc, :preference_ids)

@param [ Document ] doc The document to flag. @param [ Symbol ] key The key to flag on the document.

@return [ true ] true.

@since 3.0.0

# File lib/mongoid/relations/referenced/many_to_many.rb, line 267
def unsynced(doc, key)
  doc.synced[key] = false
  true
end