mongoid 7.2.3 → 7.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Rakefile +16 -0
  5. data/lib/config/locales/en.yml +2 -2
  6. data/lib/mongoid/association/accessors.rb +1 -1
  7. data/lib/mongoid/association/constrainable.rb +1 -1
  8. data/lib/mongoid/association/depending.rb +4 -4
  9. data/lib/mongoid/association/embedded/batchable.rb +1 -1
  10. data/lib/mongoid/association/embedded/embedded_in.rb +1 -1
  11. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +10 -3
  12. data/lib/mongoid/association/nested/many.rb +1 -1
  13. data/lib/mongoid/association/nested/one.rb +4 -2
  14. data/lib/mongoid/association/proxy.rb +6 -1
  15. data/lib/mongoid/association/referenced/auto_save.rb +2 -2
  16. data/lib/mongoid/association/referenced/has_many/enumerable.rb +493 -495
  17. data/lib/mongoid/association/referenced/has_many/proxy.rb +2 -2
  18. data/lib/mongoid/association/referenced/has_one/nested_builder.rb +2 -2
  19. data/lib/mongoid/attributes.rb +24 -13
  20. data/lib/mongoid/attributes/projector.rb +120 -0
  21. data/lib/mongoid/cacheable.rb +2 -2
  22. data/lib/mongoid/clients.rb +1 -1
  23. data/lib/mongoid/clients/factory.rb +22 -8
  24. data/lib/mongoid/config.rb +19 -2
  25. data/lib/mongoid/contextual/aggregable/mongo.rb +10 -8
  26. data/lib/mongoid/copyable.rb +1 -1
  27. data/lib/mongoid/criteria.rb +4 -5
  28. data/lib/mongoid/criteria/findable.rb +1 -1
  29. data/lib/mongoid/criteria/queryable/expandable.rb +0 -24
  30. data/lib/mongoid/criteria/queryable/extensions.rb +0 -4
  31. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +1 -1
  32. data/lib/mongoid/criteria/queryable/mergeable.rb +46 -20
  33. data/lib/mongoid/criteria/queryable/selectable.rb +8 -8
  34. data/lib/mongoid/document.rb +1 -15
  35. data/lib/mongoid/errors/delete_restriction.rb +8 -9
  36. data/lib/mongoid/evolvable.rb +1 -1
  37. data/lib/mongoid/extensions/boolean.rb +1 -2
  38. data/lib/mongoid/extensions/false_class.rb +1 -1
  39. data/lib/mongoid/extensions/hash.rb +2 -2
  40. data/lib/mongoid/extensions/true_class.rb +1 -1
  41. data/lib/mongoid/fields.rb +43 -5
  42. data/lib/mongoid/inspectable.rb +1 -1
  43. data/lib/mongoid/matcher.rb +7 -0
  44. data/lib/mongoid/matcher/bits.rb +41 -0
  45. data/lib/mongoid/matcher/bits_all_clear.rb +20 -0
  46. data/lib/mongoid/matcher/bits_all_set.rb +20 -0
  47. data/lib/mongoid/matcher/bits_any_clear.rb +20 -0
  48. data/lib/mongoid/matcher/bits_any_set.rb +20 -0
  49. data/lib/mongoid/matcher/expression.rb +4 -0
  50. data/lib/mongoid/matcher/field_operator.rb +6 -0
  51. data/lib/mongoid/matcher/mod.rb +17 -0
  52. data/lib/mongoid/matcher/type.rb +99 -0
  53. data/lib/mongoid/persistable/deletable.rb +1 -2
  54. data/lib/mongoid/persistable/destroyable.rb +8 -2
  55. data/lib/mongoid/persistable/updatable.rb +27 -2
  56. data/lib/mongoid/query_cache.rb +35 -29
  57. data/lib/mongoid/selectable.rb +5 -7
  58. data/lib/mongoid/shardable.rb +21 -5
  59. data/lib/mongoid/touchable.rb +23 -4
  60. data/lib/mongoid/version.rb +1 -1
  61. data/spec/integration/associations/embeds_many_spec.rb +44 -0
  62. data/spec/integration/associations/has_one_spec.rb +48 -0
  63. data/spec/integration/criteria/date_field_spec.rb +1 -1
  64. data/spec/integration/document_spec.rb +9 -0
  65. data/spec/integration/matcher_operator_data/bits_all_clear.yml +159 -0
  66. data/spec/integration/matcher_operator_data/bits_all_set.yml +159 -0
  67. data/spec/integration/matcher_operator_data/bits_any_clear.yml +159 -0
  68. data/spec/integration/matcher_operator_data/bits_any_set.yml +159 -0
  69. data/spec/integration/matcher_operator_data/comment.yml +22 -0
  70. data/spec/integration/matcher_operator_data/in.yml +16 -0
  71. data/spec/integration/matcher_operator_data/mod.yml +55 -0
  72. data/spec/integration/matcher_operator_data/type.yml +70 -0
  73. data/spec/integration/matcher_operator_data/type_array.yml +16 -0
  74. data/spec/integration/matcher_operator_data/type_binary.yml +18 -0
  75. data/spec/integration/matcher_operator_data/type_boolean.yml +39 -0
  76. data/spec/integration/matcher_operator_data/type_code.yml +26 -0
  77. data/spec/integration/matcher_operator_data/type_code_with_scope.yml +26 -0
  78. data/spec/integration/matcher_operator_data/type_date.yml +39 -0
  79. data/spec/integration/matcher_operator_data/type_db_pointer.yml +19 -0
  80. data/spec/integration/matcher_operator_data/type_decimal.yml +40 -0
  81. data/spec/integration/matcher_operator_data/type_double.yml +15 -0
  82. data/spec/integration/matcher_operator_data/type_int32.yml +33 -0
  83. data/spec/integration/matcher_operator_data/type_int64.yml +33 -0
  84. data/spec/integration/matcher_operator_data/type_max_key.yml +17 -0
  85. data/spec/integration/matcher_operator_data/type_min_key.yml +17 -0
  86. data/spec/integration/matcher_operator_data/type_null.yml +23 -0
  87. data/spec/integration/matcher_operator_data/type_object.yml +23 -0
  88. data/spec/integration/matcher_operator_data/type_object_id.yml +25 -0
  89. data/spec/integration/matcher_operator_data/type_regex.yml +44 -0
  90. data/spec/integration/matcher_operator_data/type_string.yml +15 -0
  91. data/spec/integration/matcher_operator_data/type_symbol.yml +32 -0
  92. data/spec/integration/matcher_operator_data/type_timestamp.yml +25 -0
  93. data/spec/integration/matcher_operator_data/type_undefined.yml +17 -0
  94. data/spec/lite_spec_helper.rb +2 -0
  95. data/spec/mongoid/association/depending_spec.rb +391 -352
  96. data/spec/mongoid/association/nested/one_spec.rb +18 -14
  97. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +25 -8
  98. data/spec/mongoid/association/referenced/has_and_belongs_to_many/binding_spec.rb +1 -1
  99. data/spec/mongoid/association/referenced/has_many/binding_spec.rb +1 -1
  100. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +1 -1
  101. data/spec/mongoid/association/referenced/has_one_models.rb +8 -0
  102. data/spec/mongoid/atomic/paths_spec.rb +64 -12
  103. data/spec/mongoid/attributes/projector_data/embedded.yml +105 -0
  104. data/spec/mongoid/attributes/projector_data/fields.yml +93 -0
  105. data/spec/mongoid/attributes/projector_spec.rb +41 -0
  106. data/spec/mongoid/attributes_spec.rb +98 -6
  107. data/spec/mongoid/clients/factory_spec.rb +48 -0
  108. data/spec/mongoid/config_spec.rb +32 -0
  109. data/spec/mongoid/contextual/mongo_spec.rb +2 -2
  110. data/spec/mongoid/criteria/modifiable_spec.rb +1 -1
  111. data/spec/mongoid/criteria/queryable/expandable_spec.rb +0 -73
  112. data/spec/mongoid/criteria/queryable/extensions/boolean_spec.rb +1 -1
  113. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +105 -7
  114. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +229 -24
  115. data/spec/mongoid/criteria/queryable/selectable_shared_examples.rb +39 -0
  116. data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -565
  117. data/spec/mongoid/criteria/queryable/selectable_where_spec.rb +590 -0
  118. data/spec/mongoid/criteria_projection_spec.rb +411 -0
  119. data/spec/mongoid/criteria_spec.rb +0 -275
  120. data/spec/mongoid/document_spec.rb +13 -13
  121. data/spec/mongoid/errors/delete_restriction_spec.rb +1 -1
  122. data/spec/mongoid/extensions/false_class_spec.rb +1 -1
  123. data/spec/mongoid/extensions/string_spec.rb +5 -5
  124. data/spec/mongoid/extensions/true_class_spec.rb +1 -1
  125. data/spec/mongoid/fields/localized_spec.rb +4 -4
  126. data/spec/mongoid/fields_spec.rb +4 -4
  127. data/spec/mongoid/inspectable_spec.rb +12 -4
  128. data/spec/mongoid/persistable/deletable_spec.rb +175 -1
  129. data/spec/mongoid/persistable/destroyable_spec.rb +191 -3
  130. data/spec/mongoid/persistable/savable_spec.rb +3 -5
  131. data/spec/mongoid/persistable/upsertable_spec.rb +1 -1
  132. data/spec/mongoid/query_cache_middleware_spec.rb +8 -0
  133. data/spec/mongoid/reloadable_spec.rb +18 -1
  134. data/spec/mongoid/shardable_spec.rb +44 -0
  135. data/spec/mongoid/touchable_spec.rb +104 -16
  136. data/spec/mongoid/touchable_spec_models.rb +52 -0
  137. data/spec/mongoid/validatable_spec.rb +1 -1
  138. data/spec/spec_helper.rb +6 -2
  139. data/spec/support/client_registry.rb +9 -0
  140. data/spec/support/models/bolt.rb +8 -0
  141. data/spec/support/models/hole.rb +13 -0
  142. data/spec/support/models/mop.rb +0 -1
  143. data/spec/support/models/nut.rb +8 -0
  144. data/spec/support/models/person.rb +6 -0
  145. data/spec/support/models/sealer.rb +8 -0
  146. data/spec/support/models/shirt.rb +12 -0
  147. data/spec/support/models/spacer.rb +8 -0
  148. data/spec/support/models/threadlocker.rb +8 -0
  149. data/spec/support/models/washer.rb +8 -0
  150. metadata +97 -3
  151. metadata.gz.sig +5 -3
  152. data/spec/support/cluster_config.rb +0 -158
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 864274384839e2cd26892e87cac91bde61eea71957bd18530fd483b9a0e0dbae
4
- data.tar.gz: 2c351df40f5af200ca04671b1b8d1f86dbb78cfabfcb27bd39dcfa568e0e80dc
3
+ metadata.gz: 3d1e8ba04808e5217cc1622623532caa8c519a412278bfe85d2ed6cfdeda62c7
4
+ data.tar.gz: 38067c7a3a2a3347b8e564e2069938cbed1d1adb4668573465b088fc01bbe167
5
5
  SHA512:
6
- metadata.gz: 2e46b93f378bca30c949897549190f90470ea55e053c1fd0205705dfae524c2ca35d5ef28c72e3e7491103aa6b5d5bda4d364244428df0186725c9bb45577266
7
- data.tar.gz: 33aa58be38ead0eb4eb5e50a032b49e5ab1d7a63daf7607171e200048713b3fd01ba3c6a1da266eff5956730632f84018ea6f3d2629e951963065ef27d3451ab
6
+ metadata.gz: 067d6b81359b6c45c2c877ebeab3382425abe08f3ca5a75bf4dd880e7f082479a63823c38030fba356c061da8e9a7ea7393ca30f58c10c858b2f2d412a0aca0d
7
+ data.tar.gz: 4cdc9196e41db201ec36ea7024eab9ce9d7f20bf45db005c5c5111bd73756e9c5cf1ff2fc2fbead7c702ac47fabaa44aead2ab6d4db6d008ffb387d2c0c0efdb
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
data/Rakefile CHANGED
@@ -66,6 +66,22 @@ task :ci do
66
66
  spec_organizer.run
67
67
  end
68
68
 
69
+ task :bucket, %i(buckets) do |task, args|
70
+ buckets = args[:buckets]
71
+ buckets = if buckets.nil? || buckets.empty?
72
+ [nil]
73
+ else
74
+ buckets.split(':').map do |bucket|
75
+ if bucket.empty?
76
+ nil
77
+ else
78
+ bucket.to_sym
79
+ end
80
+ end
81
+ end
82
+ spec_organizer.run_buckets(*buckets)
83
+ end
84
+
69
85
  task :default => :spec
70
86
 
71
87
  desc "Generate all documentation"
@@ -578,9 +578,9 @@ en:
578
578
  resolution: "Try persisting the document with valid data or remove
579
579
  the validations."
580
580
  delete_restriction:
581
- message: "Cannot delete %{document} because of dependent '%{relation}'."
581
+ message: "Cannot destroy %{document} because of dependent '%{relation}'."
582
582
  summary: "When defining '%{relation}' with a :dependent => :restrict_with_error,
583
- Mongoid will raise an error when attempting to delete the
583
+ Mongoid will raise an error when attempting to destroy the
584
584
  %{document} when the child '%{relation}' still has documents in it."
585
585
  resolution: "Don't attempt to delete the parent %{document} when
586
586
  it has children, or change the dependent option on the association."
@@ -300,7 +300,7 @@ module Mongoid
300
300
  ids_method = "#{association.name.to_s.singularize}_ids"
301
301
  association.inverse_class.tap do |klass|
302
302
  klass.re_define_method(ids_method) do
303
- send(association.name).only(:id).map(&:id)
303
+ send(association.name).only(:_id).map(&:_id)
304
304
  end
305
305
  end
306
306
  end
@@ -36,7 +36,7 @@ module Mongoid
36
36
 
37
37
  def convert_polymorphic(object)
38
38
  if object.is_a?(Mongoid::Document)
39
- object.id
39
+ object._id
40
40
  else
41
41
  BSON::ObjectId.mongoize(object)
42
42
  end
@@ -78,13 +78,13 @@ module Mongoid
78
78
  # the appropriate strategy to perform the operation.
79
79
  #
80
80
  # @example Execute cascades.
81
- # document.apply_delete_dependencies!
81
+ # document.apply_destroy_dependencies!
82
82
  #
83
83
  # @since 2.0.0.rc.1
84
- def apply_delete_dependencies!
84
+ def apply_destroy_dependencies!
85
85
  self.class._all_dependents.each do |association|
86
- if association.try(:dependent)
87
- send("_dependent_#{association.dependent}!", association)
86
+ if dependent = association.try(:dependent)
87
+ send("_dependent_#{dependent}!", association)
88
88
  end
89
89
  end
90
90
  end
@@ -329,7 +329,7 @@ module Mongoid
329
329
  self.path = doc.atomic_path unless path
330
330
  execute_callback :before_remove, doc
331
331
  unless _assigning?
332
- doc.apply_delete_dependencies!
332
+ doc.apply_destroy_dependencies!
333
333
  doc.run_before_callbacks(:destroy) if method == :destroy
334
334
  end
335
335
  _target.delete_one(doc)
@@ -26,7 +26,7 @@ module Mongoid
26
26
  :autobuild,
27
27
  :cyclic,
28
28
  :polymorphic,
29
- :touch
29
+ :touch,
30
30
  ].freeze
31
31
 
32
32
  # The complete list of valid options for this association, including
@@ -83,8 +83,15 @@ module Mongoid
83
83
 
84
84
  alias :new :build
85
85
 
86
- # Clear the association. Will delete the documents from the db if they are
87
- # already persisted.
86
+ # Clear the association. Will delete the documents from the db
87
+ # if they are already persisted.
88
+ #
89
+ # If the host document is not persisted but its _id matches a
90
+ # persisted document, calling #clear on an association will remove
91
+ # the association's documents from the database even though the
92
+ # set of documents in the application (as loaded in the host)
93
+ # is different from what is in the database, and the host may
94
+ # not contain any persisted documents in the association either.
88
95
  #
89
96
  # @example Clear the association.
90
97
  # person.addresses.clear
@@ -332,7 +339,7 @@ module Mongoid
332
339
  private
333
340
 
334
341
  def object_already_related?(document)
335
- _target.any? { |existing| existing.id && existing === document }
342
+ _target.any? { |existing| existing._id && existing === document }
336
343
  end
337
344
 
338
345
  # Appends the document to the target array, updating the index on the
@@ -181,7 +181,7 @@ module Mongoid
181
181
  first = existing.first
182
182
  converted = first ? convert_id(first.class, id) : id
183
183
 
184
- if existing.where(id: converted).exists?
184
+ if existing.where(_id: converted).exists?
185
185
  # document exists in association
186
186
  doc = existing.find(converted)
187
187
  if destroyable?(attrs)
@@ -71,7 +71,8 @@ module Mongoid
71
71
  #
72
72
  # @since 2.0.0
73
73
  def acceptable_id?
74
- id = convert_id(existing.class, attributes[:id])
74
+ id = association.klass.extract_id_field(attributes)
75
+ id = convert_id(existing.class, id)
75
76
  existing._id == id || id.nil? || (existing._id != id && update_only?)
76
77
  end
77
78
 
@@ -84,7 +85,8 @@ module Mongoid
84
85
  #
85
86
  # @since 2.0.0
86
87
  def delete?
87
- destroyable? && !attributes[:id].nil?
88
+ id = association.klass.extract_id_field(attributes)
89
+ destroyable? && !id.nil?
88
90
  end
89
91
 
90
92
  # Can the existing association potentially be destroyed?
@@ -16,7 +16,7 @@ module Mongoid
16
16
  # We undefine most methods to get them sent through to the target.
17
17
  instance_methods.each do |method|
18
18
  undef_method(method) unless
19
- method =~ /\A(__.*|send|object_id|equal\?|respond_to\?|tap|public_send|extend_proxy|extend_proxies)\z/
19
+ method =~ /\A(?:__.*|send|object_id|equal\?|respond_to\?|respond_to_missing\?|tap|public_send|extend_proxy|extend_proxies)\z/
20
20
  end
21
21
 
22
22
  include Threaded::Lifecycle
@@ -137,6 +137,11 @@ module Mongoid
137
137
  _target.send(name, *args, &block)
138
138
  end
139
139
 
140
+ # @api private
141
+ def respond_to_missing?(name, *args)
142
+ _target.respond_to?(name, *args)
143
+ end
144
+
140
145
  # When the base document illegally references an embedded document this
141
146
  # error will get raised.
142
147
  #
@@ -63,8 +63,8 @@ module Mongoid
63
63
  self.before_callback_halted = false
64
64
  else
65
65
  __autosaving__ do
66
- if relation = ivar(association.name)
67
- Array(relation).each do |doc|
66
+ if assoc_value = ivar(association.name)
67
+ Array(assoc_value).each do |doc|
68
68
  doc.with(persistence_context) do |d|
69
69
  d.save
70
70
  end
@@ -5,544 +5,542 @@ module Mongoid
5
5
  module Association
6
6
  module Referenced
7
7
  class HasMany
8
- module Targets
9
-
10
- # This class is the wrapper for all referenced associations that have a
11
- # target that can be a criteria or array of _loaded documents. This
12
- # handles both cases or a combination of the two.
13
- class Enumerable
14
- extend Forwardable
15
- include ::Enumerable
16
-
17
- # The three main instance variables are collections of documents.
18
- #
19
- # @attribute [rw] _added Documents that have been appended.
20
- # @attribute [rw] _loaded Persisted documents that have been _loaded.
21
- # @attribute [rw] _unloaded A criteria representing persisted docs.
22
- attr_accessor :_added, :_loaded, :_unloaded
23
-
24
- def_delegators [], :is_a?, :kind_of?
25
-
26
- # Check if the enumerable is equal to the other object.
27
- #
28
- # @example Check equality.
29
- # enumerable == []
30
- #
31
- # @param [ Enumerable ] other The other enumerable.
32
- #
33
- # @return [ true, false ] If the objects are equal.
34
- #
35
- # @since 2.1.0
36
- def ==(other)
37
- return false unless other.respond_to?(:entries)
38
- entries == other.entries
39
- end
40
8
 
41
- # Check equality of the enumerable against the provided object for case
42
- # statements.
43
- #
44
- # @example Check case equality.
45
- # enumerable === Array
46
- #
47
- # @param [ Object ] other The object to check.
48
- #
49
- # @return [ true, false ] If the objects are equal in a case.
50
- #
51
- # @since 3.1.4
52
- def ===(other)
53
- other.class == Class ? (Array == other || Enumerable == other) : self == other
54
- end
9
+ # This class is the wrapper for all referenced associations that have a
10
+ # target that can be a criteria or array of _loaded documents. This
11
+ # handles both cases or a combination of the two.
12
+ class Enumerable
13
+ extend Forwardable
14
+ include ::Enumerable
15
+
16
+ # The three main instance variables are collections of documents.
17
+ #
18
+ # @attribute [rw] _added Documents that have been appended.
19
+ # @attribute [rw] _loaded Persisted documents that have been _loaded.
20
+ # @attribute [rw] _unloaded A criteria representing persisted docs.
21
+ attr_accessor :_added, :_loaded, :_unloaded
22
+
23
+ def_delegators [], :is_a?, :kind_of?
24
+
25
+ # Check if the enumerable is equal to the other object.
26
+ #
27
+ # @example Check equality.
28
+ # enumerable == []
29
+ #
30
+ # @param [ Enumerable ] other The other enumerable.
31
+ #
32
+ # @return [ true, false ] If the objects are equal.
33
+ #
34
+ # @since 2.1.0
35
+ def ==(other)
36
+ return false unless other.respond_to?(:entries)
37
+ entries == other.entries
38
+ end
55
39
 
56
- # Append a document to the enumerable.
57
- #
58
- # @example Append the document.
59
- # enumerable << document
60
- #
61
- # @param [ Document ] document The document to append.
62
- #
63
- # @return [ Document ] The document.
64
- #
65
- # @since 2.1.0
66
- def <<(document)
67
- _added[document._id] = document
68
- self
69
- end
40
+ # Check equality of the enumerable against the provided object for case
41
+ # statements.
42
+ #
43
+ # @example Check case equality.
44
+ # enumerable === Array
45
+ #
46
+ # @param [ Object ] other The object to check.
47
+ #
48
+ # @return [ true, false ] If the objects are equal in a case.
49
+ #
50
+ # @since 3.1.4
51
+ def ===(other)
52
+ other.class == Class ? (Array == other || Enumerable == other) : self == other
53
+ end
70
54
 
71
- alias :push :<<
72
-
73
- # Clears out all the documents in this enumerable. If passed a block it
74
- # will yield to each document that is in memory.
75
- #
76
- # @example Clear out the enumerable.
77
- # enumerable.clear
78
- #
79
- # @example Clear out the enumerable with a block.
80
- # enumerable.clear do |doc|
81
- # doc.unbind
82
- # end
83
- #
84
- # @return [ Array<Document> ] The cleared out _added docs.
85
- #
86
- # @since 2.1.0
87
- def clear
88
- if block_given?
89
- in_memory { |doc| yield(doc) }
90
- end
91
- _loaded.clear and _added.clear
92
- end
55
+ # Append a document to the enumerable.
56
+ #
57
+ # @example Append the document.
58
+ # enumerable << document
59
+ #
60
+ # @param [ Document ] document The document to append.
61
+ #
62
+ # @return [ Document ] The document.
63
+ #
64
+ # @since 2.1.0
65
+ def <<(document)
66
+ _added[document._id] = document
67
+ self
68
+ end
93
69
 
94
- # Clones each document in the enumerable.
95
- #
96
- # @note This loads all documents into memory.
97
- #
98
- # @example Clone the enumerable.
99
- # enumerable.clone
100
- #
101
- # @return [ Array<Document> ] An array clone of the enumerable.
102
- #
103
- # @since 2.1.6
104
- def clone
105
- collect { |doc| doc.clone }
70
+ alias :push :<<
71
+
72
+ # Clears out all the documents in this enumerable. If passed a block it
73
+ # will yield to each document that is in memory.
74
+ #
75
+ # @example Clear out the enumerable.
76
+ # enumerable.clear
77
+ #
78
+ # @example Clear out the enumerable with a block.
79
+ # enumerable.clear do |doc|
80
+ # doc.unbind
81
+ # end
82
+ #
83
+ # @return [ Array<Document> ] The cleared out _added docs.
84
+ #
85
+ # @since 2.1.0
86
+ def clear
87
+ if block_given?
88
+ in_memory { |doc| yield(doc) }
106
89
  end
90
+ _loaded.clear and _added.clear
91
+ end
92
+
93
+ # Clones each document in the enumerable.
94
+ #
95
+ # @note This loads all documents into memory.
96
+ #
97
+ # @example Clone the enumerable.
98
+ # enumerable.clone
99
+ #
100
+ # @return [ Array<Document> ] An array clone of the enumerable.
101
+ #
102
+ # @since 2.1.6
103
+ def clone
104
+ collect { |doc| doc.clone }
105
+ end
107
106
 
108
- # Delete the supplied document from the enumerable.
109
- #
110
- # @example Delete the document.
111
- # enumerable.delete(document)
112
- #
113
- # @param [ Document ] document The document to delete.
114
- #
115
- # @return [ Document ] The deleted document.
116
- #
117
- # @since 2.1.0
118
- def delete(document)
119
- doc = (_loaded.delete(document._id) || _added.delete(document._id))
120
- unless doc
121
- if _unloaded && _unloaded.where(_id: document._id).exists?
122
- yield(document) if block_given?
123
- return document
124
- end
107
+ # Delete the supplied document from the enumerable.
108
+ #
109
+ # @example Delete the document.
110
+ # enumerable.delete(document)
111
+ #
112
+ # @param [ Document ] document The document to delete.
113
+ #
114
+ # @return [ Document ] The deleted document.
115
+ #
116
+ # @since 2.1.0
117
+ def delete(document)
118
+ doc = (_loaded.delete(document._id) || _added.delete(document._id))
119
+ unless doc
120
+ if _unloaded && _unloaded.where(_id: document._id).exists?
121
+ yield(document) if block_given?
122
+ return document
125
123
  end
126
- yield(doc) if block_given?
127
- doc
128
124
  end
125
+ yield(doc) if block_given?
126
+ doc
127
+ end
129
128
 
130
- # Deletes every document in the enumerable for where the block returns
131
- # true.
132
- #
133
- # @note This operation loads all documents from the database.
134
- #
135
- # @example Delete all matching documents.
136
- # enumerable.delete_if do |doc|
137
- # dod._id == _id
138
- # end
139
- #
140
- # @return [ Array<Document> ] The remaining docs.
141
- #
142
- # @since 2.1.0
143
- def delete_if(&block)
144
- load_all!
145
- deleted = in_memory.select(&block)
146
- deleted.each do |doc|
147
- _loaded.delete(doc._id)
148
- _added.delete(doc._id)
149
- end
150
- self
129
+ # Deletes every document in the enumerable for where the block returns
130
+ # true.
131
+ #
132
+ # @note This operation loads all documents from the database.
133
+ #
134
+ # @example Delete all matching documents.
135
+ # enumerable.delete_if do |doc|
136
+ # dod._id == _id
137
+ # end
138
+ #
139
+ # @return [ Array<Document> ] The remaining docs.
140
+ #
141
+ # @since 2.1.0
142
+ def delete_if(&block)
143
+ load_all!
144
+ deleted = in_memory.select(&block)
145
+ deleted.each do |doc|
146
+ _loaded.delete(doc._id)
147
+ _added.delete(doc._id)
151
148
  end
149
+ self
150
+ end
152
151
 
153
- # Iterating over this enumerable has to handle a few different
154
- # scenarios.
155
- #
156
- # If the enumerable has its criteria _loaded into memory then it yields
157
- # to all the _loaded docs and all the _added docs.
158
- #
159
- # If the enumerable has not _loaded the criteria then it iterates over
160
- # the cursor while loading the documents and then iterates over the
161
- # _added docs.
162
- #
163
- # If no block is passed then it returns an enumerator containing all
164
- # docs.
165
- #
166
- # @example Iterate over the enumerable.
167
- # enumerable.each do |doc|
168
- # puts doc
169
- # end
170
- #
171
- # @example return an enumerator containing all the docs
172
- #
173
- # a = enumerable.each
174
- #
175
- # @return [ true ] That the enumerable is now _loaded.
176
- #
177
- # @since 2.1.0
178
- def each
179
- unless block_given?
180
- return to_enum
181
- end
182
- if _loaded?
183
- _loaded.each_pair do |id, doc|
184
- document = _added.delete(doc._id) || doc
185
- set_base(document)
186
- yield(document)
187
- end
188
- else
189
- unloaded_documents.each do |doc|
190
- document = _added.delete(doc._id) || _loaded.delete(doc._id) || doc
191
- _loaded[document._id] = document
192
- set_base(document)
193
- yield(document)
194
- end
152
+ # Iterating over this enumerable has to handle a few different
153
+ # scenarios.
154
+ #
155
+ # If the enumerable has its criteria _loaded into memory then it yields
156
+ # to all the _loaded docs and all the _added docs.
157
+ #
158
+ # If the enumerable has not _loaded the criteria then it iterates over
159
+ # the cursor while loading the documents and then iterates over the
160
+ # _added docs.
161
+ #
162
+ # If no block is passed then it returns an enumerator containing all
163
+ # docs.
164
+ #
165
+ # @example Iterate over the enumerable.
166
+ # enumerable.each do |doc|
167
+ # puts doc
168
+ # end
169
+ #
170
+ # @example return an enumerator containing all the docs
171
+ #
172
+ # a = enumerable.each
173
+ #
174
+ # @return [ true ] That the enumerable is now _loaded.
175
+ #
176
+ # @since 2.1.0
177
+ def each
178
+ unless block_given?
179
+ return to_enum
180
+ end
181
+ if _loaded?
182
+ _loaded.each_pair do |id, doc|
183
+ document = _added.delete(doc._id) || doc
184
+ set_base(document)
185
+ yield(document)
195
186
  end
196
- _added.each_pair do |id, doc|
197
- yield(doc)
187
+ else
188
+ unloaded_documents.each do |doc|
189
+ document = _added.delete(doc._id) || _loaded.delete(doc._id) || doc
190
+ _loaded[document._id] = document
191
+ set_base(document)
192
+ yield(document)
198
193
  end
199
- @executed = true
200
194
  end
201
-
202
- # Is the enumerable empty? Will determine if the count is zero based on
203
- # whether or not it is _loaded.
204
- #
205
- # @example Is the enumerable empty?
206
- # enumerable.empty?
207
- #
208
- # @return [ true, false ] If the enumerable is empty.
209
- #
210
- # @since 2.1.0
211
- def empty?
212
- if _loaded?
213
- in_memory.count == 0
214
- else
215
- _unloaded.count + _added.count == 0
216
- end
195
+ _added.each_pair do |id, doc|
196
+ yield(doc)
217
197
  end
198
+ @executed = true
199
+ end
218
200
 
219
- # Returns whether the association has any documents, optionally
220
- # subject to the provided filters.
221
- #
222
- # This method returns true if the association has any persisted
223
- # documents and if it has any not yet persisted documents.
224
- #
225
- # If the association is already loaded, this method inspects the
226
- # loaded documents and does not query the database. If the
227
- # association is not loaded, the argument-less and block-less
228
- # version does not load the association; the other versions
229
- # (that delegate to Enumerable) may or may not load the association
230
- # completely depending on whether it is iterated to completion.
231
- #
232
- # This method can take a parameter and a block. The behavior with
233
- # either the paramater or the block is delegated to the standard
234
- # library Enumerable module.
235
- #
236
- # Note that when Enumerable's any? method is invoked with both
237
- # a block and a pattern, it only uses the pattern.
238
- #
239
- # @param [ Object ] condition The condition that documents
240
- # must satisfy. See Enumerable documentation for details.
241
- #
242
- # @return [ true, false ] If the association has any documents.
243
- def any?(*args)
244
- return super if args.any? || block_given?
245
-
246
- if _loaded?
247
- in_memory.length > 0
248
- else
249
- _unloaded.exists? || _added.length > 0
250
- end
201
+ # Is the enumerable empty? Will determine if the count is zero based on
202
+ # whether or not it is _loaded.
203
+ #
204
+ # @example Is the enumerable empty?
205
+ # enumerable.empty?
206
+ #
207
+ # @return [ true, false ] If the enumerable is empty.
208
+ #
209
+ # @since 2.1.0
210
+ def empty?
211
+ if _loaded?
212
+ in_memory.count == 0
213
+ else
214
+ _unloaded.count + _added.count == 0
251
215
  end
216
+ end
252
217
 
253
- # Get the first document in the enumerable. Will check the persisted
254
- # documents first. Does not load the entire enumerable.
255
- #
256
- # @example Get the first document.
257
- # enumerable.first
258
- #
259
- # @note Automatically adding a sort on _id when no other sort is
260
- # defined on the criteria has the potential to cause bad performance issues.
261
- # If you experience unexpected poor performance when using #first or #last,
262
- # use the option { id_sort: :none }.
263
- # Be aware that #first/#last won't guarantee order in this case.
264
- #
265
- # @param [ Hash ] opts The options for the query returning the first document.
266
- #
267
- # @option opts [ :none ] :id_sort Don't apply a sort on _id.
268
- #
269
- # @return [ Document ] The first document found.
270
- #
271
- # @since 2.1.0
272
- def first(opts = {})
273
- _loaded.try(:values).try(:first) ||
274
- _added[(ul = _unloaded.try(:first, opts)).try(:id)] ||
275
- ul ||
276
- _added.values.try(:first)
218
+ # Returns whether the association has any documents, optionally
219
+ # subject to the provided filters.
220
+ #
221
+ # This method returns true if the association has any persisted
222
+ # documents and if it has any not yet persisted documents.
223
+ #
224
+ # If the association is already loaded, this method inspects the
225
+ # loaded documents and does not query the database. If the
226
+ # association is not loaded, the argument-less and block-less
227
+ # version does not load the association; the other versions
228
+ # (that delegate to Enumerable) may or may not load the association
229
+ # completely depending on whether it is iterated to completion.
230
+ #
231
+ # This method can take a parameter and a block. The behavior with
232
+ # either the paramater or the block is delegated to the standard
233
+ # library Enumerable module.
234
+ #
235
+ # Note that when Enumerable's any? method is invoked with both
236
+ # a block and a pattern, it only uses the pattern.
237
+ #
238
+ # @param [ Object ] condition The condition that documents
239
+ # must satisfy. See Enumerable documentation for details.
240
+ #
241
+ # @return [ true, false ] If the association has any documents.
242
+ def any?(*args)
243
+ return super if args.any? || block_given?
244
+
245
+ if _loaded?
246
+ in_memory.length > 0
247
+ else
248
+ _unloaded.exists? || _added.length > 0
277
249
  end
250
+ end
278
251
 
279
- # Initialize the new enumerable either with a criteria or an array.
280
- #
281
- # @example Initialize the enumerable with a criteria.
282
- # Enumberable.new(Post.where(:person_id => id))
283
- #
284
- # @example Initialize the enumerable with an array.
285
- # Enumerable.new([ post ])
286
- #
287
- # @param [ Criteria, Array<Document> ] target The wrapped object.
288
- #
289
- # @since 2.1.0
290
- def initialize(target, base = nil, association = nil)
291
- @_base = base
292
- @_association = association
293
- if target.is_a?(Criteria)
294
- @_added, @executed, @_loaded, @_unloaded = {}, false, {}, target
295
- else
296
- @_added, @executed = {}, true
297
- @_loaded = target.inject({}) do |_target, doc|
298
- _target[doc._id] = doc if doc
299
- _target
300
- end
252
+ # Get the first document in the enumerable. Will check the persisted
253
+ # documents first. Does not load the entire enumerable.
254
+ #
255
+ # @example Get the first document.
256
+ # enumerable.first
257
+ #
258
+ # @note Automatically adding a sort on _id when no other sort is
259
+ # defined on the criteria has the potential to cause bad performance issues.
260
+ # If you experience unexpected poor performance when using #first or #last,
261
+ # use the option { id_sort: :none }.
262
+ # Be aware that #first/#last won't guarantee order in this case.
263
+ #
264
+ # @param [ Hash ] opts The options for the query returning the first document.
265
+ #
266
+ # @option opts [ :none ] :id_sort Don't apply a sort on _id.
267
+ #
268
+ # @return [ Document ] The first document found.
269
+ #
270
+ # @since 2.1.0
271
+ def first(opts = {})
272
+ _loaded.try(:values).try(:first) ||
273
+ _added[(ul = _unloaded.try(:first, opts)).try(:_id)] ||
274
+ ul ||
275
+ _added.values.try(:first)
276
+ end
277
+
278
+ # Initialize the new enumerable either with a criteria or an array.
279
+ #
280
+ # @example Initialize the enumerable with a criteria.
281
+ # Enumberable.new(Post.where(:person_id => id))
282
+ #
283
+ # @example Initialize the enumerable with an array.
284
+ # Enumerable.new([ post ])
285
+ #
286
+ # @param [ Criteria, Array<Document> ] target The wrapped object.
287
+ #
288
+ # @since 2.1.0
289
+ def initialize(target, base = nil, association = nil)
290
+ @_base = base
291
+ @_association = association
292
+ if target.is_a?(Criteria)
293
+ @_added, @executed, @_loaded, @_unloaded = {}, false, {}, target
294
+ else
295
+ @_added, @executed = {}, true
296
+ @_loaded = target.inject({}) do |_target, doc|
297
+ _target[doc._id] = doc if doc
298
+ _target
301
299
  end
302
300
  end
301
+ end
303
302
 
304
- # Does the target include the provided document?
305
- #
306
- # @example Does the target include the document?
307
- # enumerable.include?(document)
308
- #
309
- # @param [ Document ] doc The document to check.
310
- #
311
- # @return [ true, false ] If the document is in the target.
312
- #
313
- # @since 3.0.0
314
- def include?(doc)
315
- return super unless _unloaded
316
- _unloaded.where(_id: doc._id).exists? || _added.has_key?(doc._id)
317
- end
303
+ # Does the target include the provided document?
304
+ #
305
+ # @example Does the target include the document?
306
+ # enumerable.include?(document)
307
+ #
308
+ # @param [ Document ] doc The document to check.
309
+ #
310
+ # @return [ true, false ] If the document is in the target.
311
+ #
312
+ # @since 3.0.0
313
+ def include?(doc)
314
+ return super unless _unloaded
315
+ _unloaded.where(_id: doc._id).exists? || _added.has_key?(doc._id)
316
+ end
318
317
 
319
- # Inspection will just inspect the entries for nice array-style
320
- # printing.
321
- #
322
- # @example Inspect the enumerable.
323
- # enumerable.inspect
324
- #
325
- # @return [ String ] The inspected enum.
326
- #
327
- # @since 2.1.0
328
- def inspect
329
- entries.inspect
330
- end
318
+ # Inspection will just inspect the entries for nice array-style
319
+ # printing.
320
+ #
321
+ # @example Inspect the enumerable.
322
+ # enumerable.inspect
323
+ #
324
+ # @return [ String ] The inspected enum.
325
+ #
326
+ # @since 2.1.0
327
+ def inspect
328
+ entries.inspect
329
+ end
331
330
 
332
- # Return all the documents in the enumerable that have been _loaded or
333
- # _added.
334
- #
335
- # @note When passed a block it yields to each document.
336
- #
337
- # @example Get the in memory docs.
338
- # enumerable.in_memory
339
- #
340
- # @return [ Array<Document> ] The in memory docs.
341
- #
342
- # @since 2.1.0
343
- def in_memory
344
- docs = (_loaded.values + _added.values)
345
- docs.each do |doc|
346
- yield(doc) if block_given?
347
- end
331
+ # Return all the documents in the enumerable that have been _loaded or
332
+ # _added.
333
+ #
334
+ # @note When passed a block it yields to each document.
335
+ #
336
+ # @example Get the in memory docs.
337
+ # enumerable.in_memory
338
+ #
339
+ # @return [ Array<Document> ] The in memory docs.
340
+ #
341
+ # @since 2.1.0
342
+ def in_memory
343
+ docs = (_loaded.values + _added.values)
344
+ docs.each do |doc|
345
+ yield(doc) if block_given?
348
346
  end
347
+ end
349
348
 
350
- # Get the last document in the enumerable. Will check the new
351
- # documents first. Does not load the entire enumerable.
352
- #
353
- # @example Get the last document.
354
- # enumerable.last
355
- #
356
- # @note Automatically adding a sort on _id when no other sort is
357
- # defined on the criteria has the potential to cause bad performance issues.
358
- # If you experience unexpected poor performance when using #first or #last,
359
- # use the option { id_sort: :none }.
360
- # Be aware that #first/#last won't guarantee order in this case.
361
- #
362
- # @param [ Hash ] opts The options for the query returning the first document.
363
- #
364
- # @option opts [ :none ] :id_sort Don't apply a sort on _id.
365
- #
366
- # @return [ Document ] The last document found.
367
- #
368
- # @since 2.1.0
369
- def last(opts = {})
370
- _added.values.try(:last) ||
371
- _loaded.try(:values).try(:last) ||
372
- _added[(ul = _unloaded.try(:last, opts)).try(:id)] ||
373
- ul
374
- end
349
+ # Get the last document in the enumerable. Will check the new
350
+ # documents first. Does not load the entire enumerable.
351
+ #
352
+ # @example Get the last document.
353
+ # enumerable.last
354
+ #
355
+ # @note Automatically adding a sort on _id when no other sort is
356
+ # defined on the criteria has the potential to cause bad performance issues.
357
+ # If you experience unexpected poor performance when using #first or #last,
358
+ # use the option { id_sort: :none }.
359
+ # Be aware that #first/#last won't guarantee order in this case.
360
+ #
361
+ # @param [ Hash ] opts The options for the query returning the first document.
362
+ #
363
+ # @option opts [ :none ] :id_sort Don't apply a sort on _id.
364
+ #
365
+ # @return [ Document ] The last document found.
366
+ #
367
+ # @since 2.1.0
368
+ def last(opts = {})
369
+ _added.values.try(:last) ||
370
+ _loaded.try(:values).try(:last) ||
371
+ _added[(ul = _unloaded.try(:last, opts)).try(:_id)] ||
372
+ ul
373
+ end
375
374
 
376
- # Loads all the documents in the enumerable from the database.
377
- #
378
- # @example Load all the documents.
379
- # enumerable.load_all!
380
- #
381
- # @return [ true ] That the enumerable is _loaded.
382
- #
383
- # @since 2.1.0
384
- alias :load_all! :entries
385
-
386
- # Has the enumerable been _loaded? This will be true if the criteria has
387
- # been executed or we manually load the entire thing.
388
- #
389
- # @example Is the enumerable _loaded?
390
- # enumerable._loaded?
391
- #
392
- # @return [ true, false ] If the enumerable has been _loaded.
393
- #
394
- # @since 2.1.0
395
- def _loaded?
396
- !!@executed
397
- end
375
+ # Loads all the documents in the enumerable from the database.
376
+ #
377
+ # @example Load all the documents.
378
+ # enumerable.load_all!
379
+ #
380
+ # @return [ true ] That the enumerable is _loaded.
381
+ #
382
+ # @since 2.1.0
383
+ alias :load_all! :entries
384
+
385
+ # Has the enumerable been _loaded? This will be true if the criteria has
386
+ # been executed or we manually load the entire thing.
387
+ #
388
+ # @example Is the enumerable _loaded?
389
+ # enumerable._loaded?
390
+ #
391
+ # @return [ true, false ] If the enumerable has been _loaded.
392
+ #
393
+ # @since 2.1.0
394
+ def _loaded?
395
+ !!@executed
396
+ end
398
397
 
399
- # Provides the data needed to Marshal.dump an enumerable proxy.
400
- #
401
- # @example Dump the proxy.
402
- # Marshal.dump(proxy)
403
- #
404
- # @return [ Array<Object> ] The dumped data.
405
- #
406
- # @since 3.0.15
407
- def marshal_dump
408
- [_added, _loaded, _unloaded, @executed]
409
- end
398
+ # Provides the data needed to Marshal.dump an enumerable proxy.
399
+ #
400
+ # @example Dump the proxy.
401
+ # Marshal.dump(proxy)
402
+ #
403
+ # @return [ Array<Object> ] The dumped data.
404
+ #
405
+ # @since 3.0.15
406
+ def marshal_dump
407
+ [_added, _loaded, _unloaded, @executed]
408
+ end
410
409
 
411
- # Loads the data needed to Marshal.load an enumerable proxy.
412
- #
413
- # @example Load the proxy.
414
- # Marshal.load(proxy)
415
- #
416
- # @return [ Array<Object> ] The dumped data.
417
- #
418
- # @since 3.0.15
419
- def marshal_load(data)
420
- @_added, @_loaded, @_unloaded, @executed = data
421
- end
410
+ # Loads the data needed to Marshal.load an enumerable proxy.
411
+ #
412
+ # @example Load the proxy.
413
+ # Marshal.load(proxy)
414
+ #
415
+ # @return [ Array<Object> ] The dumped data.
416
+ #
417
+ # @since 3.0.15
418
+ def marshal_load(data)
419
+ @_added, @_loaded, @_unloaded, @executed = data
420
+ end
422
421
 
423
- # Reset the enumerable back to its persisted state.
424
- #
425
- # @example Reset the enumerable.
426
- # enumerable.reset
427
- #
428
- # @return [ false ] Always false.
429
- #
430
- # @since 2.1.0
431
- def reset
432
- _loaded.clear
433
- _added.clear
434
- @executed = false
435
- end
422
+ # Reset the enumerable back to its persisted state.
423
+ #
424
+ # @example Reset the enumerable.
425
+ # enumerable.reset
426
+ #
427
+ # @return [ false ] Always false.
428
+ #
429
+ # @since 2.1.0
430
+ def reset
431
+ _loaded.clear
432
+ _added.clear
433
+ @executed = false
434
+ end
436
435
 
437
- # Resets the underlying unloaded criteria object with a new one. Used
438
- # my HABTM associations to keep the underlying array in sync.
439
- #
440
- # @example Reset the unloaded documents.
441
- # enumerable.reset_unloaded(criteria)
442
- #
443
- # @param [ Criteria ] criteria The criteria to replace with.
444
- #
445
- # @since 3.0.14
446
- def reset_unloaded(criteria)
447
- @_unloaded = criteria if _unloaded.is_a?(Criteria)
448
- end
436
+ # Resets the underlying unloaded criteria object with a new one. Used
437
+ # my HABTM associations to keep the underlying array in sync.
438
+ #
439
+ # @example Reset the unloaded documents.
440
+ # enumerable.reset_unloaded(criteria)
441
+ #
442
+ # @param [ Criteria ] criteria The criteria to replace with.
443
+ #
444
+ # @since 3.0.14
445
+ def reset_unloaded(criteria)
446
+ @_unloaded = criteria if _unloaded.is_a?(Criteria)
447
+ end
449
448
 
450
- # Does this enumerable respond to the provided method?
451
- #
452
- # @example Does the enumerable respond to the method?
453
- # enumerable.respond_to?(:sum)
454
- #
455
- # @param [ String, Symbol ] name The name of the method.
456
- # @param [ true, false ] include_private Whether to include private
457
- # methods.
458
- #
459
- # @return [ true, false ] Whether the enumerable responds.
460
- #
461
- # @since 2.1.0
462
- def respond_to?(name, include_private = false)
463
- [].respond_to?(name, include_private) || super
464
- end
449
+ # Does this enumerable respond to the provided method?
450
+ #
451
+ # @example Does the enumerable respond to the method?
452
+ # enumerable.respond_to?(:sum)
453
+ #
454
+ # @param [ String, Symbol ] name The name of the method.
455
+ # @param [ true, false ] include_private Whether to include private
456
+ # methods.
457
+ #
458
+ # @return [ true, false ] Whether the enumerable responds.
459
+ #
460
+ # @since 2.1.0
461
+ def respond_to?(name, include_private = false)
462
+ [].respond_to?(name, include_private) || super
463
+ end
465
464
 
466
- # Gets the total size of this enumerable. This is a combination of all
467
- # the persisted and unpersisted documents.
468
- #
469
- # @example Get the size.
470
- # enumerable.size
471
- #
472
- # @return [ Integer ] The size of the enumerable.
473
- #
474
- # @since 2.1.0
475
- def size
476
- count = (_unloaded ? _unloaded.count : _loaded.count)
477
- if count.zero?
478
- count + _added.count
479
- else
480
- count + _added.values.count { |d| d.new_record? }
481
- end
465
+ # Gets the total size of this enumerable. This is a combination of all
466
+ # the persisted and unpersisted documents.
467
+ #
468
+ # @example Get the size.
469
+ # enumerable.size
470
+ #
471
+ # @return [ Integer ] The size of the enumerable.
472
+ #
473
+ # @since 2.1.0
474
+ def size
475
+ count = (_unloaded ? _unloaded.count : _loaded.count)
476
+ if count.zero?
477
+ count + _added.count
478
+ else
479
+ count + _added.values.count { |d| d.new_record? }
482
480
  end
481
+ end
483
482
 
484
- alias :length :size
485
-
486
- # Send #to_json to the entries.
487
- #
488
- # @example Get the enumerable as json.
489
- # enumerable.to_json
490
- #
491
- # @param [ Hash ] options Optional parameters.
492
- #
493
- # @return [ String ] The entries all _loaded as a string.
494
- #
495
- # @since 2.2.0
496
- def to_json(options = {})
497
- entries.to_json(options)
498
- end
483
+ alias :length :size
484
+
485
+ # Send #to_json to the entries.
486
+ #
487
+ # @example Get the enumerable as json.
488
+ # enumerable.to_json
489
+ #
490
+ # @param [ Hash ] options Optional parameters.
491
+ #
492
+ # @return [ String ] The entries all _loaded as a string.
493
+ #
494
+ # @since 2.2.0
495
+ def to_json(options = {})
496
+ entries.to_json(options)
497
+ end
499
498
 
500
- # Send #as_json to the entries, without encoding.
501
- #
502
- # @example Get the enumerable as json.
503
- # enumerable.as_json
504
- #
505
- # @param [ Hash ] options Optional parameters.
506
- #
507
- # @return [ Hash ] The entries all _loaded as a hash.
508
- #
509
- # @since 2.2.0
510
- def as_json(options = {})
511
- entries.as_json(options)
512
- end
499
+ # Send #as_json to the entries, without encoding.
500
+ #
501
+ # @example Get the enumerable as json.
502
+ # enumerable.as_json
503
+ #
504
+ # @param [ Hash ] options Optional parameters.
505
+ #
506
+ # @return [ Hash ] The entries all _loaded as a hash.
507
+ #
508
+ # @since 2.2.0
509
+ def as_json(options = {})
510
+ entries.as_json(options)
511
+ end
513
512
 
514
- # Return all the unique documents in the enumerable.
515
- #
516
- # @note This operation loads all documents from the database.
517
- #
518
- # @example Get all the unique documents.
519
- # enumerable.uniq
520
- #
521
- # @return [ Array<Document> ] The unique documents.
522
- #
523
- # @since 2.1.0
524
- def uniq
525
- entries.uniq
526
- end
513
+ # Return all the unique documents in the enumerable.
514
+ #
515
+ # @note This operation loads all documents from the database.
516
+ #
517
+ # @example Get all the unique documents.
518
+ # enumerable.uniq
519
+ #
520
+ # @return [ Array<Document> ] The unique documents.
521
+ #
522
+ # @since 2.1.0
523
+ def uniq
524
+ entries.uniq
525
+ end
527
526
 
528
- private
527
+ private
529
528
 
530
- def set_base(document)
531
- if @_association.is_a?(Referenced::HasMany)
532
- document.set_relation(@_association.inverse, @_base) if @_association
533
- end
529
+ def set_base(document)
530
+ if @_association.is_a?(Referenced::HasMany)
531
+ document.set_relation(@_association.inverse, @_base) if @_association
534
532
  end
533
+ end
535
534
 
536
- def method_missing(name, *args, &block)
537
- entries.send(name, *args, &block)
538
- end
535
+ def method_missing(name, *args, &block)
536
+ entries.send(name, *args, &block)
537
+ end
539
538
 
540
- def unloaded_documents
541
- if _unloaded.selector._mongoid_unsatisfiable_criteria?
542
- []
543
- else
544
- _unloaded
545
- end
539
+ def unloaded_documents
540
+ if _unloaded.selector._mongoid_unsatisfiable_criteria?
541
+ []
542
+ else
543
+ _unloaded
546
544
  end
547
545
  end
548
546
  end