mongoid 4.0.0 → 4.0.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -1
- data/README.md +6 -2
- data/lib/config/locales/en.yml +2 -2
- data/lib/mongoid/atomic.rb +2 -2
- data/lib/mongoid/attributes.rb +2 -0
- data/lib/mongoid/contextual/aggregable/memory.rb +2 -2
- data/lib/mongoid/contextual/mongo.rb +3 -3
- data/lib/mongoid/criteria/#findable.rb# +141 -0
- data/lib/mongoid/document.rb +5 -5
- data/lib/mongoid/query_cache.rb +10 -2
- data/lib/mongoid/railtie.rb +2 -15
- data/lib/mongoid/railties/database.rake +1 -1
- data/lib/mongoid/relations/accessors.rb +2 -2
- data/lib/mongoid/relations/binding.rb +1 -1
- data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +1 -1
- data/lib/mongoid/relations/builders/embedded/one.rb +1 -1
- data/lib/mongoid/relations/builders/nested_attributes/one.rb +1 -1
- data/lib/mongoid/relations/counter_cache.rb +2 -2
- data/lib/mongoid/relations/one.rb +1 -1
- data/lib/mongoid/relations/referenced/many.rb +4 -4
- data/lib/mongoid/relations/referenced/many_to_many.rb +5 -5
- data/lib/mongoid/relations/synchronization.rb +4 -4
- data/lib/mongoid/relations/targets/enumerable.rb +10 -10
- data/lib/mongoid/reloadable.rb +3 -3
- data/lib/mongoid/threaded.rb +26 -15
- data/lib/mongoid/traversable.rb +6 -2
- data/lib/mongoid/validatable/uniqueness.rb +3 -3
- data/lib/mongoid/version.rb +1 -1
- data/lib/rails/generators/mongoid/config/templates/mongoid.yml +1 -1
- data/spec/app/models/id_key.rb +6 -0
- data/spec/mongoid/#atomic_spec.rb# +365 -0
- data/spec/mongoid/attributes_spec.rb +36 -0
- data/spec/mongoid/contextual/atomic_spec.rb +7 -13
- data/spec/mongoid/criteria_spec.rb +86 -75
- data/spec/mongoid/document_spec.rb +2 -2
- data/spec/mongoid/findable_spec.rb +2 -2
- data/spec/mongoid/positional_spec.rb +5 -10
- data/spec/mongoid/query_cache_spec.rb +32 -0
- data/spec/mongoid/relations/referenced/many_spec.rb +23 -0
- data/spec/mongoid/reloadable_spec.rb +23 -0
- data/spec/spec_helper.rb +2 -0
- metadata +7 -2
@@ -68,7 +68,7 @@ module Mongoid
|
|
68
68
|
# @since 2.0.0
|
69
69
|
def acceptable_id?
|
70
70
|
id = convert_id(existing.class, attributes[:id])
|
71
|
-
existing.
|
71
|
+
existing._id == id || id.nil? || (existing._id != id && update_only?)
|
72
72
|
end
|
73
73
|
|
74
74
|
# Can the existing relation be deleted?
|
@@ -108,7 +108,7 @@ module Mongoid
|
|
108
108
|
record[cache_column] = (record[cache_column] || 0) + 1
|
109
109
|
|
110
110
|
if record.persisted?
|
111
|
-
record.class.increment_counter(cache_column, record.
|
111
|
+
record.class.increment_counter(cache_column, record._id)
|
112
112
|
record.remove_change(cache_column)
|
113
113
|
end
|
114
114
|
end
|
@@ -119,7 +119,7 @@ module Mongoid
|
|
119
119
|
record[cache_column] = (record[cache_column] || 0) - 1
|
120
120
|
|
121
121
|
if record.persisted?
|
122
|
-
record.class.decrement_counter(cache_column, record.
|
122
|
+
record.class.decrement_counter(cache_column, record._id)
|
123
123
|
record.remove_change(cache_column)
|
124
124
|
end
|
125
125
|
end
|
@@ -281,10 +281,10 @@ module Mongoid
|
|
281
281
|
def substitute(replacement)
|
282
282
|
if replacement
|
283
283
|
new_docs, docs = replacement.compact, []
|
284
|
-
new_ids = new_docs.map { |doc| doc.
|
284
|
+
new_ids = new_docs.map { |doc| doc._id }
|
285
285
|
remove_not_in(new_ids)
|
286
286
|
new_docs.each do |doc|
|
287
|
-
docs.push(doc) if doc.send(foreign_key) != base.
|
287
|
+
docs.push(doc) if doc.send(foreign_key) != base._id
|
288
288
|
end
|
289
289
|
concat(docs)
|
290
290
|
else
|
@@ -304,7 +304,7 @@ module Mongoid
|
|
304
304
|
# @since 2.4.0
|
305
305
|
def unscoped
|
306
306
|
klass.unscoped.where(
|
307
|
-
foreign_key => Conversions.flag(base.
|
307
|
+
foreign_key => Conversions.flag(base._id, __metadata)
|
308
308
|
)
|
309
309
|
end
|
310
310
|
|
@@ -492,7 +492,7 @@ module Mongoid
|
|
492
492
|
removed.update_all(foreign_key => nil)
|
493
493
|
end
|
494
494
|
in_memory.each do |doc|
|
495
|
-
if !ids.include?(doc.
|
495
|
+
if !ids.include?(doc._id)
|
496
496
|
unbind_one(doc)
|
497
497
|
target.delete(doc)
|
498
498
|
if __metadata.destructive?
|
@@ -55,12 +55,12 @@ module Mongoid
|
|
55
55
|
next unless doc
|
56
56
|
append(doc)
|
57
57
|
if persistable? || _creating?
|
58
|
-
ids[doc.
|
58
|
+
ids[doc._id] = true
|
59
59
|
save_or_delay(doc, docs, inserts)
|
60
60
|
else
|
61
61
|
existing = base.send(foreign_key)
|
62
|
-
unless existing.include?(doc.
|
63
|
-
existing.push(doc.
|
62
|
+
unless existing.include?(doc._id)
|
63
|
+
existing.push(doc._id) and unsynced(base, foreign_key)
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
@@ -90,7 +90,7 @@ module Mongoid
|
|
90
90
|
# @since 2.0.0.beta.1
|
91
91
|
def build(attributes = {}, type = nil)
|
92
92
|
doc = Factory.build(type || klass, attributes)
|
93
|
-
base.send(foreign_key).push(doc.
|
93
|
+
base.send(foreign_key).push(doc._id)
|
94
94
|
append(doc)
|
95
95
|
doc.apply_post_processed_defaults
|
96
96
|
unsynced(doc, inverse_foreign_key)
|
@@ -134,7 +134,7 @@ module Mongoid
|
|
134
134
|
execute_callback :before_remove, doc
|
135
135
|
end
|
136
136
|
unless __metadata.forced_nil_inverse?
|
137
|
-
criteria.pull(inverse_foreign_key => base.
|
137
|
+
criteria.pull(inverse_foreign_key => base._id)
|
138
138
|
end
|
139
139
|
if persistable?
|
140
140
|
base.set(foreign_key => base.send(foreign_key).clear)
|
@@ -61,7 +61,7 @@ module Mongoid
|
|
61
61
|
def remove_inverse_keys(meta)
|
62
62
|
foreign_keys = send(meta.foreign_key)
|
63
63
|
unless foreign_keys.nil? || foreign_keys.empty?
|
64
|
-
meta.criteria(foreign_keys, self.class).pull(meta.inverse_foreign_key =>
|
64
|
+
meta.criteria(foreign_keys, self.class).pull(meta.inverse_foreign_key => _id)
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
@@ -87,15 +87,15 @@ module Mongoid
|
|
87
87
|
# had occurred.
|
88
88
|
if meta.autosave?
|
89
89
|
send(meta.name).in_memory.each do |doc|
|
90
|
-
adds.delete_one(doc.
|
90
|
+
adds.delete_one(doc._id)
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
94
94
|
unless adds.empty?
|
95
|
-
meta.criteria(adds, self.class).without_options.add_to_set(meta.inverse_foreign_key =>
|
95
|
+
meta.criteria(adds, self.class).without_options.add_to_set(meta.inverse_foreign_key => _id)
|
96
96
|
end
|
97
97
|
unless subs.empty?
|
98
|
-
meta.criteria(subs, self.class).without_options.pull(meta.inverse_foreign_key =>
|
98
|
+
meta.criteria(subs, self.class).without_options.pull(meta.inverse_foreign_key => _id)
|
99
99
|
end
|
100
100
|
end
|
101
101
|
end
|
@@ -59,7 +59,7 @@ module Mongoid
|
|
59
59
|
#
|
60
60
|
# @since 2.1.0
|
61
61
|
def <<(document)
|
62
|
-
_added[document.
|
62
|
+
_added[document._id] = document
|
63
63
|
self
|
64
64
|
end
|
65
65
|
alias :push :<<
|
@@ -110,9 +110,9 @@ module Mongoid
|
|
110
110
|
#
|
111
111
|
# @since 2.1.0
|
112
112
|
def delete(document)
|
113
|
-
doc = (_loaded.delete(document.
|
113
|
+
doc = (_loaded.delete(document._id) || _added.delete(document._id))
|
114
114
|
unless doc
|
115
|
-
if _unloaded && _unloaded.where(_id: document.
|
115
|
+
if _unloaded && _unloaded.where(_id: document._id).exists?
|
116
116
|
yield(document) if block_given?
|
117
117
|
return document
|
118
118
|
end
|
@@ -128,7 +128,7 @@ module Mongoid
|
|
128
128
|
#
|
129
129
|
# @example Delete all matching documents.
|
130
130
|
# enumerable.delete_if do |doc|
|
131
|
-
# dod.
|
131
|
+
# dod._id == _id
|
132
132
|
# end
|
133
133
|
#
|
134
134
|
# @return [ Array<Document> ] The remaining docs.
|
@@ -138,8 +138,8 @@ module Mongoid
|
|
138
138
|
load_all!
|
139
139
|
deleted = in_memory.select(&block)
|
140
140
|
deleted.each do |doc|
|
141
|
-
_loaded.delete(doc.
|
142
|
-
_added.delete(doc.
|
141
|
+
_loaded.delete(doc._id)
|
142
|
+
_added.delete(doc._id)
|
143
143
|
end
|
144
144
|
self
|
145
145
|
end
|
@@ -179,8 +179,8 @@ module Mongoid
|
|
179
179
|
end
|
180
180
|
else
|
181
181
|
unloaded_documents.each do |doc|
|
182
|
-
document = _added.delete(doc.
|
183
|
-
_loaded[document.
|
182
|
+
document = _added.delete(doc._id) || _loaded.delete(doc._id) || doc
|
183
|
+
_loaded[document._id] = document
|
184
184
|
yield(document)
|
185
185
|
end
|
186
186
|
end
|
@@ -237,7 +237,7 @@ module Mongoid
|
|
237
237
|
else
|
238
238
|
@_added, @executed = {}, true
|
239
239
|
@_loaded = target.inject({}) do |_target, doc|
|
240
|
-
_target[doc.
|
240
|
+
_target[doc._id] = doc
|
241
241
|
_target
|
242
242
|
end
|
243
243
|
end
|
@@ -255,7 +255,7 @@ module Mongoid
|
|
255
255
|
# @since 3.0.0
|
256
256
|
def include?(doc)
|
257
257
|
return super unless _unloaded
|
258
|
-
_unloaded.where(_id: doc.
|
258
|
+
_unloaded.where(_id: doc._id).exists? || _added.has_key?(doc._id)
|
259
259
|
end
|
260
260
|
|
261
261
|
# Inspection will just inspect the entries for nice array-style
|
data/lib/mongoid/reloadable.rb
CHANGED
@@ -21,7 +21,7 @@ module Mongoid
|
|
21
21
|
def reload
|
22
22
|
reloaded = _reload
|
23
23
|
if Mongoid.raise_not_found_error && reloaded.empty?
|
24
|
-
raise Errors::DocumentNotFound.new(self.class,
|
24
|
+
raise Errors::DocumentNotFound.new(self.class, _id, _id)
|
25
25
|
end
|
26
26
|
@attributes = reloaded
|
27
27
|
@attributes_before_type_cast = {}
|
@@ -57,7 +57,7 @@ module Mongoid
|
|
57
57
|
#
|
58
58
|
# @since 2.3.2
|
59
59
|
def reload_root_document
|
60
|
-
{}.merge(with(read: :primary).collection.find(_id:
|
60
|
+
{}.merge(with(read: :primary).collection.find(_id: _id).one || {})
|
61
61
|
end
|
62
62
|
|
63
63
|
# Reload the embedded document.
|
@@ -70,7 +70,7 @@ module Mongoid
|
|
70
70
|
# @since 2.3.2
|
71
71
|
def reload_embedded_document
|
72
72
|
extract_embedded_attributes({}.merge(
|
73
|
-
_root.with(read: :primary).collection.find(_id: _root.
|
73
|
+
_root.with(read: :primary).collection.find(_id: _root._id).one
|
74
74
|
))
|
75
75
|
end
|
76
76
|
|
data/lib/mongoid/threaded.rb
CHANGED
@@ -6,6 +6,17 @@ module Mongoid
|
|
6
6
|
# This module contains logic for easy access to objects that have a lifecycle
|
7
7
|
# on the current thread.
|
8
8
|
module Threaded
|
9
|
+
DATABASE_OVERRIDE_KEY = "[mongoid]:db-override"
|
10
|
+
SESSIONS_KEY = "[mongoid]:sessions"
|
11
|
+
SESSION_OVERRIDE_KEY = "[mongoid]:session-override"
|
12
|
+
SCOPE_STACK_KEY = "[mongoid]:scope-stack"
|
13
|
+
AUTOSAVES_KEY = "[mongoid]:autosaves"
|
14
|
+
VALIDATIONS_KEY = "[mongoid]:validations"
|
15
|
+
|
16
|
+
STACK_KEYS = Hash.new do |hash, key|
|
17
|
+
hash[key] = "[mongoid]:#{key}-stack"
|
18
|
+
end
|
19
|
+
|
9
20
|
extend self
|
10
21
|
|
11
22
|
# Begin entry into a named thread local stack.
|
@@ -31,7 +42,7 @@ module Mongoid
|
|
31
42
|
#
|
32
43
|
# @since 3.0.0
|
33
44
|
def database_override
|
34
|
-
Thread.current[
|
45
|
+
Thread.current[DATABASE_OVERRIDE_KEY]
|
35
46
|
end
|
36
47
|
|
37
48
|
# Set the global database override.
|
@@ -45,7 +56,7 @@ module Mongoid
|
|
45
56
|
#
|
46
57
|
# @since 3.0.0
|
47
58
|
def database_override=(name)
|
48
|
-
Thread.current[
|
59
|
+
Thread.current[DATABASE_OVERRIDE_KEY] = name
|
49
60
|
end
|
50
61
|
|
51
62
|
# Get the database sessions from the current thread.
|
@@ -57,7 +68,7 @@ module Mongoid
|
|
57
68
|
#
|
58
69
|
# @since 3.0.0
|
59
70
|
def sessions
|
60
|
-
Thread.current[
|
71
|
+
Thread.current[SESSIONS_KEY] ||= {}
|
61
72
|
end
|
62
73
|
|
63
74
|
# Are in the middle of executing the named stack
|
@@ -99,7 +110,7 @@ module Mongoid
|
|
99
110
|
#
|
100
111
|
# @since 2.4.0
|
101
112
|
def stack(name)
|
102
|
-
Thread.current[
|
113
|
+
Thread.current[STACK_KEYS[name]] ||= []
|
103
114
|
end
|
104
115
|
|
105
116
|
# Begin autosaving a document on the current thread.
|
@@ -111,7 +122,7 @@ module Mongoid
|
|
111
122
|
#
|
112
123
|
# @since 3.0.0
|
113
124
|
def begin_autosave(document)
|
114
|
-
autosaves_for(document.class).push(document.
|
125
|
+
autosaves_for(document.class).push(document._id)
|
115
126
|
end
|
116
127
|
|
117
128
|
# Begin validating a document on the current thread.
|
@@ -123,7 +134,7 @@ module Mongoid
|
|
123
134
|
#
|
124
135
|
# @since 2.1.9
|
125
136
|
def begin_validate(document)
|
126
|
-
validations_for(document.class).push(document.
|
137
|
+
validations_for(document.class).push(document._id)
|
127
138
|
end
|
128
139
|
|
129
140
|
# Exit autosaving a document on the current thread.
|
@@ -135,7 +146,7 @@ module Mongoid
|
|
135
146
|
#
|
136
147
|
# @since 3.0.0
|
137
148
|
def exit_autosave(document)
|
138
|
-
autosaves_for(document.class).delete_one(document.
|
149
|
+
autosaves_for(document.class).delete_one(document._id)
|
139
150
|
end
|
140
151
|
|
141
152
|
# Exit validating a document on the current thread.
|
@@ -147,7 +158,7 @@ module Mongoid
|
|
147
158
|
#
|
148
159
|
# @since 2.1.9
|
149
160
|
def exit_validate(document)
|
150
|
-
validations_for(document.class).delete_one(document.
|
161
|
+
validations_for(document.class).delete_one(document._id)
|
151
162
|
end
|
152
163
|
|
153
164
|
# Get the global session override.
|
@@ -159,7 +170,7 @@ module Mongoid
|
|
159
170
|
#
|
160
171
|
# @since 3.0.0
|
161
172
|
def session_override
|
162
|
-
Thread.current[
|
173
|
+
Thread.current[SESSION_OVERRIDE_KEY]
|
163
174
|
end
|
164
175
|
|
165
176
|
# Set the global session override.
|
@@ -173,7 +184,7 @@ module Mongoid
|
|
173
184
|
#
|
174
185
|
# @since 3.0.0
|
175
186
|
def session_override=(name)
|
176
|
-
Thread.current[
|
187
|
+
Thread.current[SESSION_OVERRIDE_KEY] = name
|
177
188
|
end
|
178
189
|
|
179
190
|
# Get the mongoid scope stack for chained criteria.
|
@@ -185,7 +196,7 @@ module Mongoid
|
|
185
196
|
#
|
186
197
|
# @since 2.1.0
|
187
198
|
def scope_stack
|
188
|
-
Thread.current[
|
199
|
+
Thread.current[SCOPE_STACK_KEY] ||= {}
|
189
200
|
end
|
190
201
|
|
191
202
|
# Is the document autosaved on the current thread?
|
@@ -199,7 +210,7 @@ module Mongoid
|
|
199
210
|
#
|
200
211
|
# @since 2.1.9
|
201
212
|
def autosaved?(document)
|
202
|
-
autosaves_for(document.class).include?(document.
|
213
|
+
autosaves_for(document.class).include?(document._id)
|
203
214
|
end
|
204
215
|
|
205
216
|
# Is the document validated on the current thread?
|
@@ -213,7 +224,7 @@ module Mongoid
|
|
213
224
|
#
|
214
225
|
# @since 2.1.9
|
215
226
|
def validated?(document)
|
216
|
-
validations_for(document.class).include?(document.
|
227
|
+
validations_for(document.class).include?(document._id)
|
217
228
|
end
|
218
229
|
|
219
230
|
# Get all autosaves on the current thread.
|
@@ -225,7 +236,7 @@ module Mongoid
|
|
225
236
|
#
|
226
237
|
# @since 3.0.0
|
227
238
|
def autosaves
|
228
|
-
Thread.current[
|
239
|
+
Thread.current[AUTOSAVES_KEY] ||= {}
|
229
240
|
end
|
230
241
|
|
231
242
|
# Get all validations on the current thread.
|
@@ -237,7 +248,7 @@ module Mongoid
|
|
237
248
|
#
|
238
249
|
# @since 2.1.9
|
239
250
|
def validations
|
240
|
-
Thread.current[
|
251
|
+
Thread.current[VALIDATIONS_KEY] ||= {}
|
241
252
|
end
|
242
253
|
|
243
254
|
# Get all autosaves on the current thread for the class.
|
data/lib/mongoid/traversable.rb
CHANGED
@@ -7,8 +7,12 @@ module Mongoid
|
|
7
7
|
module Traversable
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
def _parent
|
11
|
+
@__parent
|
12
|
+
end
|
13
|
+
|
14
|
+
def _parent=(p)
|
15
|
+
@__parent = p
|
12
16
|
end
|
13
17
|
|
14
18
|
# Get all child +Documents+ to this +Document+, going n levels deep if
|
@@ -130,7 +130,7 @@ module Mongoid
|
|
130
130
|
end
|
131
131
|
|
132
132
|
if document.persisted? && !document.embedded?
|
133
|
-
selector.merge!(_id: { "$ne" => document.
|
133
|
+
selector.merge!(_id: { "$ne" => document._id })
|
134
134
|
end
|
135
135
|
selector
|
136
136
|
end
|
@@ -164,7 +164,7 @@ module Mongoid
|
|
164
164
|
# @return [ Criteria ] The scoped criteria.
|
165
165
|
#
|
166
166
|
# @since 2.3.0
|
167
|
-
def scope(criteria, document,
|
167
|
+
def scope(criteria, document, _attribute)
|
168
168
|
Array.wrap(options[:scope]).each do |item|
|
169
169
|
name = document.database_field_name(item)
|
170
170
|
criteria = criteria.where(item => document.attributes[name])
|
@@ -227,7 +227,7 @@ module Mongoid
|
|
227
227
|
def to_validate(document, attribute, value)
|
228
228
|
metadata = document.relations[attribute.to_s]
|
229
229
|
if metadata && metadata.stores_foreign_key?
|
230
|
-
[ metadata.foreign_key, value && value.
|
230
|
+
[ metadata.foreign_key, value && value._id ]
|
231
231
|
else
|
232
232
|
[ attribute, value ]
|
233
233
|
end
|
data/lib/mongoid/version.rb
CHANGED
@@ -32,7 +32,7 @@ development:
|
|
32
32
|
# Includes the root model name in json serialization. (default: false)
|
33
33
|
# include_root_in_json: false
|
34
34
|
|
35
|
-
# Include the _type field in
|
35
|
+
# Include the _type field in serialization. (default: false)
|
36
36
|
# include_type_for_serialization: false
|
37
37
|
|
38
38
|
# Preload all models in development, needed when models use
|
@@ -0,0 +1,365 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Mongoid::Atomic do
|
4
|
+
|
5
|
+
describe "#add_atomic_pull" do
|
6
|
+
|
7
|
+
let!(:person) do
|
8
|
+
Person.create
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:address) do
|
12
|
+
person.addresses.create
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:location) do
|
16
|
+
address.locations.create
|
17
|
+
end
|
18
|
+
|
19
|
+
before do
|
20
|
+
person.add_atomic_pull(address)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "adds the document to the delayed atomic pulls" do
|
24
|
+
expect(person.delayed_atomic_pulls["addresses"]).to eq([ address ])
|
25
|
+
end
|
26
|
+
|
27
|
+
it "flags the document for destruction" do
|
28
|
+
expect(address).to be_flagged_for_destroy
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#add_atomic_unset" do
|
33
|
+
|
34
|
+
let!(:person) do
|
35
|
+
Person.new
|
36
|
+
end
|
37
|
+
|
38
|
+
let(:name) do
|
39
|
+
person.build_name
|
40
|
+
end
|
41
|
+
|
42
|
+
before do
|
43
|
+
person.add_atomic_unset(name)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "adds the document to the delayed atomic unsets" do
|
47
|
+
expect(person.delayed_atomic_unsets["name"]).to eq([ name ])
|
48
|
+
end
|
49
|
+
|
50
|
+
it "flags the document for destruction" do
|
51
|
+
expect(name).to be_flagged_for_destroy
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#atomic_updates" do
|
56
|
+
|
57
|
+
context "when the document is persisted" do
|
58
|
+
|
59
|
+
let(:person) do
|
60
|
+
Person.create
|
61
|
+
end
|
62
|
+
|
63
|
+
context "when the document is modified" do
|
64
|
+
|
65
|
+
before do
|
66
|
+
person.title = "Sir"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "returns the atomic updates" do
|
70
|
+
expect(person.atomic_updates).to eq({ "$set" => { "title" => "Sir" }})
|
71
|
+
end
|
72
|
+
|
73
|
+
context "when an embeds many child is added" do
|
74
|
+
|
75
|
+
let!(:address) do
|
76
|
+
person.addresses.build(street: "Oxford St")
|
77
|
+
end
|
78
|
+
|
79
|
+
it "returns a $set and $pushAll for modifications" do
|
80
|
+
expect(person.atomic_updates).to eq(
|
81
|
+
{
|
82
|
+
"$set" => { "title" => "Sir" },
|
83
|
+
"$pushAll" => { "addresses" => [
|
84
|
+
{ "_id" => "oxford-st", "street" => "Oxford St" }
|
85
|
+
]}
|
86
|
+
}
|
87
|
+
)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "when an embeds one child is added" do
|
92
|
+
|
93
|
+
let!(:name) do
|
94
|
+
person.build_name(first_name: "Lionel")
|
95
|
+
end
|
96
|
+
|
97
|
+
it "returns a $set for modifications" do
|
98
|
+
expect(person.atomic_updates).to eq(
|
99
|
+
{
|
100
|
+
"$set" => {
|
101
|
+
"title" => "Sir",
|
102
|
+
"name" => { "_id" => "Lionel-", "first_name" => "Lionel" }
|
103
|
+
}
|
104
|
+
}
|
105
|
+
)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "when an existing embeds many gets modified" do
|
110
|
+
|
111
|
+
let!(:address) do
|
112
|
+
person.addresses.create(street: "Oxford St")
|
113
|
+
end
|
114
|
+
|
115
|
+
before do
|
116
|
+
address.street = "Bond St"
|
117
|
+
end
|
118
|
+
|
119
|
+
context "when asking for the updates from the root" do
|
120
|
+
|
121
|
+
it "returns the $set with correct position and modifications" do
|
122
|
+
expect(person.atomic_updates).to eq(
|
123
|
+
{ "$set" => { "title" => "Sir", "addresses.0.street" => "Bond St" }}
|
124
|
+
)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context "when asking for the updates from the child" do
|
129
|
+
|
130
|
+
it "returns the $set with correct position and modifications" do
|
131
|
+
expect(address.atomic_updates).to eq(
|
132
|
+
{ "$set" => { "addresses.0.street" => "Bond St" }}
|
133
|
+
)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context "when an existing 2nd level embedded child gets modified" do
|
138
|
+
|
139
|
+
let!(:location) do
|
140
|
+
address.locations.create(name: "Home")
|
141
|
+
end
|
142
|
+
|
143
|
+
before do
|
144
|
+
location.name = "Work"
|
145
|
+
end
|
146
|
+
|
147
|
+
context "when asking for the updates from the root" do
|
148
|
+
|
149
|
+
it "returns the $set with correct positions and modifications" do
|
150
|
+
expect(person.atomic_updates).to eq(
|
151
|
+
{ "$set" => {
|
152
|
+
"title" => "Sir",
|
153
|
+
"addresses.0.street" => "Bond St",
|
154
|
+
"addresses.0.locations.0.name" => "Work" }
|
155
|
+
}
|
156
|
+
)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context "when asking for the updates from the 1st level child" do
|
161
|
+
|
162
|
+
it "returns the $set with correct positions and modifications" do
|
163
|
+
expect(address.atomic_updates).to eq(
|
164
|
+
{ "$set" => {
|
165
|
+
"addresses.0.street" => "Bond St",
|
166
|
+
"addresses.0.locations.0.name" => "Work" }
|
167
|
+
}
|
168
|
+
)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context "when asking for the updates from the 2nd level child" do
|
173
|
+
|
174
|
+
it "returns the $set with correct positions and modifications" do
|
175
|
+
expect(location.atomic_updates).to eq(
|
176
|
+
{ "$set" => {
|
177
|
+
"addresses.0.locations.0.name" => "Work" }
|
178
|
+
}
|
179
|
+
)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
context "when a 2nd level embedded child gets added" do
|
185
|
+
|
186
|
+
let!(:location) do
|
187
|
+
address.locations.build(name: "Home")
|
188
|
+
end
|
189
|
+
|
190
|
+
context "when asking for the updates from the root" do
|
191
|
+
|
192
|
+
it "returns the $set with correct positions and modifications" do
|
193
|
+
expect(person.atomic_updates).to eq(
|
194
|
+
{
|
195
|
+
"$set" => {
|
196
|
+
"title" => "Sir",
|
197
|
+
"addresses.0.street" => "Bond St"
|
198
|
+
},
|
199
|
+
conflicts: {
|
200
|
+
"$pushAll" => {
|
201
|
+
"addresses.0.locations" => [{ "_id" => location.id, "name" => "Home" }]
|
202
|
+
}
|
203
|
+
}
|
204
|
+
}
|
205
|
+
)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context "when asking for the updates from the 1st level child" do
|
210
|
+
|
211
|
+
it "returns the $set with correct positions and modifications" do
|
212
|
+
expect(address.atomic_updates).to eq(
|
213
|
+
{
|
214
|
+
"$set" => {
|
215
|
+
"addresses.0.street" => "Bond St"
|
216
|
+
},
|
217
|
+
conflicts: {
|
218
|
+
"$pushAll" => {
|
219
|
+
"addresses.0.locations" => [{ "_id" => location.id, "name" => "Home" }]
|
220
|
+
}
|
221
|
+
}
|
222
|
+
}
|
223
|
+
)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
context "when an embedded child gets unset" do
|
229
|
+
|
230
|
+
before do
|
231
|
+
person.attributes = { addresses: nil }
|
232
|
+
end
|
233
|
+
|
234
|
+
let(:updates) do
|
235
|
+
person.atomic_updates
|
236
|
+
end
|
237
|
+
|
238
|
+
it "returns the $set for the first level and $unset for other." do
|
239
|
+
expect(updates).to eq({
|
240
|
+
"$unset" => { "addresses" => true },
|
241
|
+
"$set" => { "title" => "Sir" }
|
242
|
+
})
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
context "when adding a new second level child" do
|
247
|
+
|
248
|
+
let!(:new_address) do
|
249
|
+
person.addresses.build(street: "Another")
|
250
|
+
end
|
251
|
+
|
252
|
+
let!(:location) do
|
253
|
+
new_address.locations.build(name: "Home")
|
254
|
+
end
|
255
|
+
|
256
|
+
context "when asking for the updates from the root document" do
|
257
|
+
|
258
|
+
it "returns the $set for 1st level and other for the 2nd level" do
|
259
|
+
expect(person.atomic_updates).to eq(
|
260
|
+
{
|
261
|
+
"$set" => {
|
262
|
+
"title" => "Sir",
|
263
|
+
"addresses.0.street" => "Bond St"
|
264
|
+
},
|
265
|
+
conflicts: {
|
266
|
+
"$pushAll" => {
|
267
|
+
"addresses" => [{
|
268
|
+
"_id" => new_address.id,
|
269
|
+
"street" => "Another",
|
270
|
+
"locations" => [
|
271
|
+
"_id" => location.id,
|
272
|
+
"name" => "Home"
|
273
|
+
]
|
274
|
+
}]
|
275
|
+
}
|
276
|
+
}
|
277
|
+
}
|
278
|
+
)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
context "when asking for the updates from the 1st level document" do
|
283
|
+
|
284
|
+
it "returns the $set for 1st level and other for the 2nd level" do
|
285
|
+
expect(address.atomic_updates).to eq(
|
286
|
+
{ "$set" => { "addresses.0.street" => "Bond St" }}
|
287
|
+
)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
context "when adding a new child beetween two existing and updating one of them" do
|
293
|
+
|
294
|
+
let!(:new_address) do
|
295
|
+
person.addresses.build(street: "Ipanema")
|
296
|
+
end
|
297
|
+
|
298
|
+
let!(:location) do
|
299
|
+
new_address.locations.build(name: "Home")
|
300
|
+
end
|
301
|
+
|
302
|
+
before do
|
303
|
+
person.addresses[0] = new_address
|
304
|
+
person.addresses[1] = address
|
305
|
+
end
|
306
|
+
|
307
|
+
it "returns the $set for 1st and 2nd level and other for the 3nd level" do
|
308
|
+
expect(person.atomic_updates).to eq(
|
309
|
+
{
|
310
|
+
"$set" => {
|
311
|
+
"title" => "Sir"
|
312
|
+
},
|
313
|
+
"$pushAll" => {
|
314
|
+
"addresses" => [{
|
315
|
+
"_id" => new_address.id,
|
316
|
+
"street" => "Ipanema",
|
317
|
+
"locations" => [
|
318
|
+
"_id" => location.id,
|
319
|
+
"name" => "Home"
|
320
|
+
]
|
321
|
+
}]
|
322
|
+
},
|
323
|
+
conflicts: {
|
324
|
+
"$set" => { "addresses.0.street"=>"Bond St" }
|
325
|
+
}
|
326
|
+
}
|
327
|
+
)
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
context "when adding new embedded docs at multiple levels" do
|
333
|
+
|
334
|
+
let!(:address) do
|
335
|
+
person.addresses.build(street: "Another")
|
336
|
+
end
|
337
|
+
|
338
|
+
let!(:location) do
|
339
|
+
address.locations.build(name: "Home")
|
340
|
+
end
|
341
|
+
|
342
|
+
it "returns the proper $sets and $pushAlls for all levels" do
|
343
|
+
expect(person.atomic_updates).to eq(
|
344
|
+
{
|
345
|
+
"$set" => {
|
346
|
+
"title" => "Sir",
|
347
|
+
},
|
348
|
+
"$pushAll" => {
|
349
|
+
"addresses" => [{
|
350
|
+
"_id" => address.id,
|
351
|
+
"street" => "Another",
|
352
|
+
"locations" => [
|
353
|
+
"_id" => location.id,
|
354
|
+
"name" => "Home"
|
355
|
+
]
|
356
|
+
}]
|
357
|
+
}
|
358
|
+
}
|
359
|
+
)
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|