mongoid 7.2.0 → 7.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/Rakefile +16 -0
  4. data/lib/config/locales/en.yml +2 -2
  5. data/lib/mongoid/association/accessors.rb +1 -1
  6. data/lib/mongoid/association/constrainable.rb +1 -1
  7. data/lib/mongoid/association/depending.rb +4 -4
  8. data/lib/mongoid/association/embedded/batchable.rb +1 -1
  9. data/lib/mongoid/association/embedded/embedded_in.rb +1 -1
  10. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +10 -3
  11. data/lib/mongoid/association/nested/many.rb +1 -1
  12. data/lib/mongoid/association/nested/one.rb +4 -2
  13. data/lib/mongoid/association/proxy.rb +6 -1
  14. data/lib/mongoid/association/referenced/auto_save.rb +2 -2
  15. data/lib/mongoid/association/referenced/has_many/enumerable.rb +493 -495
  16. data/lib/mongoid/association/referenced/has_many/proxy.rb +2 -2
  17. data/lib/mongoid/association/referenced/has_one/nested_builder.rb +2 -2
  18. data/lib/mongoid/attributes/projector.rb +120 -0
  19. data/lib/mongoid/attributes.rb +32 -14
  20. data/lib/mongoid/cacheable.rb +2 -2
  21. data/lib/mongoid/clients/factory.rb +22 -8
  22. data/lib/mongoid/clients.rb +1 -1
  23. data/lib/mongoid/config.rb +19 -2
  24. data/lib/mongoid/contextual/aggregable/mongo.rb +10 -8
  25. data/lib/mongoid/copyable.rb +1 -1
  26. data/lib/mongoid/criteria/findable.rb +1 -1
  27. data/lib/mongoid/criteria/queryable/expandable.rb +0 -24
  28. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +1 -1
  29. data/lib/mongoid/criteria/queryable/extensions.rb +0 -4
  30. data/lib/mongoid/criteria/queryable/mergeable.rb +46 -20
  31. data/lib/mongoid/criteria/queryable/selectable.rb +8 -8
  32. data/lib/mongoid/criteria/queryable/selector.rb +0 -4
  33. data/lib/mongoid/criteria.rb +4 -5
  34. data/lib/mongoid/document.rb +4 -17
  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/interceptable.rb +3 -1
  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/elem_match.rb +2 -1
  50. data/lib/mongoid/matcher/expression.rb +9 -14
  51. data/lib/mongoid/matcher/field_expression.rb +4 -5
  52. data/lib/mongoid/matcher/field_operator.rb +13 -11
  53. data/lib/mongoid/matcher/mod.rb +17 -0
  54. data/lib/mongoid/matcher/type.rb +99 -0
  55. data/lib/mongoid/matcher.rb +26 -43
  56. data/lib/mongoid/persistable/deletable.rb +1 -2
  57. data/lib/mongoid/persistable/destroyable.rb +8 -2
  58. data/lib/mongoid/persistable/updatable.rb +27 -2
  59. data/lib/mongoid/query_cache.rb +35 -29
  60. data/lib/mongoid/reloadable.rb +5 -0
  61. data/lib/mongoid/selectable.rb +5 -7
  62. data/lib/mongoid/shardable.rb +21 -5
  63. data/lib/mongoid/touchable.rb +23 -4
  64. data/lib/mongoid/version.rb +1 -1
  65. data/lib/rails/generators/mongoid/config/config_generator.rb +8 -1
  66. data/spec/integration/app_spec.rb +171 -84
  67. data/spec/integration/associations/embeds_many_spec.rb +44 -0
  68. data/spec/integration/associations/has_one_spec.rb +48 -0
  69. data/spec/integration/callbacks_models.rb +49 -0
  70. data/spec/integration/callbacks_spec.rb +216 -0
  71. data/spec/integration/criteria/date_field_spec.rb +1 -1
  72. data/spec/integration/document_spec.rb +30 -0
  73. data/spec/integration/matcher_operator_data/bits_all_clear.yml +159 -0
  74. data/spec/integration/matcher_operator_data/bits_all_set.yml +159 -0
  75. data/spec/integration/matcher_operator_data/bits_any_clear.yml +159 -0
  76. data/spec/integration/matcher_operator_data/bits_any_set.yml +159 -0
  77. data/spec/integration/matcher_operator_data/comment.yml +22 -0
  78. data/spec/integration/matcher_operator_data/elem_match.yml +46 -0
  79. data/spec/integration/matcher_operator_data/gt_types.yml +63 -0
  80. data/spec/integration/matcher_operator_data/gte_types.yml +15 -0
  81. data/spec/integration/matcher_operator_data/implicit_traversal.yml +96 -0
  82. data/spec/integration/matcher_operator_data/in.yml +16 -0
  83. data/spec/integration/matcher_operator_data/lt_types.yml +15 -0
  84. data/spec/integration/matcher_operator_data/lte_types.yml +15 -0
  85. data/spec/integration/matcher_operator_data/mod.yml +55 -0
  86. data/spec/integration/matcher_operator_data/ne_types.yml +15 -0
  87. data/spec/integration/matcher_operator_data/type.yml +70 -0
  88. data/spec/integration/matcher_operator_data/type_array.yml +16 -0
  89. data/spec/integration/matcher_operator_data/type_binary.yml +18 -0
  90. data/spec/integration/matcher_operator_data/type_boolean.yml +39 -0
  91. data/spec/integration/matcher_operator_data/type_code.yml +26 -0
  92. data/spec/integration/matcher_operator_data/type_code_with_scope.yml +26 -0
  93. data/spec/integration/matcher_operator_data/type_date.yml +39 -0
  94. data/spec/integration/matcher_operator_data/type_db_pointer.yml +19 -0
  95. data/spec/integration/matcher_operator_data/type_decimal.yml +40 -0
  96. data/spec/integration/matcher_operator_data/type_double.yml +15 -0
  97. data/spec/integration/matcher_operator_data/type_int32.yml +33 -0
  98. data/spec/integration/matcher_operator_data/type_int64.yml +33 -0
  99. data/spec/integration/matcher_operator_data/type_max_key.yml +17 -0
  100. data/spec/integration/matcher_operator_data/type_min_key.yml +17 -0
  101. data/spec/integration/matcher_operator_data/type_null.yml +23 -0
  102. data/spec/integration/matcher_operator_data/type_object.yml +23 -0
  103. data/spec/integration/matcher_operator_data/type_object_id.yml +25 -0
  104. data/spec/integration/matcher_operator_data/type_regex.yml +44 -0
  105. data/spec/integration/matcher_operator_data/type_string.yml +15 -0
  106. data/spec/integration/matcher_operator_data/type_symbol.yml +32 -0
  107. data/spec/integration/matcher_operator_data/type_timestamp.yml +25 -0
  108. data/spec/integration/matcher_operator_data/type_undefined.yml +17 -0
  109. data/spec/lite_spec_helper.rb +5 -4
  110. data/spec/mongoid/association/depending_spec.rb +391 -352
  111. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +50 -0
  112. data/spec/mongoid/association/nested/one_spec.rb +18 -14
  113. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +25 -8
  114. data/spec/mongoid/association/referenced/has_and_belongs_to_many/binding_spec.rb +1 -1
  115. data/spec/mongoid/association/referenced/has_many/binding_spec.rb +1 -1
  116. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +1 -1
  117. data/spec/mongoid/association/referenced/has_one_models.rb +8 -0
  118. data/spec/mongoid/atomic/paths_spec.rb +105 -12
  119. data/spec/mongoid/attributes/projector_data/embedded.yml +105 -0
  120. data/spec/mongoid/attributes/projector_data/fields.yml +93 -0
  121. data/spec/mongoid/attributes/projector_spec.rb +41 -0
  122. data/spec/mongoid/attributes_spec.rb +333 -0
  123. data/spec/mongoid/clients/factory_spec.rb +48 -0
  124. data/spec/mongoid/config_spec.rb +32 -0
  125. data/spec/mongoid/contextual/atomic_spec.rb +17 -4
  126. data/spec/mongoid/contextual/mongo_spec.rb +2 -2
  127. data/spec/mongoid/criteria/modifiable_spec.rb +1 -1
  128. data/spec/mongoid/criteria/queryable/expandable_spec.rb +0 -73
  129. data/spec/mongoid/criteria/queryable/extensions/boolean_spec.rb +1 -1
  130. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +105 -7
  131. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +265 -24
  132. data/spec/mongoid/criteria/queryable/selectable_shared_examples.rb +39 -0
  133. data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -565
  134. data/spec/mongoid/criteria/queryable/selectable_where_spec.rb +590 -0
  135. data/spec/mongoid/criteria_projection_spec.rb +411 -0
  136. data/spec/mongoid/criteria_spec.rb +0 -275
  137. data/spec/mongoid/document_fields_spec.rb +26 -0
  138. data/spec/mongoid/document_spec.rb +13 -13
  139. data/spec/mongoid/errors/delete_restriction_spec.rb +1 -1
  140. data/spec/mongoid/extensions/false_class_spec.rb +1 -1
  141. data/spec/mongoid/extensions/string_spec.rb +5 -5
  142. data/spec/mongoid/extensions/true_class_spec.rb +1 -1
  143. data/spec/mongoid/fields/localized_spec.rb +4 -4
  144. data/spec/mongoid/fields_spec.rb +4 -4
  145. data/spec/mongoid/inspectable_spec.rb +12 -4
  146. data/spec/mongoid/matcher/extract_attribute_data/numeric_keys.yml +104 -0
  147. data/spec/mongoid/matcher/extract_attribute_data/traversal.yml +68 -88
  148. data/spec/mongoid/matcher/extract_attribute_spec.rb +3 -13
  149. data/spec/mongoid/persistable/deletable_spec.rb +175 -1
  150. data/spec/mongoid/persistable/destroyable_spec.rb +191 -3
  151. data/spec/mongoid/persistable/savable_spec.rb +3 -5
  152. data/spec/mongoid/persistable/settable_spec.rb +30 -0
  153. data/spec/mongoid/persistable/upsertable_spec.rb +1 -1
  154. data/spec/mongoid/query_cache_middleware_spec.rb +8 -0
  155. data/spec/mongoid/reloadable_spec.rb +18 -1
  156. data/spec/mongoid/shardable_spec.rb +44 -0
  157. data/spec/mongoid/touchable_spec.rb +104 -16
  158. data/spec/mongoid/touchable_spec_models.rb +52 -0
  159. data/spec/mongoid/validatable_spec.rb +1 -1
  160. data/spec/shared/bin/get-mongodb-download-url +17 -0
  161. data/spec/shared/lib/mrss/cluster_config.rb +221 -0
  162. data/spec/shared/lib/mrss/constraints.rb +51 -0
  163. data/spec/shared/lib/mrss/docker_runner.rb +265 -0
  164. data/spec/shared/lib/mrss/lite_constraints.rb +16 -0
  165. data/spec/shared/lib/mrss/server_version_registry.rb +115 -0
  166. data/spec/shared/lib/mrss/spec_organizer.rb +14 -1
  167. data/spec/shared/lib/mrss/utils.rb +15 -0
  168. data/spec/shared/share/Dockerfile.erb +231 -0
  169. data/spec/shared/shlib/distro.sh +73 -0
  170. data/spec/shared/shlib/server.sh +290 -0
  171. data/spec/shared/shlib/set_env.sh +128 -0
  172. data/spec/spec_helper.rb +6 -2
  173. data/spec/support/client_registry.rb +9 -0
  174. data/spec/support/models/bolt.rb +8 -0
  175. data/spec/support/models/customer.rb +11 -0
  176. data/spec/support/models/customer_address.rb +12 -0
  177. data/spec/support/models/dictionary.rb +6 -0
  178. data/spec/support/models/hole.rb +13 -0
  179. data/spec/support/models/mop.rb +9 -0
  180. data/spec/support/models/nut.rb +8 -0
  181. data/spec/support/models/person.rb +6 -0
  182. data/spec/support/models/sealer.rb +8 -0
  183. data/spec/support/models/shirt.rb +12 -0
  184. data/spec/support/models/spacer.rb +8 -0
  185. data/spec/support/models/threadlocker.rb +8 -0
  186. data/spec/support/models/washer.rb +8 -0
  187. data/spec/support/spec_config.rb +8 -0
  188. data.tar.gz.sig +0 -0
  189. metadata +146 -14
  190. metadata.gz.sig +5 -2
  191. data/spec/support/cluster_config.rb +0 -158
@@ -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