mongoid 7.0.0.beta → 7.0.0

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