mongoid 7.2.6 → 7.3.0

Sign up to get free protection for your applications and to get access to all the features.
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'] ||= []