Class: Mongoid::Association::Embedded::EmbedsMany::Proxy

Inherits:
Many
  • Object
show all
Extended by:
ClassMethods
Includes:
Batchable
Defined in:
lib/mongoid/association/embedded/embeds_many/proxy.rb

Overview

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.

Defined Under Namespace

Modules: ClassMethods

Constant Summary

Constants inherited from Proxy

Proxy::KEEPER_METHODS

Instance Attribute Summary

Attributes inherited from Proxy

#_association, #_base, #_target

Instance Method Summary collapse

Methods included from ClassMethods

eager_loader, embedded?, foreign_key_suffix

Methods included from Batchable

#batch_clear, #batch_insert, #batch_remove, #batch_replace

Methods included from Positional

#positionally

Methods inherited from Many

#blank?, #create, #create!, #find_or_create_by, #find_or_create_by!, #find_or_initialize_by, #nil?, #respond_to?, #scoped, #serializable_hash

Methods inherited from Proxy

apply_ordering, #extend_proxies, #klass, #reset_unloaded, #substitutable

Methods included from Marshalable

#marshal_dump, #marshal_load

Constructor Details

#initialize(base, target, association) ⇒ Many

Instantiate a new embeds_many association.

Examples:

Create the new association.

Many.new(person, addresses, association)

Parameters:



67
68
69
70
71
72
73
74
75
76
77
# 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

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missingCriteria | Object (private)

If the target array does not respond to the supplied method then try to find a named scope or criteria on the class and send the call there.

If the method exists on the array, use the default proxy behavior.

TODO: make sure we are consistingly using respond_to_missing

anywhere we define method_missing.

rubocop:disable Style/MissingRespondToMissing

Parameters:

  • name (Symbol | String)

    The name of the method.

  • *args (Object...)

    The method args.

  • &block

    Optional block to pass.

Returns:

  • (Criteria | Object)

    A Criteria or return value from the target.



490
491
492
493
494
495
496
# File 'lib/mongoid/association/embedded/embeds_many/proxy.rb', line 490

ruby2_keywords 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

Instance Method Details

#<<(*args) ⇒ Object Also known as: push

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

Examples:

Append a document.

person.addresses << address

Push a document.

person.addresses.push(address)

Parameters:

  • *args (Document...)

    Any number of documents.



89
90
91
92
93
94
95
96
97
98
99
100
# 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

#_remove(document) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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

Parameters:

  • document (Document)

    The document to delete.



236
237
238
239
240
241
# 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_documentArray<Hash>

Get this association as as its representation in the database.

Examples:

Convert the association to an attributes hash.

person.addresses.as_document

Returns:

  • (Array<Hash>)

    The association as stored in the db.



110
111
112
# 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) ⇒ Document Also known as: new

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

Examples:

Build a new document on the association.

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

Parameters:

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

    The attributes to build the document with.

  • type (Class) (defaults to: nil)

    Optional class to build the document with.

Returns:



138
139
140
141
142
143
144
145
146
147
# 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

#clearself

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.

Examples:

Clear the association.

person.addresses.clear

Returns:

  • (self)

    The empty association.



165
166
167
168
169
# File 'lib/mongoid/association/embedded/embeds_many/proxy.rb', line 165

def clear
  batch_clear(_target.dup)
  update_attributes_hash
  self
end

#concat(docs) ⇒ Array<Document>

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

Examples:

Concat with other documents.

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

Parameters:

  • docs (Array<Document>)

    The docs to add.

Returns:



123
124
125
126
# 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) ⇒ Integer

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.

Examples:

Get the count of persisted documents.

person.addresses.count

Get the count of all documents matching a block.

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

Use #persisted? inside block to count persisted documents.

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

Parameters:

  • *args (Object...)

    Args to delegate to the target.

Returns:

  • (Integer)

    The total number of persisted embedded docs, as flagged by the #persisted? method.



193
194
195
196
197
# 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) ⇒ Document | nil Also known as: delete_one

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

Examples:

Delete the document from the association.

person.addresses.delete(address)

Parameters:

  • document (Document)

    The document to be deleted.

Returns:

  • (Document | nil)

    The deleted document or nil if nothing deleted.



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# 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

#delete_all(conditions = {}) ⇒ Integer

Delete all the documents in the association without running callbacks.

Examples:

Delete all documents from the association.

person.addresses.delete_all

Conditionally delete documents from the association.

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

Parameters:

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

    Conditions on which documents to delete.

Returns:

  • (Integer)

    The number of documents deleted.



254
255
256
# File 'lib/mongoid/association/embedded/embeds_many/proxy.rb', line 254

def delete_all(conditions = {})
  remove_all(conditions, :delete)
end

#delete_ifEmbedsMany::Proxy | Enumerator

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

Examples:

Delete the matching documents.

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

Returns:

  • (EmbedsMany::Proxy | Enumerator)

    The proxy or an enumerator if no block was provided.



267
268
269
270
271
272
273
# 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

#destroy_all(conditions = {}) ⇒ Integer

Destroy all the documents in the association whilst running callbacks.

Examples:

Destroy all documents from the association.

person.addresses.destroy_all

Conditionally destroy documents from the association.

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

Parameters:

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

    Conditions on which documents to destroy.

Returns:

  • (Integer)

    The number of documents destroyed.



286
287
288
# 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) ⇒ true | false

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

Examples:

Are there persisted documents?

person.posts.exists?

Parameters:

  • id_or_conditions (:none | nil | false | Hash | Object) (defaults to: :none)

    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.

Returns:

  • (true | false)

    True if persisted documents exist, false if not.



306
307
308
309
310
311
312
313
# 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 {|Object| ... } ⇒ Document | Array<Document> | nil

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.

Examples:

Find a document by its id.

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

Find documents for multiple ids.

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

Finds the first matching document using a block.

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

Parameters:

  • *args (Object...)

    Various arguments.

  • &block

    Optional block to pass.

Yields:

  • (Object)

    Yields each enumerable element to the block.

Returns:



340
341
342
# File 'lib/mongoid/association/embedded/embeds_many/proxy.rb', line 340

def find(...)
  criteria.find(...)
end

#pop(count = nil) ⇒ Document | Array<Document> | nil

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

Examples:

Pop a single document.

relation.pop

Pop multiple documents.

relation.pop(3)

Parameters:

  • count (Integer) (defaults to: nil)

    The number of documents to pop, or 1 if not provided.

Returns:



365
366
367
368
369
370
# 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

#shift(count = nil) ⇒ Document | Array<Document> | nil

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

Examples:

Shift a single document.

relation.shift

Shift multiple documents.

relation.shift(3)

Parameters:

  • count (Integer) (defaults to: nil)

    The number of documents to shift, or 1 if not provided.

Returns:



385
386
387
388
389
390
# 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) ⇒ Many

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

Examples:

Substitute the association’s target.

person.addresses.substitute([ address ])

Parameters:

  • docs (Array<Document> | Array<Hash>)

    The replacement docs.

Returns:

  • (Many)

    The proxied association.



401
402
403
404
405
# File 'lib/mongoid/association/embedded/embeds_many/proxy.rb', line 401

def substitute(docs)
  batch_replace(docs)
  update_attributes_hash
  self
end

#unscopedCriteria

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

Examples:

Get the unscoped documents.

person.addresses.unscoped

Returns:

  • (Criteria)

    The unscoped association.



414
415
416
417
418
419
# 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