mongoid 7.2.0.rc1 → 7.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (214) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Rakefile +45 -10
  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/buildable.rb +8 -0
  19. data/lib/mongoid/association/referenced/has_one/nested_builder.rb +2 -2
  20. data/lib/mongoid/association/referenced/has_one/proxy.rb +6 -1
  21. data/lib/mongoid/attributes.rb +32 -14
  22. data/lib/mongoid/attributes/projector.rb +120 -0
  23. data/lib/mongoid/cacheable.rb +2 -2
  24. data/lib/mongoid/clients.rb +1 -1
  25. data/lib/mongoid/clients/factory.rb +22 -8
  26. data/lib/mongoid/config.rb +19 -2
  27. data/lib/mongoid/contextual/aggregable/mongo.rb +10 -8
  28. data/lib/mongoid/copyable.rb +6 -2
  29. data/lib/mongoid/criteria.rb +4 -5
  30. data/lib/mongoid/criteria/findable.rb +1 -1
  31. data/lib/mongoid/criteria/queryable/expandable.rb +0 -24
  32. data/lib/mongoid/criteria/queryable/extensions.rb +0 -4
  33. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +1 -1
  34. data/lib/mongoid/criteria/queryable/mergeable.rb +46 -20
  35. data/lib/mongoid/criteria/queryable/selectable.rb +8 -8
  36. data/lib/mongoid/criteria/queryable/selector.rb +0 -4
  37. data/lib/mongoid/document.rb +4 -17
  38. data/lib/mongoid/errors/delete_restriction.rb +8 -9
  39. data/lib/mongoid/evolvable.rb +1 -1
  40. data/lib/mongoid/extensions.rb +1 -0
  41. data/lib/mongoid/extensions/boolean.rb +1 -2
  42. data/lib/mongoid/extensions/false_class.rb +1 -1
  43. data/lib/mongoid/extensions/hash.rb +2 -2
  44. data/lib/mongoid/extensions/true_class.rb +1 -1
  45. data/lib/mongoid/fields.rb +46 -5
  46. data/lib/mongoid/inspectable.rb +1 -1
  47. data/lib/mongoid/interceptable.rb +3 -1
  48. data/lib/mongoid/matcher.rb +26 -43
  49. data/lib/mongoid/matcher/bits.rb +41 -0
  50. data/lib/mongoid/matcher/bits_all_clear.rb +20 -0
  51. data/lib/mongoid/matcher/bits_all_set.rb +20 -0
  52. data/lib/mongoid/matcher/bits_any_clear.rb +20 -0
  53. data/lib/mongoid/matcher/bits_any_set.rb +20 -0
  54. data/lib/mongoid/matcher/elem_match.rb +2 -1
  55. data/lib/mongoid/matcher/expression.rb +9 -14
  56. data/lib/mongoid/matcher/field_expression.rb +4 -5
  57. data/lib/mongoid/matcher/field_operator.rb +13 -11
  58. data/lib/mongoid/matcher/mod.rb +17 -0
  59. data/lib/mongoid/matcher/type.rb +99 -0
  60. data/lib/mongoid/persistable/deletable.rb +1 -2
  61. data/lib/mongoid/persistable/destroyable.rb +8 -2
  62. data/lib/mongoid/persistable/updatable.rb +27 -2
  63. data/lib/mongoid/query_cache.rb +35 -29
  64. data/lib/mongoid/reloadable.rb +5 -0
  65. data/lib/mongoid/selectable.rb +5 -7
  66. data/lib/mongoid/shardable.rb +21 -5
  67. data/lib/mongoid/stringified_symbol.rb +53 -0
  68. data/lib/mongoid/touchable.rb +23 -4
  69. data/lib/mongoid/version.rb +1 -1
  70. data/lib/rails/generators/mongoid/config/config_generator.rb +8 -1
  71. data/spec/README.md +19 -4
  72. data/spec/integration/app_spec.rb +175 -88
  73. data/spec/integration/associations/embeds_many_spec.rb +68 -0
  74. data/spec/integration/associations/embeds_one_spec.rb +24 -0
  75. data/spec/integration/associations/has_many_spec.rb +60 -0
  76. data/spec/integration/associations/has_one_spec.rb +108 -0
  77. data/spec/integration/callbacks_models.rb +49 -0
  78. data/spec/integration/callbacks_spec.rb +216 -0
  79. data/spec/integration/criteria/date_field_spec.rb +1 -1
  80. data/spec/integration/document_spec.rb +30 -0
  81. data/spec/integration/matcher_operator_data/bits_all_clear.yml +159 -0
  82. data/spec/integration/matcher_operator_data/bits_all_set.yml +159 -0
  83. data/spec/integration/matcher_operator_data/bits_any_clear.yml +159 -0
  84. data/spec/integration/matcher_operator_data/bits_any_set.yml +159 -0
  85. data/spec/integration/matcher_operator_data/comment.yml +22 -0
  86. data/spec/integration/matcher_operator_data/elem_match.yml +46 -0
  87. data/spec/integration/matcher_operator_data/gt_types.yml +63 -0
  88. data/spec/integration/matcher_operator_data/gte_types.yml +15 -0
  89. data/spec/integration/matcher_operator_data/implicit_traversal.yml +96 -0
  90. data/spec/integration/matcher_operator_data/in.yml +16 -0
  91. data/spec/integration/matcher_operator_data/lt_types.yml +15 -0
  92. data/spec/integration/matcher_operator_data/lte_types.yml +15 -0
  93. data/spec/integration/matcher_operator_data/mod.yml +55 -0
  94. data/spec/integration/matcher_operator_data/ne_types.yml +15 -0
  95. data/spec/integration/matcher_operator_data/type.yml +70 -0
  96. data/spec/integration/matcher_operator_data/type_array.yml +16 -0
  97. data/spec/integration/matcher_operator_data/type_binary.yml +18 -0
  98. data/spec/integration/matcher_operator_data/type_boolean.yml +39 -0
  99. data/spec/integration/matcher_operator_data/type_code.yml +26 -0
  100. data/spec/integration/matcher_operator_data/type_code_with_scope.yml +26 -0
  101. data/spec/integration/matcher_operator_data/type_date.yml +39 -0
  102. data/spec/integration/matcher_operator_data/type_db_pointer.yml +19 -0
  103. data/spec/integration/matcher_operator_data/type_decimal.yml +40 -0
  104. data/spec/integration/matcher_operator_data/type_double.yml +15 -0
  105. data/spec/integration/matcher_operator_data/type_int32.yml +33 -0
  106. data/spec/integration/matcher_operator_data/type_int64.yml +33 -0
  107. data/spec/integration/matcher_operator_data/type_max_key.yml +17 -0
  108. data/spec/integration/matcher_operator_data/type_min_key.yml +17 -0
  109. data/spec/integration/matcher_operator_data/type_null.yml +23 -0
  110. data/spec/integration/matcher_operator_data/type_object.yml +23 -0
  111. data/spec/integration/matcher_operator_data/type_object_id.yml +25 -0
  112. data/spec/integration/matcher_operator_data/type_regex.yml +44 -0
  113. data/spec/integration/matcher_operator_data/type_string.yml +15 -0
  114. data/spec/integration/matcher_operator_data/type_symbol.yml +32 -0
  115. data/spec/integration/matcher_operator_data/type_timestamp.yml +25 -0
  116. data/spec/integration/matcher_operator_data/type_undefined.yml +17 -0
  117. data/spec/integration/stringified_symbol_field_spec.rb +190 -0
  118. data/spec/lite_spec_helper.rb +9 -7
  119. data/spec/mongoid/association/depending_spec.rb +391 -352
  120. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +50 -0
  121. data/spec/mongoid/association/nested/one_spec.rb +18 -14
  122. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +25 -8
  123. data/spec/mongoid/association/referenced/has_and_belongs_to_many/binding_spec.rb +1 -1
  124. data/spec/mongoid/association/referenced/has_many/binding_spec.rb +1 -1
  125. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +1 -1
  126. data/spec/mongoid/association/referenced/has_many_models.rb +12 -0
  127. data/spec/mongoid/association/referenced/has_one_models.rb +20 -0
  128. data/spec/mongoid/association/referenced/has_one_spec.rb +1 -1
  129. data/spec/mongoid/atomic/paths_spec.rb +105 -12
  130. data/spec/mongoid/attributes/projector_data/embedded.yml +105 -0
  131. data/spec/mongoid/attributes/projector_data/fields.yml +93 -0
  132. data/spec/mongoid/attributes/projector_spec.rb +41 -0
  133. data/spec/mongoid/attributes_spec.rb +333 -0
  134. data/spec/mongoid/clients/factory_spec.rb +48 -0
  135. data/spec/mongoid/config_spec.rb +32 -0
  136. data/spec/mongoid/contextual/atomic_spec.rb +17 -4
  137. data/spec/mongoid/contextual/mongo_spec.rb +2 -2
  138. data/spec/mongoid/copyable_spec.rb +44 -17
  139. data/spec/mongoid/copyable_spec_models.rb +14 -0
  140. data/spec/mongoid/criteria/modifiable_spec.rb +1 -1
  141. data/spec/mongoid/criteria/queryable/expandable_spec.rb +0 -73
  142. data/spec/mongoid/criteria/queryable/extensions/boolean_spec.rb +1 -1
  143. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +105 -7
  144. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +265 -24
  145. data/spec/mongoid/criteria/queryable/selectable_shared_examples.rb +39 -0
  146. data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -565
  147. data/spec/mongoid/criteria/queryable/selectable_where_spec.rb +590 -0
  148. data/spec/mongoid/criteria_projection_spec.rb +411 -0
  149. data/spec/mongoid/criteria_spec.rb +0 -275
  150. data/spec/mongoid/document_fields_spec.rb +26 -0
  151. data/spec/mongoid/document_spec.rb +13 -13
  152. data/spec/mongoid/equality_spec.rb +0 -1
  153. data/spec/mongoid/errors/delete_restriction_spec.rb +1 -1
  154. data/spec/mongoid/extensions/false_class_spec.rb +1 -1
  155. data/spec/mongoid/extensions/string_spec.rb +5 -5
  156. data/spec/mongoid/extensions/stringified_symbol_spec.rb +85 -0
  157. data/spec/mongoid/extensions/true_class_spec.rb +1 -1
  158. data/spec/mongoid/fields/localized_spec.rb +4 -4
  159. data/spec/mongoid/fields_spec.rb +4 -4
  160. data/spec/mongoid/inspectable_spec.rb +12 -4
  161. data/spec/mongoid/matcher/extract_attribute_data/numeric_keys.yml +104 -0
  162. data/spec/mongoid/matcher/extract_attribute_data/traversal.yml +68 -88
  163. data/spec/mongoid/matcher/extract_attribute_spec.rb +3 -13
  164. data/spec/mongoid/persistable/deletable_spec.rb +175 -1
  165. data/spec/mongoid/persistable/destroyable_spec.rb +191 -3
  166. data/spec/mongoid/persistable/savable_spec.rb +3 -5
  167. data/spec/mongoid/persistable/settable_spec.rb +30 -0
  168. data/spec/mongoid/persistable/upsertable_spec.rb +1 -1
  169. data/spec/mongoid/query_cache_middleware_spec.rb +8 -0
  170. data/spec/mongoid/reloadable_spec.rb +18 -1
  171. data/spec/mongoid/shardable_spec.rb +44 -0
  172. data/spec/mongoid/touchable_spec.rb +104 -16
  173. data/spec/mongoid/touchable_spec_models.rb +52 -0
  174. data/spec/mongoid/validatable_spec.rb +1 -1
  175. data/spec/shared/LICENSE +20 -0
  176. data/spec/shared/bin/get-mongodb-download-url +17 -0
  177. data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
  178. data/spec/shared/lib/mrss/cluster_config.rb +221 -0
  179. data/spec/shared/lib/mrss/constraints.rb +354 -0
  180. data/spec/shared/lib/mrss/docker_runner.rb +265 -0
  181. data/spec/shared/lib/mrss/lite_constraints.rb +191 -0
  182. data/spec/shared/lib/mrss/server_version_registry.rb +115 -0
  183. data/spec/shared/lib/mrss/spec_organizer.rb +162 -0
  184. data/spec/shared/lib/mrss/utils.rb +15 -0
  185. data/spec/shared/share/Dockerfile.erb +231 -0
  186. data/spec/shared/shlib/distro.sh +73 -0
  187. data/spec/shared/shlib/server.sh +290 -0
  188. data/spec/shared/shlib/set_env.sh +128 -0
  189. data/spec/spec_helper.rb +7 -1
  190. data/spec/support/client_registry.rb +9 -0
  191. data/spec/support/constraints.rb +0 -226
  192. data/spec/support/models/bolt.rb +8 -0
  193. data/spec/support/models/customer.rb +11 -0
  194. data/spec/support/models/customer_address.rb +12 -0
  195. data/spec/support/models/dictionary.rb +6 -0
  196. data/spec/support/models/hole.rb +13 -0
  197. data/spec/support/models/mop.rb +9 -0
  198. data/spec/support/models/nut.rb +8 -0
  199. data/spec/support/models/order.rb +11 -0
  200. data/spec/support/models/person.rb +8 -0
  201. data/spec/support/models/sealer.rb +8 -0
  202. data/spec/support/models/series.rb +1 -0
  203. data/spec/support/models/shirt.rb +12 -0
  204. data/spec/support/models/spacer.rb +8 -0
  205. data/spec/support/models/threadlocker.rb +8 -0
  206. data/spec/support/models/washer.rb +8 -0
  207. data/spec/support/models/wiki_page.rb +1 -0
  208. data/spec/support/spec_config.rb +8 -0
  209. metadata +655 -507
  210. metadata.gz.sig +0 -0
  211. data/spec/support/child_process_helper.rb +0 -79
  212. data/spec/support/cluster_config.rb +0 -158
  213. data/spec/support/lite_constraints.rb +0 -22
  214. data/spec/support/spec_organizer.rb +0 -130
@@ -220,7 +220,7 @@ module Mongoid
220
220
  #
221
221
  # @since 2.0.0.beta.1
222
222
  def initialize(base, target, association)
223
- enum = HasMany::Targets::Enumerable.new(target, base, association)
223
+ enum = HasMany::Enumerable.new(target, base, association)
224
224
  init(base, enum, association) do
225
225
  raise_mixed if klass.embedded? && !klass.cyclic?
226
226
  end
@@ -367,7 +367,7 @@ module Mongoid
367
367
  document.persisted? &&
368
368
  document._association &&
369
369
  document.respond_to?(document._association.foreign_key) &&
370
- document.__send__(document._association.foreign_key) == _base.id
370
+ document.__send__(document._association.foreign_key) == _base._id
371
371
  end
372
372
 
373
373
  # Instantiate the binding associated with this association.
@@ -35,6 +35,14 @@ module Mongoid
35
35
  private
36
36
 
37
37
  def clear_associated(object)
38
+ unless inverse
39
+ raise Errors::InverseNotFound.new(
40
+ @owner_class,
41
+ name,
42
+ object.class,
43
+ foreign_key,
44
+ )
45
+ end
38
46
  if object && (associated = object.send(inverse))
39
47
  associated.substitute(nil)
40
48
  end
@@ -69,7 +69,7 @@ module Mongoid
69
69
  #
70
70
  # @since 2.0.0
71
71
  def acceptable_id?
72
- id = convert_id(existing.class, attributes[:id])
72
+ id = convert_id(existing.class, attributes[:_id])
73
73
  existing._id == id || id.nil? || (existing._id != id && update_only?)
74
74
  end
75
75
 
@@ -82,7 +82,7 @@ module Mongoid
82
82
  #
83
83
  # @since 2.0.0
84
84
  def delete?
85
- destroyable? && !attributes[:id].nil?
85
+ destroyable? && !attributes[:_id].nil?
86
86
  end
87
87
 
88
88
  # Can the existing association potentially be destroyed?
@@ -54,9 +54,14 @@ module Mongoid
54
54
  #
55
55
  # @since 2.0.0.rc.1
56
56
  def substitute(replacement)
57
+ # If the same object currently associated is being assigned,
58
+ # rebind the association and save the target but do not destroy
59
+ # the target.
60
+
57
61
  unbind_one
58
62
  if persistable?
59
- if _association.destructive?
63
+ # TODO can this entire method be skipped if self == replacement?
64
+ if _association.destructive? && self != replacement
60
65
  send(_association.dependent)
61
66
  else
62
67
  save if persisted?
@@ -5,6 +5,7 @@ require "active_model/attribute_methods"
5
5
  require "mongoid/attributes/dynamic"
6
6
  require "mongoid/attributes/nested"
7
7
  require "mongoid/attributes/processing"
8
+ require "mongoid/attributes/projector"
8
9
  require "mongoid/attributes/readonly"
9
10
 
10
11
  module Mongoid
@@ -160,6 +161,11 @@ module Mongoid
160
161
  # @since 1.0.0
161
162
  def write_attribute(name, value)
162
163
  field_name = database_field_name(name)
164
+
165
+ if attribute_missing?(field_name)
166
+ raise ActiveModel::MissingAttributeError, "Missing attribute: '#{name}'"
167
+ end
168
+
163
169
  if attribute_writable?(field_name)
164
170
  _assigning do
165
171
  validate_attribute_value(field_name, value)
@@ -177,6 +183,8 @@ module Mongoid
177
183
  end
178
184
  typed_value
179
185
  end
186
+ else
187
+ # TODO: MONGOID-5072
180
188
  end
181
189
  end
182
190
  alias :[]= :write_attribute
@@ -231,11 +239,7 @@ module Mongoid
231
239
  #
232
240
  # @since 4.0.0
233
241
  def attribute_missing?(name)
234
- selection = __selected_fields
235
- return false unless selection
236
- field = fields[name]
237
- (selection.values.first == 0 && selection_excluded?(name, selection, field)) ||
238
- (selection.values.first == 1 && !selection_included?(name, selection, field))
242
+ !Projector.new(__selected_fields).attribute_or_path_allowed?(name)
239
243
  end
240
244
 
241
245
  # Return type-casted attributes.
@@ -252,14 +256,6 @@ module Mongoid
252
256
 
253
257
  private
254
258
 
255
- def selection_excluded?(name, selection, field)
256
- selection[name] == 0
257
- end
258
-
259
- def selection_included?(name, selection, field)
260
- selection.key?(name) || selection.keys.collect { |k| k.partition('.').first }.include?(name)
261
- end
262
-
263
259
  # Does the string contain dot syntax for accessing hashes?
264
260
  #
265
261
  # @api private
@@ -293,9 +289,11 @@ module Mongoid
293
289
 
294
290
  def read_raw_attribute(name)
295
291
  normalized = database_field_name(name.to_s)
292
+
296
293
  if attribute_missing?(normalized)
297
- raise ActiveModel::MissingAttributeError, "Missing attribute: '#{name}'."
294
+ raise ActiveModel::MissingAttributeError, "Missing attribute: '#{name}'"
298
295
  end
296
+
299
297
  if hash_dot_syntax?(normalized)
300
298
  attributes.__nested__(normalized)
301
299
  else
@@ -334,6 +332,26 @@ module Mongoid
334
332
  alias_method "#{name}_will_change!", "#{original}_will_change!"
335
333
  alias_method "#{name}_before_type_cast", "#{original}_before_type_cast"
336
334
  end
335
+
336
+ # Removes a field alias.
337
+ #
338
+ # @param [ Symbol ] name The aliased field name to remove.
339
+ def unalias_attribute(name)
340
+ unless aliased_fields.delete(name.to_s)
341
+ raise AttributeError, "Field #{name} is not an aliased field"
342
+ end
343
+
344
+ remove_method name
345
+ remove_method "#{name}="
346
+ remove_method "#{name}?"
347
+ remove_method "#{name}_change"
348
+ remove_method "#{name}_changed?"
349
+ remove_method "reset_#{name}!"
350
+ remove_method "reset_#{name}_to_default!"
351
+ remove_method "#{name}_was"
352
+ remove_method "#{name}_will_change!"
353
+ remove_method "#{name}_before_type_cast"
354
+ end
337
355
  end
338
356
 
339
357
  private
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ module Mongoid
5
+ module Attributes
6
+
7
+ # This module defines projection helpers.
8
+ #
9
+ # Projection rules are rather non-trivial. See
10
+ # https://docs.mongodb.com/manual/reference/method/db.collection.find/#find-projection
11
+ # for server documentation.
12
+ # 4.4 server (and presumably all older ones) requires that a projection
13
+ # for content fields is either exclusionary or inclusionary, i.e. one
14
+ # cannot mix exclusions and inclusions in the same query.
15
+ # However, _id can be excluded in a projection that includes content
16
+ # fields.
17
+ # Integer projection values other than 0 and 1 aren't officially
18
+ # documented as of this writing; see DOCSP-15266.
19
+ # 4.4 server also allows nested hash projection specification
20
+ # in addition to dot notation, which I assume Mongoid doesn't handle yet.
21
+ #
22
+ # @api private
23
+ class Projector
24
+ def initialize(projection)
25
+ if projection
26
+ @content_projection = projection.dup
27
+ @content_projection.delete('_id')
28
+ @id_projection_value = projection['_id']
29
+ else
30
+ @content_projection = nil
31
+ @id_projection_value = nil
32
+ end
33
+ end
34
+
35
+ attr_reader :id_projection_value
36
+ attr_reader :content_projection
37
+
38
+ # Determine if the specified attribute, or a dot notation path, is allowed
39
+ # by the configured projection, if any.
40
+ #
41
+ # If there is no configured projection, returns true.
42
+ #
43
+ # @param [ String ] name The name of the attribute or a dot notation path.
44
+ #
45
+ # @return [ true, false ] Whether the attribute is allowed by projection.
46
+ #
47
+ # @api private
48
+ def attribute_or_path_allowed?(name)
49
+ # Special handling for _id.
50
+ if name == '_id'
51
+ result = unless id_projection_value.nil?
52
+ value_inclusionary?(id_projection_value)
53
+ else
54
+ true
55
+ end
56
+ return result
57
+ end
58
+
59
+ if content_projection.nil?
60
+ # No projection (as opposed to an empty projection).
61
+ # All attributes are allowed.
62
+ return true
63
+ end
64
+
65
+ # Find an item which matches or is a parent of the requested name/path.
66
+ # This handles the case when, for example, the projection was
67
+ # {foo: true} and we want to know if foo.bar is allowed.
68
+ item, value = content_projection.detect do |path, value|
69
+ (name + '.').start_with?(path + '.')
70
+ end
71
+ if item
72
+ return value_inclusionary?(value)
73
+ end
74
+
75
+ if content_inclusionary?
76
+ # Find an item which would be a strict child of the requested name/path.
77
+ # This handles the case when, for example, the projection was
78
+ # {"foo.bar" => true} and we want to know if foo is allowed.
79
+ # (It is as a container of bars.)
80
+ item, value = content_projection.detect do |path, value|
81
+ (path + '.').start_with?(name + '.')
82
+ end
83
+ if item
84
+ return true
85
+ end
86
+ end
87
+
88
+ !content_inclusionary?
89
+ end
90
+
91
+ private
92
+
93
+ # Determines whether the projection for content fields is inclusionary.
94
+ #
95
+ # An empty projection is inclusionary.
96
+ def content_inclusionary?
97
+ if content_projection.empty?
98
+ return value_inclusionary?(id_projection_value)
99
+ end
100
+
101
+ value_inclusionary?(content_projection.values.first)
102
+ end
103
+
104
+ def value_inclusionary?(value)
105
+ case value
106
+ when Integer
107
+ value >= 1
108
+ when true
109
+ true
110
+ when false
111
+ false
112
+ else
113
+ # The various expressions that are permitted as projection arguments
114
+ # imply an inclusionary projection.
115
+ true
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -31,8 +31,8 @@ module Mongoid
31
31
  # @since 2.4.0
32
32
  def cache_key
33
33
  return "#{model_key}/new" if new_record?
34
- return "#{model_key}/#{id}-#{updated_at.utc.to_s(cache_timestamp_format)}" if do_or_do_not(:updated_at)
35
- "#{model_key}/#{id}"
34
+ return "#{model_key}/#{_id}-#{updated_at.utc.to_s(cache_timestamp_format)}" if do_or_do_not(:updated_at)
35
+ "#{model_key}/#{_id}"
36
36
  end
37
37
  end
38
38
  end
@@ -59,7 +59,7 @@ module Mongoid
59
59
  # @example Get a client with the name.
60
60
  # Mongoid::Clients.with_name(:replica)
61
61
  #
62
- # @param [ Symbol ] name The name of the client.
62
+ # @param [ String | Symbol ] name The name of the client.
63
63
  #
64
64
  # @return [ Mongo::Client ] The named client.
65
65
  #
@@ -5,6 +5,7 @@ module Mongoid
5
5
  module Clients
6
6
  module Factory
7
7
  extend self
8
+ extend Loggable
8
9
 
9
10
  # Create a new client given the named configuration. If no name is
10
11
  # provided, return a new client with the default configuration. If a
@@ -59,12 +60,20 @@ module Mongoid
59
60
  # @since 3.0.0
60
61
  def create_client(configuration)
61
62
  raise Errors::NoClientsConfig.new unless configuration
62
- if configuration[:uri]
63
- Mongo::Client.new(configuration[:uri], options(configuration))
63
+ config = configuration.dup
64
+ uri = config.delete(:uri)
65
+ database = config.delete(:database)
66
+ hosts = config.delete(:hosts)
67
+ opts = config.delete(:options) || {}
68
+ unless config.empty?
69
+ default_logger.warn("Unknown config options detected: #{config}.")
70
+ end
71
+ if uri
72
+ Mongo::Client.new(uri, options(opts))
64
73
  else
65
74
  Mongo::Client.new(
66
- configuration[:hosts],
67
- options(configuration).merge(database: configuration[:database])
75
+ hosts,
76
+ options(opts).merge(database: database)
68
77
  )
69
78
  end
70
79
  end
@@ -78,9 +87,14 @@ module Mongoid
78
87
  Mongo::VERSION.split('.')[0...2].map(&:to_i)
79
88
  end
80
89
 
81
- def options(configuration)
82
- config = configuration.dup
83
- options = config.delete(:options) || {}
90
+ # Prepare options for Mongo::Client based on Mongoid client configuration.
91
+ #
92
+ # @param [ Hash ] opts Parameters from options section of Mongoid client configuration.
93
+ # @return [ Hash ] Options that should be passed to Mongo::Client constructor.
94
+ #
95
+ # @api private
96
+ def options(opts)
97
+ options = opts.dup
84
98
  options[:platform] = PLATFORM_DETAILS
85
99
  options[:app_name] = Mongoid::Config.app_name if Mongoid::Config.app_name
86
100
  if (driver_version <=> [2, 13]) >= 0
@@ -91,7 +105,7 @@ module Mongoid
91
105
  end
92
106
  options[:wrapping_libraries] = wrap_lib
93
107
  end
94
- options.reject{ |k, v| k == :hosts }.to_hash.symbolize_keys!
108
+ options.reject{ |k, _v| k == :hosts }.to_hash.symbolize_keys!
95
109
  end
96
110
  end
97
111
  end
@@ -220,7 +220,7 @@ module Mongoid
220
220
  #
221
221
  # @since 2.0.2
222
222
  def purge!
223
- Clients.default.database.collections.each(&:drop) and true
223
+ global_client.database.collections.each(&:drop) and true
224
224
  end
225
225
 
226
226
  # Truncate all data in all collections, but not the indexes.
@@ -234,7 +234,7 @@ module Mongoid
234
234
  #
235
235
  # @since 2.0.2
236
236
  def truncate!
237
- Clients.default.database.collections.each do |collection|
237
+ global_client.database.collections.each do |collection|
238
238
  collection.find.delete_many
239
239
  end and true
240
240
  end
@@ -305,5 +305,22 @@ module Mongoid
305
305
  Validators::Client.validate(c)
306
306
  @clients = c
307
307
  end
308
+
309
+ # Get database client that respects global overrides
310
+ # Config.override_database and Config.override_client.
311
+ #
312
+ # @return [Mongo::Client] Client according to global overrides.
313
+ def global_client
314
+ client = if Threaded.client_override
315
+ Clients.with_name(Threaded.client_override)
316
+ else
317
+ Clients.default
318
+ end
319
+ if Threaded.database_override
320
+ client.use(Threaded.database_override)
321
+ else
322
+ client
323
+ end
324
+ end
308
325
  end
309
326
  end
@@ -11,17 +11,19 @@ module Mongoid
11
11
  #
12
12
  # @example Get all the aggregate values.
13
13
  # aggregable.aggregates(:likes)
14
+ # # => {
15
+ # # "count" => 2.0,
16
+ # # "max" => 1000.0,
17
+ # # "min" => 500.0,
18
+ # # "sum" => 1500.0,
19
+ # # "avg" => 750.0
20
+ # # }
14
21
  #
15
22
  # @param [ String, Symbol ] field The field name.
16
23
  #
17
- # @return [ Hash ] count is a number of documents with the provided field. If there're none, then count is 0 and max, min, sum, avg are nil.
18
- # {
19
- # "count" => 2.0,
20
- # "max" => 1000.0,
21
- # "min" => 500.0,
22
- # "sum" => 1500.0,
23
- # "avg" => 750.0
24
- # }
24
+ # @return [ Hash ] count is a number of documents with the provided
25
+ # field. If there're none, then count is 0 and max, min, sum, avg
26
+ # are nil.
25
27
  #
26
28
  # @since 3.0.0
27
29
  def aggregates(field)
@@ -21,10 +21,14 @@ module Mongoid
21
21
  # @note This next line is here to address #2704, even though having an
22
22
  # _id and id field in the document would cause problems with Mongoid
23
23
  # elsewhere.
24
- attrs = clone_document.except("_id", "id")
24
+ attrs = clone_document.except(*self.class.id_fields)
25
25
  dynamic_attrs = {}
26
+ _attribute_names = self.attribute_names
26
27
  attrs.reject! do |attr_name, value|
27
- dynamic_attrs.merge!(attr_name => value) unless self.attribute_names.include?(attr_name)
28
+ unless _attribute_names.include?(attr_name)
29
+ dynamic_attrs[attr_name] = value
30
+ true
31
+ end
28
32
  end
29
33
  self.class.new(attrs).tap do |object|
30
34
  dynamic_attrs.each do |attr_name, value|