mongoid 7.0.0.beta → 7.0.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 (86) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/config/locales/en.yml +21 -0
  5. data/lib/mongoid.rb +1 -1
  6. data/lib/mongoid/association/embedded/batchable.rb +39 -13
  7. data/lib/mongoid/association/many.rb +4 -0
  8. data/lib/mongoid/association/referenced/has_many.rb +2 -0
  9. data/lib/mongoid/association/referenced/has_many/enumerable.rb +38 -7
  10. data/lib/mongoid/association/referenced/has_many/proxy.rb +3 -2
  11. data/lib/mongoid/association/referenced/syncable.rb +1 -1
  12. data/lib/mongoid/association/touchable.rb +1 -1
  13. data/lib/mongoid/atomic.rb +3 -3
  14. data/lib/mongoid/atomic/modifiers.rb +12 -8
  15. data/lib/mongoid/attributes.rb +20 -12
  16. data/lib/mongoid/attributes/dynamic.rb +2 -2
  17. data/lib/mongoid/changeable.rb +1 -1
  18. data/lib/mongoid/clients.rb +2 -0
  19. data/lib/mongoid/clients/sessions.rb +113 -0
  20. data/lib/mongoid/clients/storage_options.rb +1 -0
  21. data/lib/mongoid/composable.rb +1 -0
  22. data/lib/mongoid/contextual/aggregable/mongo.rb +1 -1
  23. data/lib/mongoid/contextual/atomic.rb +6 -3
  24. data/lib/mongoid/contextual/geo_near.rb +1 -1
  25. data/lib/mongoid/contextual/map_reduce.rb +6 -2
  26. data/lib/mongoid/contextual/memory.rb +25 -2
  27. data/lib/mongoid/contextual/mongo.rb +33 -14
  28. data/lib/mongoid/copyable.rb +2 -2
  29. data/lib/mongoid/criteria.rb +1 -0
  30. data/lib/mongoid/criteria/queryable/extensions/string.rb +1 -1
  31. data/lib/mongoid/criteria/queryable/mergeable.rb +3 -1
  32. data/lib/mongoid/errors.rb +1 -0
  33. data/lib/mongoid/errors/invalid_session_use.rb +24 -0
  34. data/lib/mongoid/extensions.rb +0 -4
  35. data/lib/mongoid/extensions/hash.rb +5 -2
  36. data/lib/mongoid/factory.rb +14 -5
  37. data/lib/mongoid/fields.rb +2 -2
  38. data/lib/mongoid/indexable.rb +4 -4
  39. data/lib/mongoid/matchable/and.rb +1 -1
  40. data/lib/mongoid/matchable/elem_match.rb +9 -3
  41. data/lib/mongoid/persistable.rb +2 -2
  42. data/lib/mongoid/persistable/creatable.rb +4 -2
  43. data/lib/mongoid/persistable/deletable.rb +4 -2
  44. data/lib/mongoid/persistable/destroyable.rb +1 -5
  45. data/lib/mongoid/persistable/updatable.rb +2 -2
  46. data/lib/mongoid/persistable/upsertable.rb +2 -1
  47. data/lib/mongoid/persistence_context.rb +5 -4
  48. data/lib/mongoid/query_cache.rb +5 -3
  49. data/lib/mongoid/reloadable.rb +1 -1
  50. data/lib/mongoid/serializable.rb +1 -1
  51. data/lib/mongoid/shardable.rb +1 -1
  52. data/lib/mongoid/tasks/database.rb +3 -2
  53. data/lib/mongoid/threaded.rb +38 -0
  54. data/lib/mongoid/traversable.rb +1 -1
  55. data/lib/mongoid/version.rb +1 -1
  56. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +4 -0
  57. data/spec/app/models/band.rb +1 -0
  58. data/spec/app/models/shipment_address.rb +1 -0
  59. data/spec/mongoid/association/macros_spec.rb +20 -0
  60. data/spec/mongoid/association/referenced/has_many/eager_spec.rb +15 -0
  61. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +229 -0
  62. data/spec/mongoid/atomic/modifiers_spec.rb +17 -17
  63. data/spec/mongoid/atomic_spec.rb +17 -17
  64. data/spec/mongoid/attributes_spec.rb +38 -2
  65. data/spec/mongoid/clients/sessions_spec.rb +325 -0
  66. data/spec/mongoid/contextual/memory_spec.rb +19 -0
  67. data/spec/mongoid/contextual/mongo_spec.rb +133 -0
  68. data/spec/mongoid/copyable_spec.rb +34 -0
  69. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +43 -0
  70. data/spec/mongoid/criteria/queryable/selectable_spec.rb +32 -3
  71. data/spec/mongoid/extensions/hash_spec.rb +18 -1
  72. data/spec/mongoid/factory_spec.rb +11 -0
  73. data/spec/mongoid/interceptable_spec.rb +3 -1
  74. data/spec/mongoid/matchable/elem_match_spec.rb +20 -0
  75. data/spec/mongoid/persistable/deletable_spec.rb +19 -0
  76. data/spec/mongoid/persistable/destroyable_spec.rb +19 -0
  77. data/spec/mongoid/persistable/savable_spec.rb +2 -2
  78. data/spec/mongoid/persistable/updatable_spec.rb +2 -2
  79. data/spec/mongoid/persistable_spec.rb +31 -16
  80. data/spec/mongoid/persistence_context_spec.rb +14 -0
  81. data/spec/mongoid/positional_spec.rb +10 -10
  82. data/spec/mongoid/query_cache_spec.rb +2 -16
  83. data/spec/mongoid/shardable_spec.rb +32 -12
  84. data/spec/spec_helper.rb +74 -0
  85. metadata +456 -446
  86. metadata.gz.sig +0 -0
@@ -29,7 +29,7 @@ module Mongoid
29
29
  #
30
30
  # @since 1.0.0
31
31
  def __evolve_time__
32
- ::Time.parse(self).utc
32
+ __mongoize_time__.utc
33
33
  end
34
34
 
35
35
  # Get the string as a mongo expression, adding $ to the front.
@@ -259,7 +259,9 @@ module Mongoid
259
259
  def prepare(field, operator, value)
260
260
  unless operator =~ /exists|type|size/
261
261
  value = value.__expand_complex__
262
- serializer = serializers[field]
262
+ field = field.to_s
263
+ name = aliases[field] || field
264
+ serializer = serializers[name]
263
265
  value = serializer ? serializer.evolve(value) : value
264
266
  end
265
267
  selection = { operator => value }
@@ -20,6 +20,7 @@ require "mongoid/errors/invalid_persistence_option"
20
20
  require "mongoid/errors/invalid_relation"
21
21
  require "mongoid/errors/invalid_relation_option"
22
22
  require "mongoid/errors/invalid_scope"
23
+ require "mongoid/errors/invalid_session_use"
23
24
  require "mongoid/errors/invalid_set_polymorphic_relation"
24
25
  require "mongoid/errors/invalid_storage_options"
25
26
  require "mongoid/errors/invalid_storage_parent"
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ module Errors
4
+
5
+ # This error is raised when a session is attempted to be used with a model whose client cannot use it, if
6
+ # sessions are nested, or if the mongodb deployment doesn't support sessions.
7
+ #
8
+ # @since 6.4.0
9
+ class InvalidSessionUse < MongoidError
10
+
11
+ # Create the error.
12
+ #
13
+ # @example Create the error.
14
+ # InvalidSessionUse.new(:invalid_session_use)
15
+ #
16
+ # @param [ :invalid_sesion_use, :invalid_session_nesting ] error_type The type of session misuse.
17
+ #
18
+ # @since 6.4.0
19
+ def initialize(error_type)
20
+ super(compose_message(error_type.to_s))
21
+ end
22
+ end
23
+ end
24
+ end
@@ -5,10 +5,6 @@ class BSON::ObjectId
5
5
  end
6
6
  end
7
7
 
8
- class Symbol
9
- remove_method :size if instance_methods.include? :size # temporal fix for ruby 1.9
10
- end
11
-
12
8
  class BSON::Document
13
9
  # We need to override this as ActiveSupport creates a new Object, instead of a new Hash
14
10
  # see https://github.com/rails/rails/commit/f1bad130d0c9bd77c94e43b696adca56c46a66aa
@@ -106,8 +106,11 @@ module Mongoid
106
106
  value = self
107
107
  keys.each do |key|
108
108
  return nil if value.nil?
109
- nested = value[key] || value[key.to_i]
110
- value = nested
109
+ value_for_key = value[key]
110
+ if value_for_key.nil? && key.to_i.to_s == key
111
+ value_for_key = value[key.to_i]
112
+ end
113
+ value = value_for_key
111
114
  end
112
115
  value
113
116
  end
@@ -17,7 +17,8 @@ module Mongoid
17
17
  #
18
18
  # @return [ Document ] The instantiated document.
19
19
  def build(klass, attributes = nil)
20
- type = (attributes || {})[TYPE]
20
+ attributes ||= {}
21
+ type = attributes[TYPE] || attributes[TYPE.to_sym]
21
22
  if type && klass._types.include?(type)
22
23
  type.constantize.new(attributes)
23
24
  else
@@ -37,14 +38,22 @@ module Mongoid
37
38
  # #only we give the document a list of the selected fields.
38
39
  #
39
40
  # @return [ Document ] The instantiated document.
40
- def from_db(klass, attributes = nil, selected_fields = nil)
41
+ def from_db(klass, attributes = nil, criteria = nil)
42
+ selected_fields = criteria.options[:fields] if criteria
41
43
  type = (attributes || {})[TYPE]
42
44
  if type.blank?
43
- klass.instantiate(attributes, selected_fields)
45
+ obj = klass.instantiate(attributes, selected_fields)
46
+ if criteria && criteria.association && criteria.parent_document
47
+ obj.set_relation(criteria.association.inverse, criteria.parent_document)
48
+ end
49
+ obj
44
50
  else
45
51
  camelized = type.camelize
46
- raise Errors::UnknownModel.new(camelized, type) unless Object.const_defined?(camelized)
47
- camelized.constantize.instantiate(attributes, selected_fields)
52
+ begin
53
+ camelized.constantize.instantiate(attributes, selected_fields)
54
+ rescue NameError
55
+ raise Errors::UnknownModel.new(camelized, type)
56
+ end
48
57
  end
49
58
  end
50
59
  end
@@ -411,7 +411,7 @@ module Mongoid
411
411
  def create_field_getter(name, meth, field)
412
412
  generated_methods.module_eval do
413
413
  re_define_method(meth) do
414
- raw = read_attribute(name)
414
+ raw = read_raw_attribute(name)
415
415
  if lazy_settable?(field, raw)
416
416
  write_attribute(name, field.eval_default(self))
417
417
  else
@@ -480,7 +480,7 @@ module Mongoid
480
480
  def create_field_check(name, meth)
481
481
  generated_methods.module_eval do
482
482
  re_define_method("#{meth}?") do
483
- value = read_attribute(name)
483
+ value = read_raw_attribute(name)
484
484
  lookup_attribute_presence(name, value)
485
485
  end
486
486
  end
@@ -35,10 +35,10 @@ module Mongoid
35
35
  key, options = spec.key, default_options.merge(spec.options)
36
36
  if database = options[:database]
37
37
  with(database: database) do |klass|
38
- klass.collection.indexes.create_one(key, options.except(:database))
38
+ klass.collection.indexes(session: session).create_one(key, options.except(:database))
39
39
  end
40
40
  else
41
- collection.indexes.create_one(key, options)
41
+ collection.indexes(session: session).create_one(key, options)
42
42
  end
43
43
  end and true
44
44
  end
@@ -56,9 +56,9 @@ module Mongoid
56
56
  indexed_database_names.each do |database|
57
57
  with(database: database) do |klass|
58
58
  begin
59
- klass.collection.indexes.each do |spec|
59
+ klass.collection.indexes(session: session).each do |spec|
60
60
  unless spec["name"] == "_id_"
61
- klass.collection.indexes.drop_one(spec["key"])
61
+ klass.collection.indexes(session: session).drop_one(spec["key"])
62
62
  logger.info(
63
63
  "MONGOID: Removed index '#{spec["name"]}' on collection " +
64
64
  "'#{klass.collection.name}' in database '#{database}'."
@@ -2,7 +2,7 @@
2
2
  module Mongoid
3
3
  module Matchable
4
4
 
5
- # Defines behavior for handling $or expressions in embedded documents.
5
+ # Defines behavior for handling $and expressions in embedded documents.
6
6
  class And < Default
7
7
 
8
8
  # Does the supplied query match the attribute?
@@ -13,13 +13,19 @@ module Mongoid
13
13
  #
14
14
  # @return [ true, false ] If the values match.
15
15
  def _matches?(value)
16
- if !@attribute.is_a?(Array) || !value.kind_of?(Hash) || !value["$elemMatch"].kind_of?(Hash)
16
+ elem_match = value["$elemMatch"] || value[:$elemMatch]
17
+
18
+ if !@attribute.is_a?(Array) || !value.kind_of?(Hash) || !elem_match.kind_of?(Hash)
17
19
  return false
18
20
  end
19
21
 
20
22
  return @attribute.any? do |sub_document|
21
- value["$elemMatch"].all? do |k, v|
22
- Matchable.matcher(sub_document, k, v)._matches?(v)
23
+ elem_match.all? do |k, v|
24
+ if v.try(:first).try(:[],0) == "$not".freeze || v.try(:first).try(:[],0) == :$not
25
+ !Matchable.matcher(sub_document, k, v.first[1])._matches?(v.first[1])
26
+ else
27
+ Matchable.matcher(sub_document, k, v)._matches?(v)
28
+ end
23
29
  end
24
30
  end
25
31
  end
@@ -201,9 +201,9 @@ module Mongoid
201
201
  #
202
202
  # @since 4.0.0
203
203
  def persist_atomic_operations(operations)
204
- if persisted? && operations
204
+ if persisted? && operations && !operations.empty?
205
205
  selector = atomic_selector
206
- _root.collection.find(selector).update_one(positionally(selector, operations))
206
+ _root.collection.find(selector).update_one(positionally(selector, operations), session: session)
207
207
  end
208
208
  end
209
209
  end
@@ -61,7 +61,9 @@ module Mongoid
61
61
  _parent.insert
62
62
  else
63
63
  selector = _parent.atomic_selector
64
- _root.collection.find(selector).update_one(positionally(selector, atomic_inserts))
64
+ _root.collection.find(selector).update_one(
65
+ positionally(selector, atomic_inserts),
66
+ session: session)
65
67
  end
66
68
  end
67
69
 
@@ -76,7 +78,7 @@ module Mongoid
76
78
  #
77
79
  # @since 4.0.0
78
80
  def insert_as_root
79
- collection.insert_one(as_attributes)
81
+ collection.insert_one(as_attributes, session: session)
80
82
  end
81
83
 
82
84
  # Post process an insert, which sets the new record attribute to false
@@ -64,7 +64,9 @@ module Mongoid
64
64
  _parent.remove_child(self) if notifying_parent?(options)
65
65
  if _parent.persisted?
66
66
  selector = _parent.atomic_selector
67
- _root.collection.find(selector).update_one(positionally(selector, atomic_deletes))
67
+ _root.collection.find(selector).update_one(
68
+ positionally(selector, atomic_deletes),
69
+ session: session)
68
70
  end
69
71
  true
70
72
  end
@@ -80,7 +82,7 @@ module Mongoid
80
82
  #
81
83
  # @since 4.0.0
82
84
  def delete_as_root
83
- collection.find(atomic_selector).delete_one
85
+ collection.find(atomic_selector).delete_one(session: session)
84
86
  true
85
87
  end
86
88
 
@@ -48,11 +48,7 @@ module Mongoid
48
48
  #
49
49
  # @since 1.0.0
50
50
  def destroy_all(conditions = nil)
51
- selector = conditions || {}
52
- documents = where(selector)
53
- destroyed = documents.count
54
- documents.each { |doc| doc.destroy }
55
- destroyed
51
+ where(conditions || {}).destroy
56
52
  end
57
53
  end
58
54
  end
@@ -134,9 +134,9 @@ module Mongoid
134
134
  unless updates.empty?
135
135
  coll = collection(_root)
136
136
  selector = atomic_selector
137
- coll.find(selector).update_one(positionally(selector, updates))
137
+ coll.find(selector).update_one(positionally(selector, updates), session: session)
138
138
  conflicts.each_pair do |key, value|
139
- coll.find(selector).update_one(positionally(selector, { key => value }))
139
+ coll.find(selector).update_one(positionally(selector, { key => value }), session: session)
140
140
  end
141
141
  end
142
142
  end
@@ -21,7 +21,8 @@ module Mongoid
21
21
  # @since 3.0.0
22
22
  def upsert(options = {})
23
23
  prepare_upsert(options) do
24
- collection.find(atomic_selector).update_one(as_attributes, upsert: true)
24
+ collection.find(atomic_selector).update_one(
25
+ as_attributes, upsert: true, session: session)
25
26
  end
26
27
  end
27
28
 
@@ -195,10 +195,10 @@ module Mongoid
195
195
  Thread.current["[mongoid][#{object.object_id}]:context"]
196
196
  end
197
197
 
198
- # Get the persistence context for a particular class or model instance.
198
+ # Clear the persistence context for a particular class or model instance.
199
199
  #
200
- # @example Get the persistence context for a class or model instance.
201
- # PersistenceContext.get(model)
200
+ # @example Clear the persistence context for a class or model instance.
201
+ # PersistenceContext.clear(model)
202
202
  #
203
203
  # @param [ Class, Object ] object The class or model instance.
204
204
  # @param [ Mongo::Cluster ] cluster The original cluster before this context was used.
@@ -208,8 +208,9 @@ module Mongoid
208
208
  if context = get(object)
209
209
  context.client.close unless (context.cluster.equal?(cluster) || cluster.nil?)
210
210
  end
211
+ ensure
211
212
  Thread.current["[mongoid][#{object.object_id}]:context"] = nil
212
213
  end
213
214
  end
214
215
  end
215
- end
216
+ end
@@ -222,9 +222,11 @@ module Mongoid
222
222
  super
223
223
  else
224
224
  unless cursor = cached_cursor
225
- server = read.select_server(cluster)
226
- cursor = CachedCursor.new(view, send_initial_query(server), server)
227
- QueryCache.cache_table[cache_key] = cursor
225
+ read_with_retry do
226
+ server = server_selector.select_server(cluster)
227
+ cursor = CachedCursor.new(view, send_initial_query(server), server)
228
+ QueryCache.cache_table[cache_key] = cursor
229
+ end
228
230
  end
229
231
  cursor.each do |doc|
230
232
  yield doc
@@ -58,7 +58,7 @@ module Mongoid
58
58
  #
59
59
  # @since 2.3.2
60
60
  def reload_root_document
61
- {}.merge(collection.find(_id: _id).read(mode: :primary).first || {})
61
+ {}.merge(collection.find({ _id: _id }, session: session).read(mode: :primary).first || {})
62
62
  end
63
63
 
64
64
  # Reload the embedded document.
@@ -101,7 +101,7 @@ module Mongoid
101
101
  value = send(name)
102
102
  attrs[name] = value ? value.serializable_hash(options) : nil
103
103
  elsif names.include?(name) && !fields.key?(name)
104
- attrs[name] = read_attribute(name)
104
+ attrs[name] = read_raw_attribute(name)
105
105
  elsif !attribute_missing?(name)
106
106
  attrs[name] = send(name)
107
107
  end
@@ -37,7 +37,7 @@ module Mongoid
37
37
  def shard_key_selector
38
38
  selector = {}
39
39
  shard_key_fields.each do |field|
40
- selector[field.to_s] = send(field)
40
+ selector[field.to_s] = new_record? ? send(field) : attribute_was(field)
41
41
  end
42
42
  selector
43
43
  end
@@ -44,7 +44,7 @@ module Mongoid
44
44
  models.each do |model|
45
45
  unless model.embedded?
46
46
  begin
47
- model.collection.indexes.each do |index|
47
+ model.collection.indexes(session: model.send(:session)).each do |index|
48
48
  # ignore default index
49
49
  unless index['name'] == '_id_'
50
50
  key = index['key'].symbolize_keys
@@ -77,7 +77,7 @@ module Mongoid
77
77
  indexes.each do |index|
78
78
  key = index['key'].symbolize_keys
79
79
  collection = model.collection
80
- collection.indexes.drop_one(key)
80
+ collection.indexes(session: model.send(:session)).drop_one(key)
81
81
  logger.info(
82
82
  "MONGOID: Removed index '#{index['name']}' on collection " +
83
83
  "'#{collection.name}' in database '#{collection.database.name}'."
@@ -107,6 +107,7 @@ module Mongoid
107
107
  end
108
108
 
109
109
  private
110
+
110
111
  def logger
111
112
  Mongoid.logger
112
113
  end
@@ -325,5 +325,43 @@ module Mongoid
325
325
  def validations_for(klass)
326
326
  validations[klass] ||= []
327
327
  end
328
+
329
+ # Cache a session for this thread.
330
+ #
331
+ # @example Save a session for this thread.
332
+ # Threaded.set_session(session)
333
+ #
334
+ # @param [ Mongo::Session ] session The session to save.
335
+ #
336
+ # @since 6.4.0
337
+ def set_session(session)
338
+ Thread.current[:session] = session
339
+ end
340
+
341
+ # Get the cached session for this thread.
342
+ #
343
+ # @example Get the session for this thread.
344
+ # Threaded.get_session
345
+ #
346
+ # @return [ Mongo::Session, nil ] The session cached on this thread or nil.
347
+ #
348
+ # @since 6.4.0
349
+ def get_session
350
+ Thread.current[:session]
351
+ end
352
+
353
+ # Clear the cached session for this thread.
354
+ #
355
+ # @example Clear this thread's session.
356
+ # Threaded.clear_session
357
+ #
358
+ # @return [ nil ]
359
+ #
360
+ # @since 6.4.0
361
+ def clear_session
362
+ session = get_session
363
+ session.end_session if session
364
+ Thread.current[:session] = nil
365
+ end
328
366
  end
329
367
  end
@@ -8,7 +8,7 @@ module Mongoid
8
8
  extend ActiveSupport::Concern
9
9
 
10
10
  def _parent
11
- @__parent
11
+ @__parent ||= nil
12
12
  end
13
13
 
14
14
  def _parent=(p)
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid
3
- VERSION = "7.0.0.beta"
3
+ VERSION = "7.0.0"
4
4
  end
@@ -11,6 +11,10 @@ development:
11
11
  hosts:
12
12
  - localhost:27017
13
13
  options:
14
+ # Note that all options listed below are Ruby driver client options (the mongo gem).
15
+ # Please refer to the driver documentation of the version of the mongo gem you are using
16
+ # for the most up-to-date list of options.
17
+ #
14
18
  # Change the default write concern. (default = { w: 1 })
15
19
  # write:
16
20
  # w: 1