lib/mongoid/relations/targets/enumerable.rb in mongoid-2.8.1 vs lib/mongoid/relations/targets/enumerable.rb in mongoid-3.0.0.rc
- old
+ new
@@ -1,24 +1,24 @@
# encoding: utf-8
-module Mongoid #:nodoc:
- module Relations #:nodoc:
- module Targets #:nodoc:
+module Mongoid
+ module Relations
+ module Targets
# This class is the wrapper for all relational associations that have a
- # target that can be a criteria or array of loaded documents. This
+ # target that can be a criteria or array of _loaded documents. This
# handles both cases or a combination of the two.
class Enumerable
include ::Enumerable
# The three main instance variables are collections of documents.
#
- # @attribute [rw] added Documents that have been appended.
- # @attribute [rw] loaded Persisted documents that have been loaded.
- # @attribute [rw] unloaded A criteria representing persisted docs.
- attr_accessor :added, :loaded, :unloaded
+ # @attribute [rw] _added Documents that have been appended.
+ # @attribute [rw] _loaded Persisted documents that have been _loaded.
+ # @attribute [rw] _unloaded A criteria representing persisted docs.
+ attr_accessor :_added, :_loaded, :_unloaded
- delegate :===, :is_a?, :kind_of?, :to => :added
+ delegate :===, :is_a?, :kind_of?, to: []
# Check if the enumerable is equal to the other object.
#
# @example Check equality.
# enumerable == []
@@ -42,11 +42,12 @@
#
# @return [ Document ] The document.
#
# @since 2.1.0
def <<(document)
- added.push(document)
+ _added[document.id] = document
+ self
end
alias :push :<<
# Clears out all the documents in this enumerable. If passed a block it
# will yield to each document that is in memory.
@@ -57,18 +58,18 @@
# @example Clear out the enumerable with a block.
# enumerable.clear do |doc|
# doc.unbind
# end
#
- # @return [ Array<Document> ] The cleared out added docs.
+ # @return [ Array<Document> ] The cleared out _added docs.
#
# @since 2.1.0
def clear
if block_given?
in_memory { |doc| yield(doc) }
end
- loaded.clear and added.clear
+ _loaded.clear and _added.clear
end
# Clones each document in the enumerable.
#
# @note This loads all documents into memory.
@@ -92,19 +93,19 @@
#
# @return [ Document ] The deleted document.
#
# @since 2.1.0
def delete(document)
- (loaded.delete(document) || added.delete(document)).tap do |doc|
- unless doc
- if unloaded && unloaded.where(:_id => document.id).exists?
- yield(document) if block_given?
- return document
- end
+ 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
- yield(doc) if block_given?
end
+ yield(doc) if block_given?
+ doc
end
# Deletes every document in the enumerable for where the block returns
# true.
#
@@ -118,66 +119,68 @@
# @return [ Array<Document> ] The remaining docs.
#
# @since 2.1.0
def delete_if(&block)
load_all!
- tap do
- loaded.delete_if(&block)
- added.delete_if(&block)
+ deleted = in_memory.select(&block)
+ deleted.each do |doc|
+ _loaded.delete(doc.id)
+ _added.delete(doc.id)
end
+ self
end
# 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 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
+ # 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.
+ # _added docs.
#
# @example Iterate over the enumerable.
# enumerable.each do |doc|
# puts doc
# end
#
- # @return [ true ] That the enumerable is now loaded.
+ # @return [ true ] That the enumerable is now _loaded.
#
# @since 2.1.0
def each
- if loaded?
- loaded.each do |doc|
+ if _loaded?
+ _loaded.each_pair do |id, doc|
yield(doc)
end
else
- unloaded.each do |doc|
- document = added.delete_one(doc) || loaded.delete_one(doc) || doc
+ _unloaded.each do |doc|
+ document = _added.delete(doc.id) || _loaded.delete(doc.id) || doc
yield(document)
- loaded.push(document)
+ _loaded[document.id] = document
end
end
- added.each do |doc|
+ _added.each_pair do |id, doc|
yield(doc)
end
@executed = true
end
# Is the enumerable empty? Will determine if the count is zero based on
- # whether or not it is loaded.
+ # whether or not it is _loaded.
#
# @example Is the enumerable empty?
# enumerable.empty?
#
# @return [ true, false ] If the enumerable is empty.
#
# @since 2.1.0
def empty?
- if loaded?
+ if _loaded?
in_memory.count == 0
else
- unloaded.count + added.count == 0
+ _unloaded.count + _added.count == 0
end
end
# Get the first document in the enumerable. Will check the persisted
# documents first. Does not load the entire enumerable.
@@ -187,11 +190,11 @@
#
# @return [ Document ] The first document found.
#
# @since 2.1.0
def first
- added.first || (loaded? ? loaded.first : unloaded.first)
+ matching_document(:first)
end
# Initialize the new enumerable either with a criteria or an array.
#
# @example Initialize the enumerable with a criteria.
@@ -203,16 +206,35 @@
# @param [ Criteria, Array<Document> ] target The wrapped object.
#
# @since 2.1.0
def initialize(target)
if target.is_a?(Criteria)
- @added, @loaded, @unloaded = [], [], target
+ @_added, @executed, @_loaded, @_unloaded = {}, false, {}, target
else
- @added, @executed, @loaded = [], true, target
+ @_added, @executed = {}, true
+ @_loaded = target.inject({}) do |_target, doc|
+ _target[doc.id] = doc
+ _target
+ end
end
end
+ # Does the target include the provided document?
+ #
+ # @example Does the target include the document?
+ # enumerable.include?(document)
+ #
+ # @param [ Document ] doc The document to check.
+ #
+ # @return [ true, false ] If the document is in the target.
+ #
+ # @since 3.0.0
+ def include?(doc)
+ return super unless _unloaded
+ _unloaded.where(_id: doc.id).exists? || _added.has_key?(doc.id)
+ end
+
# Inspection will just inspect the entries for nice array-style
# printing.
#
# @example Inspect the enumerable.
# enumerable.inspect
@@ -222,25 +244,25 @@
# @since 2.1.0
def inspect
entries.inspect
end
- # Return all the documents in the enumerable that have been loaded or
- # added.
+ # Return all the documents in the enumerable that have been _loaded or
+ # _added.
#
# @note When passed a block it yields to each document.
#
# @example Get the in memory docs.
# enumerable.in_memory
#
# @return [ Array<Document> ] The in memory docs.
#
# @since 2.1.0
def in_memory
- (loaded + added).tap do |docs|
- docs.each { |doc| yield(doc) } if block_given?
- end
+ docs = (_loaded.values + _added.values)
+ docs.each { |doc| yield(doc) } if block_given?
+ docs
end
# Get the last document in the enumerable. Will check the new
# documents first. Does not load the entire enumerable.
#
@@ -249,33 +271,33 @@
#
# @return [ Document ] The last document found.
#
# @since 2.1.0
def last
- added.last || (loaded? ? loaded.last : unloaded.last)
+ matching_document(:last)
end
# Loads all the documents in the enumerable from the database.
#
# @example Load all the documents.
# enumerable.load_all!
#
- # @return [ true ] That the enumerable is loaded.
+ # @return [ true ] That the enumerable is _loaded.
#
# @since 2.1.0
alias :load_all! :entries
- # Has the enumerable been loaded? This will be true if the criteria has
+ # Has the enumerable been _loaded? This will be true if the criteria has
# been executed or we manually load the entire thing.
#
- # @example Is the enumerable loaded?
- # enumerable.loaded?
+ # @example Is the enumerable _loaded?
+ # enumerable._loaded?
#
- # @return [ true, false ] If the enumerable has been loaded.
+ # @return [ true, false ] If the enumerable has been _loaded.
#
# @since 2.1.0
- def loaded?
+ def _loaded?
!!@executed
end
# Reset the enumerable back to it's persisted state.
#
@@ -284,11 +306,11 @@
#
# @return [ false ] Always false.
#
# @since 2.1.0
def reset
- loaded.clear and added.clear
+ _loaded.clear and _added.clear
@executed = false
end
# Does this enumerable respond to the provided method?
#
@@ -314,15 +336,15 @@
#
# @return [ Integer ] The size of the enumerable.
#
# @since 2.1.0
def size
- count = (unloaded ? unloaded.count : loaded.count)
+ count = (_unloaded ? _unloaded.count : _loaded.count)
if count.zero?
- count + added.count
+ count + _added.count
else
- count + added.count{ |d| d.new_record? }
+ count + _added.values.count{ |d| d.new_record? }
end
end
alias :length :size
# Send #to_json to the entries.
@@ -330,11 +352,11 @@
# @example Get the enumerable as json.
# enumerable.to_json
#
# @param [ Hash ] options Optional parameters.
#
- # @return [ String ] The entries all loaded as a string.
+ # @return [ String ] The entries all _loaded as a string.
#
# @since 2.2.0
def to_json(options = {})
entries.to_json(options)
end
@@ -344,11 +366,11 @@
# @example Get the enumerable as json.
# enumerable.as_json
#
# @param [ Hash ] options Optional parameters.
#
- # @return [ Hash ] The entries all loaded as a hash.
+ # @return [ Hash ] The entries all _loaded as a hash.
#
# @since 2.2.0
def as_json(options = {})
entries.as_json(options)
end
@@ -369,9 +391,16 @@
private
def method_missing(name, *args, &block)
entries.send(name, *args, &block)
+ end
+
+ def matching_document(location)
+ _loaded.try(:values).try(location) ||
+ _added[_unloaded.try(location).try(:id)] ||
+ _unloaded.try(location) ||
+ _added.values.try(location)
end
end
end
end
end