mongoid 7.2.6 → 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 (200) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +1 -1
  4. data/Rakefile +16 -0
  5. data/lib/config/locales/en.yml +2 -15
  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 +11 -4
  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 +7 -2
  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 +3 -3
  18. data/lib/mongoid/association/referenced/has_one/nested_builder.rb +2 -2
  19. data/lib/mongoid/association/relatable.rb +0 -2
  20. data/lib/mongoid/attributes/projector.rb +120 -0
  21. data/lib/mongoid/attributes.rb +24 -13
  22. data/lib/mongoid/cacheable.rb +2 -2
  23. data/lib/mongoid/clients/factory.rb +22 -8
  24. data/lib/mongoid/clients.rb +1 -1
  25. data/lib/mongoid/config/environment.rb +1 -9
  26. data/lib/mongoid/config.rb +19 -2
  27. data/lib/mongoid/contextual/aggregable/mongo.rb +10 -8
  28. data/lib/mongoid/contextual/atomic.rb +2 -7
  29. data/lib/mongoid/contextual/none.rb +0 -3
  30. data/lib/mongoid/copyable.rb +1 -1
  31. data/lib/mongoid/criteria/findable.rb +1 -1
  32. data/lib/mongoid/criteria/queryable/expandable.rb +0 -24
  33. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +1 -1
  34. data/lib/mongoid/criteria/queryable/extensions.rb +0 -4
  35. data/lib/mongoid/criteria/queryable/mergeable.rb +46 -20
  36. data/lib/mongoid/criteria/queryable/selectable.rb +10 -10
  37. data/lib/mongoid/criteria/queryable/storable.rb +4 -4
  38. data/lib/mongoid/criteria.rb +5 -6
  39. data/lib/mongoid/document.rb +3 -18
  40. data/lib/mongoid/errors/delete_restriction.rb +8 -9
  41. data/lib/mongoid/errors/mongoid_error.rb +1 -1
  42. data/lib/mongoid/errors.rb +0 -2
  43. data/lib/mongoid/evolvable.rb +1 -1
  44. data/lib/mongoid/extensions/boolean.rb +1 -2
  45. data/lib/mongoid/extensions/false_class.rb +1 -1
  46. data/lib/mongoid/extensions/hash.rb +2 -2
  47. data/lib/mongoid/extensions/true_class.rb +1 -1
  48. data/lib/mongoid/fields.rb +43 -5
  49. data/lib/mongoid/inspectable.rb +1 -1
  50. data/lib/mongoid/interceptable.rb +1 -1
  51. data/lib/mongoid/matcher/bits.rb +41 -0
  52. data/lib/mongoid/matcher/bits_all_clear.rb +20 -0
  53. data/lib/mongoid/matcher/bits_all_set.rb +20 -0
  54. data/lib/mongoid/matcher/bits_any_clear.rb +20 -0
  55. data/lib/mongoid/matcher/bits_any_set.rb +20 -0
  56. data/lib/mongoid/matcher/expression.rb +4 -0
  57. data/lib/mongoid/matcher/field_operator.rb +6 -0
  58. data/lib/mongoid/matcher/mod.rb +17 -0
  59. data/lib/mongoid/matcher/type.rb +99 -0
  60. data/lib/mongoid/matcher.rb +7 -0
  61. data/lib/mongoid/persistable/deletable.rb +1 -2
  62. data/lib/mongoid/persistable/destroyable.rb +8 -2
  63. data/lib/mongoid/persistable/updatable.rb +27 -2
  64. data/lib/mongoid/persistence_context.rb +1 -3
  65. data/lib/mongoid/query_cache.rb +36 -40
  66. data/lib/mongoid/selectable.rb +5 -7
  67. data/lib/mongoid/shardable.rb +21 -5
  68. data/lib/mongoid/tasks/database.rb +1 -1
  69. data/lib/mongoid/touchable.rb +23 -4
  70. data/lib/mongoid/validatable/associated.rb +1 -1
  71. data/lib/mongoid/validatable/presence.rb +3 -3
  72. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  73. data/lib/mongoid/version.rb +1 -1
  74. data/lib/mongoid.rb +0 -1
  75. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +1 -1
  76. data/spec/integration/app_spec.rb +0 -3
  77. data/spec/integration/associations/embeds_many_spec.rb +44 -0
  78. data/spec/integration/associations/has_one_spec.rb +48 -0
  79. data/spec/integration/criteria/date_field_spec.rb +1 -1
  80. data/spec/integration/document_spec.rb +9 -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/in.yml +16 -0
  87. data/spec/integration/matcher_operator_data/mod.yml +55 -0
  88. data/spec/integration/matcher_operator_data/type.yml +70 -0
  89. data/spec/integration/matcher_operator_data/type_array.yml +16 -0
  90. data/spec/integration/matcher_operator_data/type_binary.yml +18 -0
  91. data/spec/integration/matcher_operator_data/type_boolean.yml +39 -0
  92. data/spec/integration/matcher_operator_data/type_code.yml +26 -0
  93. data/spec/integration/matcher_operator_data/type_code_with_scope.yml +26 -0
  94. data/spec/integration/matcher_operator_data/type_date.yml +39 -0
  95. data/spec/integration/matcher_operator_data/type_db_pointer.yml +19 -0
  96. data/spec/integration/matcher_operator_data/type_decimal.yml +40 -0
  97. data/spec/integration/matcher_operator_data/type_double.yml +15 -0
  98. data/spec/integration/matcher_operator_data/type_int32.yml +33 -0
  99. data/spec/integration/matcher_operator_data/type_int64.yml +33 -0
  100. data/spec/integration/matcher_operator_data/type_max_key.yml +17 -0
  101. data/spec/integration/matcher_operator_data/type_min_key.yml +17 -0
  102. data/spec/integration/matcher_operator_data/type_null.yml +23 -0
  103. data/spec/integration/matcher_operator_data/type_object.yml +23 -0
  104. data/spec/integration/matcher_operator_data/type_object_id.yml +25 -0
  105. data/spec/integration/matcher_operator_data/type_regex.yml +44 -0
  106. data/spec/integration/matcher_operator_data/type_string.yml +15 -0
  107. data/spec/integration/matcher_operator_data/type_symbol.yml +32 -0
  108. data/spec/integration/matcher_operator_data/type_timestamp.yml +25 -0
  109. data/spec/integration/matcher_operator_data/type_undefined.yml +17 -0
  110. data/spec/integration/stringified_symbol_field_spec.rb +2 -2
  111. data/spec/lite_spec_helper.rb +2 -0
  112. data/spec/mongoid/association/depending_spec.rb +391 -352
  113. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +4 -17
  114. data/spec/mongoid/association/nested/one_spec.rb +18 -14
  115. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +25 -25
  116. data/spec/mongoid/association/referenced/belongs_to_query_spec.rb +0 -20
  117. data/spec/mongoid/association/referenced/has_and_belongs_to_many/binding_spec.rb +1 -1
  118. data/spec/mongoid/association/referenced/has_many/binding_spec.rb +1 -1
  119. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +1 -1
  120. data/spec/mongoid/association/referenced/has_many_models.rb +0 -17
  121. data/spec/mongoid/association/referenced/has_one_models.rb +8 -0
  122. data/spec/mongoid/atomic/paths_spec.rb +64 -12
  123. data/spec/mongoid/attributes/projector_data/embedded.yml +105 -0
  124. data/spec/mongoid/attributes/projector_data/fields.yml +93 -0
  125. data/spec/mongoid/attributes/projector_spec.rb +41 -0
  126. data/spec/mongoid/attributes_spec.rb +98 -6
  127. data/spec/mongoid/clients/factory_spec.rb +51 -9
  128. data/spec/mongoid/clients/options_spec.rb +3 -11
  129. data/spec/mongoid/config/environment_spec.rb +8 -86
  130. data/spec/mongoid/config_spec.rb +32 -0
  131. data/spec/mongoid/contextual/atomic_spec.rb +25 -64
  132. data/spec/mongoid/contextual/geo_near_spec.rb +1 -1
  133. data/spec/mongoid/contextual/mongo_spec.rb +2 -2
  134. data/spec/mongoid/criteria/modifiable_spec.rb +1 -1
  135. data/spec/mongoid/criteria/queryable/expandable_spec.rb +0 -73
  136. data/spec/mongoid/criteria/queryable/extensions/boolean_spec.rb +1 -1
  137. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +105 -7
  138. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +229 -24
  139. data/spec/mongoid/criteria/queryable/selectable_shared_examples.rb +39 -0
  140. data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -565
  141. data/spec/mongoid/criteria/queryable/selectable_where_spec.rb +590 -0
  142. data/spec/mongoid/criteria_projection_spec.rb +411 -0
  143. data/spec/mongoid/criteria_spec.rb +0 -279
  144. data/spec/mongoid/document_query_spec.rb +0 -51
  145. data/spec/mongoid/document_spec.rb +14 -34
  146. data/spec/mongoid/errors/delete_restriction_spec.rb +1 -1
  147. data/spec/mongoid/errors/mongoid_error_spec.rb +8 -20
  148. data/spec/mongoid/extensions/false_class_spec.rb +1 -1
  149. data/spec/mongoid/extensions/string_spec.rb +5 -5
  150. data/spec/mongoid/extensions/true_class_spec.rb +1 -1
  151. data/spec/mongoid/fields/localized_spec.rb +4 -4
  152. data/spec/mongoid/fields_spec.rb +4 -4
  153. data/spec/mongoid/inspectable_spec.rb +12 -4
  154. data/spec/mongoid/persistable/deletable_spec.rb +175 -1
  155. data/spec/mongoid/persistable/destroyable_spec.rb +191 -3
  156. data/spec/mongoid/persistable/savable_spec.rb +3 -5
  157. data/spec/mongoid/persistable/updatable_spec.rb +0 -2
  158. data/spec/mongoid/persistable/upsertable_spec.rb +1 -1
  159. data/spec/mongoid/persistable_spec.rb +2 -2
  160. data/spec/mongoid/query_cache_middleware_spec.rb +8 -0
  161. data/spec/mongoid/query_cache_spec.rb +0 -24
  162. data/spec/mongoid/reloadable_spec.rb +18 -1
  163. data/spec/mongoid/shardable_spec.rb +44 -0
  164. data/spec/mongoid/touchable_spec.rb +104 -16
  165. data/spec/mongoid/touchable_spec_models.rb +52 -0
  166. data/spec/mongoid/validatable_spec.rb +1 -1
  167. data/spec/shared/lib/mrss/cluster_config.rb +3 -8
  168. data/spec/shared/lib/mrss/constraints.rb +10 -41
  169. data/spec/shared/lib/mrss/docker_runner.rb +1 -7
  170. data/spec/shared/lib/mrss/server_version_registry.rb +12 -17
  171. data/spec/shared/lib/mrss/spec_organizer.rb +1 -18
  172. data/spec/shared/share/Dockerfile.erb +33 -125
  173. data/spec/shared/shlib/server.sh +23 -100
  174. data/spec/shared/shlib/set_env.sh +1 -4
  175. data/spec/spec_helper.rb +7 -3
  176. data/spec/support/client_registry.rb +9 -0
  177. data/spec/support/models/address.rb +0 -4
  178. data/spec/support/models/bolt.rb +8 -0
  179. data/spec/support/models/hole.rb +13 -0
  180. data/spec/support/models/mop.rb +0 -1
  181. data/spec/support/models/nut.rb +8 -0
  182. data/spec/support/models/person.rb +6 -9
  183. data/spec/support/models/sealer.rb +8 -0
  184. data/spec/support/models/shirt.rb +12 -0
  185. data/spec/support/models/spacer.rb +8 -0
  186. data/spec/support/models/threadlocker.rb +8 -0
  187. data/spec/support/models/washer.rb +8 -0
  188. data.tar.gz.sig +0 -0
  189. metadata +609 -545
  190. metadata.gz.sig +0 -0
  191. data/lib/mongoid/errors/empty_config_file.rb +0 -26
  192. data/lib/mongoid/errors/invalid_config_file.rb +0 -26
  193. data/spec/integration/contextual/empty_spec.rb +0 -142
  194. data/spec/mongoid/errors/invalid_config_file_spec.rb +0 -32
  195. data/spec/shared/bin/s3-copy +0 -45
  196. data/spec/shared/bin/s3-upload +0 -69
  197. data/spec/shared/lib/mrss/event_subscriber.rb +0 -200
  198. data/spec/shared/share/haproxy-1.conf +0 -16
  199. data/spec/shared/share/haproxy-2.conf +0 -17
  200. data/spec/support/cluster_config.rb +0 -158
@@ -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.
@@ -443,7 +443,7 @@ module Mongoid
443
443
  # @return [ Criteria, Object ] A Criteria or return value from the target.
444
444
  #
445
445
  # @since 2.0.0.beta.1
446
- ruby2_keywords def method_missing(name, *args, &block)
446
+ def method_missing(name, *args, &block)
447
447
  if _target.respond_to?(name)
448
448
  _target.send(name, *args, &block)
449
449
  else
@@ -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?
@@ -122,8 +122,6 @@ module Mongoid
122
122
  # @since 7.0
123
123
  def inverses(other = nil)
124
124
  return [ inverse_of ] if inverse_of
125
- return [] if @options.key?(:inverse_of) && !inverse_of
126
-
127
125
  if polymorphic?
128
126
  polymorphic_inverses(other)
129
127
  else
@@ -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
@@ -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
@@ -238,11 +239,7 @@ module Mongoid
238
239
  #
239
240
  # @since 4.0.0
240
241
  def attribute_missing?(name)
241
- selection = __selected_fields
242
- return false unless selection
243
- field = fields[name]
244
- (selection.values.first == 0 && selection_excluded?(name, selection, field)) ||
245
- (selection.values.first == 1 && !selection_included?(name, selection, field))
242
+ !Projector.new(__selected_fields).attribute_or_path_allowed?(name)
246
243
  end
247
244
 
248
245
  # Return type-casted attributes.
@@ -259,14 +256,6 @@ module Mongoid
259
256
 
260
257
  private
261
258
 
262
- def selection_excluded?(name, selection, field)
263
- selection[name] == 0
264
- end
265
-
266
- def selection_included?(name, selection, field)
267
- selection.key?(name) || selection.keys.collect { |k| k.partition('.').first }.include?(name)
268
- end
269
-
270
259
  # Does the string contain dot syntax for accessing hashes?
271
260
  #
272
261
  # @api private
@@ -300,9 +289,11 @@ module Mongoid
300
289
 
301
290
  def read_raw_attribute(name)
302
291
  normalized = database_field_name(name.to_s)
292
+
303
293
  if attribute_missing?(normalized)
304
294
  raise ActiveModel::MissingAttributeError, "Missing attribute: '#{name}'"
305
295
  end
296
+
306
297
  if hash_dot_syntax?(normalized)
307
298
  attributes.__nested__(normalized)
308
299
  else
@@ -341,6 +332,26 @@ module Mongoid
341
332
  alias_method "#{name}_will_change!", "#{original}_will_change!"
342
333
  alias_method "#{name}_before_type_cast", "#{original}_before_type_cast"
343
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
344
355
  end
345
356
 
346
357
  private
@@ -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
@@ -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
@@ -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
  #
@@ -52,15 +52,7 @@ module Mongoid
52
52
  # @api private
53
53
  def load_yaml(path, environment = nil)
54
54
  env = environment ? environment.to_s : env_name
55
- contents = File.new(path).read
56
- if contents.empty?
57
- raise Mongoid::Errors::EmptyConfigFile.new(path)
58
- end
59
- data = YAML.load(ERB.new(contents).result)
60
- unless data.is_a?(Hash)
61
- raise Mongoid::Errors::InvalidConfigFile.new(path)
62
- end
63
- data[env]
55
+ YAML.load(ERB.new(File.new(path).read).result)[env]
64
56
  end
65
57
  end
66
58
  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)
@@ -173,18 +173,13 @@ module Mongoid
173
173
  # @example Unset the field on the matches.
174
174
  # context.unset(:name)
175
175
  #
176
- # @param [ String | Symbol | Array<String|Symbol> | Hash ] args
177
- # The name(s) of the field(s) to unset.
178
- # If a Hash is specified, its keys will be used irrespective of what
179
- # each key's value is, even if the value is nil or false.
176
+ # @param [ String, Symbol, Array ] args The name of the fields.
180
177
  #
181
178
  # @return [ nil ] Nil.
182
179
  #
183
180
  # @since 3.0.0
184
181
  def unset(*args)
185
- fields = args.map { |a| a.is_a?(Hash) ? a.keys : a }
186
- .__find_args__
187
- .map { |f| [database_field_name(f), true] }
182
+ fields = args.__find_args__.collect { |f| [database_field_name(f), true] }
188
183
  view.update_many("$unset" => Hash[fields])
189
184
  end
190
185
 
@@ -112,9 +112,6 @@ module Mongoid
112
112
  entries.length
113
113
  end
114
114
  alias :size :length
115
-
116
- alias :find_first :first
117
- alias :one :first
118
115
  end
119
116
  end
120
117
  end
@@ -21,7 +21,7 @@ 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
26
  _attribute_names = self.attribute_names
27
27
  attrs.reject! do |attr_name, value|
@@ -132,7 +132,7 @@ module Mongoid
132
132
  # @since 3.0.0
133
133
  def mongoize_ids(ids)
134
134
  ids.map do |id|
135
- id = id[:id] if id.respond_to?(:keys) && id[:id]
135
+ id = id[:_id] if id.respond_to?(:keys) && id[:_id]
136
136
  klass.fields["_id"].mongoize(id)
137
137
  end
138
138
  end
@@ -15,30 +15,6 @@ module Mongoid
15
15
 
16
16
  private
17
17
 
18
- # Expands the specified condition to MongoDB syntax.
19
- #
20
- # The condition must be a hash in one of the following forms:
21
- # - {field_name: value}
22
- # - {'field_name' => value}
23
- # - {key_instance: value}
24
- # - {'$operator' => operator_value_expression}
25
- #
26
- # This method expands the key instance form to the the operator form,
27
- # and also converts hash key to string.
28
- #
29
- # The hash may contain multiple items, each representing a separate
30
- # condition.
31
- #
32
- # @param [ Hash ] condition The condition to expand.
33
- #
34
- # @return [ Hash ] The expanded condition.
35
- def expand_condition(condition)
36
- mapped = condition.map do |field, value|
37
- expand_one_condition(field, value)
38
- end
39
- Hash[mapped]
40
- end
41
-
42
18
  # Expands the specified condition to MongoDB syntax.
43
19
  #
44
20
  # This method is meant to be called when processing the items of
@@ -33,4 +33,4 @@ module Mongoid
33
33
  end
34
34
  end
35
35
 
36
- ::Boolean.__send__(:extend, Mongoid::Criteria::Queryable::Extensions::Boolean::ClassMethods)
36
+ Mongoid::Boolean.__send__(:extend, Mongoid::Criteria::Queryable::Extensions::Boolean::ClassMethods)
@@ -1,10 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  # encoding: utf-8
3
3
 
4
- unless defined?(Boolean)
5
- class Boolean; end
6
- end
7
-
8
4
  if defined?(ActiveSupport)
9
5
  unless defined?(ActiveSupport::TimeWithZone)
10
6
  require "active_support/time_with_zone"
@@ -227,7 +227,16 @@ module Mongoid
227
227
  end
228
228
 
229
229
  # Takes a criteria hash and expands Key objects into hashes containing
230
- # MQL corresponding to said key objects.
230
+ # MQL corresponding to said key objects. Also converts the input to
231
+ # BSON::Document to permit indifferent access.
232
+ #
233
+ # The argument must be a hash containing key-value pairs of the
234
+ # following forms:
235
+ # - {field_name: value}
236
+ # - {'field_name' => value}
237
+ # - {key_instance: value}
238
+ # - {:$operator => operator_value_expression}
239
+ # - {'$operator' => operator_value_expression}
231
240
  #
232
241
  # Ruby does not permit multiple symbol operators. For example,
233
242
  # {:foo.gt => 1, :foo.gt => 2} is collapsed to {:foo.gt => 2} by the
@@ -237,19 +246,23 @@ module Mongoid
237
246
  # Similarly, this method should never need to expand a literal value
238
247
  # and an operator at the same time.
239
248
  #
249
+ # This method effectively converts symbol keys to string keys in
250
+ # the input +expr+, such that the downstream code can assume that
251
+ # conditions always contain string keys.
252
+ #
240
253
  # @param [ Hash ] expr Criteria including Key instances.
241
254
  #
242
- # @return [ Hash ] The expanded criteria.
255
+ # @return [ BSON::Document ] The expanded criteria.
243
256
  private def _mongoid_expand_keys(expr)
244
257
  unless expr.is_a?(Hash)
245
258
  raise ArgumentError, 'Argument must be a Hash'
246
259
  end
247
260
 
248
- result = {}
261
+ result = BSON::Document.new
249
262
  expr.each do |field, value|
250
- field.__expr_part__(value.__expand_complex__).each do |k, v|
251
- if result[k]
252
- if result[k].is_a?(Hash)
263
+ field.__expr_part__(value.__expand_complex__, negating?).each do |k, v|
264
+ if existing = result[k]
265
+ if existing.is_a?(Hash)
253
266
  # Existing value is an operator.
254
267
  # If new value is also an operator, ensure there are no
255
268
  # conflicts and add
@@ -257,8 +270,8 @@ module Mongoid
257
270
  # The new value is also an operator.
258
271
  # If there are no conflicts, combine the hashes, otherwise
259
272
  # add new conditions to top level with $and.
260
- if (v.keys & result[k].keys).empty?
261
- result[k].update(v)
273
+ if (v.keys & existing.keys).empty?
274
+ existing.update(v)
262
275
  else
263
276
  raise NotImplementedError, 'Ruby does not allow same symbol operator with different values'
264
277
  result['$and'] ||= []
@@ -266,26 +279,39 @@ module Mongoid
266
279
  end
267
280
  else
268
281
  # The new value is a simple value.
269
- # If there isn't an $eq operator already in the query,
270
- # transform the new value into an $eq operator and add it
271
- # to the existing hash. Otherwise add the new condition
272
- # with $and to the top level.
273
- if result[k].key?('$eq')
282
+ # Transform the implicit equality to either $eq or $regexp
283
+ # depending on the type of the argument. See
284
+ # https://docs.mongodb.com/manual/reference/operator/query/eq/#std-label-eq-usage-examples
285
+ # for the description of relevant server behavior.
286
+ op = case v
287
+ when Regexp, BSON::Regexp::Raw
288
+ '$regex'
289
+ else
290
+ '$eq'
291
+ end
292
+ # If there isn't an $eq/$regex operator already in the
293
+ # query, transform the new value into an operator
294
+ # expression and add it to the existing hash. Otherwise
295
+ # add the new condition with $and to the top level.
296
+ if existing.key?(op)
274
297
  raise NotImplementedError, 'Ruby does not allow same symbol operator with different values'
275
298
  result['$and'] ||= []
276
299
  result['$and'] << {k => v}
277
300
  else
278
- result[k].update('$eq' => v)
301
+ existing.update(op => v)
279
302
  end
280
303
  end
281
304
  else
282
305
  # Existing value is a simple value.
283
- # If we are adding an operator, and the operator is not $eq,
284
- # convert existing value into $eq and add the new operator
285
- # to the same hash. Otherwise add the new condition with $and
286
- # to the top level.
287
- if v.is_a?(Hash) && !v.key?('$eq')
288
- result[k] = {'$eq' => result[k]}.update(v)
306
+ # See the notes above about transformations to $eq/$regex.
307
+ op = case existing
308
+ when Regexp, BSON::Regexp::Raw
309
+ '$regex'
310
+ else
311
+ '$eq'
312
+ end
313
+ if v.is_a?(Hash) && !v.key?(op)
314
+ result[k] = {op => existing}.update(v)
289
315
  else
290
316
  raise NotImplementedError, 'Ruby does not allow same symbol operator with different values'
291
317
  result['$and'] ||= []