mongoid 7.0.1 → 7.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongoid/association.rb +0 -1
  5. data/lib/mongoid/association/depending.rb +22 -1
  6. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +3 -3
  7. data/lib/mongoid/association/referenced/has_many/proxy.rb +1 -1
  8. data/lib/mongoid/association/relatable.rb +16 -2
  9. data/lib/mongoid/attributes/nested.rb +14 -3
  10. data/lib/mongoid/contextual/map_reduce.rb +1 -1
  11. data/lib/mongoid/copyable.rb +3 -2
  12. data/lib/mongoid/document.rb +2 -0
  13. data/lib/mongoid/matchable.rb +3 -0
  14. data/lib/mongoid/matchable/nor.rb +37 -0
  15. data/lib/mongoid/persistable/settable.rb +58 -13
  16. data/lib/mongoid/persistence_context.rb +5 -1
  17. data/lib/mongoid/touchable.rb +102 -0
  18. data/lib/mongoid/version.rb +1 -1
  19. data/spec/app/models/array_field.rb +7 -0
  20. data/spec/app/models/updatable.rb +7 -0
  21. data/spec/mongoid/association/accessors_spec.rb +39 -0
  22. data/spec/mongoid/association/depending_spec.rb +253 -0
  23. data/spec/mongoid/association/polymorphic_spec.rb +59 -0
  24. data/spec/mongoid/association/referenced/belongs_to_spec.rb +3 -3
  25. data/spec/mongoid/association/referenced/has_one_spec.rb +59 -0
  26. data/spec/mongoid/attributes/nested_spec.rb +18 -2
  27. data/spec/mongoid/clients/factory_spec.rb +3 -3
  28. data/spec/mongoid/clients/options_spec.rb +28 -13
  29. data/spec/mongoid/clients/sessions_spec.rb +3 -3
  30. data/spec/mongoid/clients/transactions_spec.rb +369 -0
  31. data/spec/mongoid/copyable_spec.rb +16 -0
  32. data/spec/mongoid/matchable/nor_spec.rb +209 -0
  33. data/spec/mongoid/matchable_spec.rb +26 -1
  34. data/spec/mongoid/persistable/settable_spec.rb +128 -9
  35. data/spec/mongoid/{association/touchable_spec.rb → touchable_spec.rb} +28 -7
  36. data/spec/spec_helper.rb +8 -0
  37. metadata +457 -448
  38. metadata.gz.sig +0 -0
  39. data/lib/mongoid/association/touchable.rb +0 -97
@@ -12,10 +12,12 @@ describe Mongoid::Attributes::Nested do
12
12
 
13
13
  before do
14
14
  Person.accepts_nested_attributes_for :favorites
15
+ Person.accepts_nested_attributes_for :children
15
16
  end
16
17
 
17
18
  after do
18
19
  Person.send(:undef_method, :favorites_attributes=)
20
+ Person.send(:undef_method, :children_attributes=)
19
21
  Person.nested_attributes.clear
20
22
  end
21
23
 
@@ -23,9 +25,18 @@ describe Mongoid::Attributes::Nested do
23
25
  expect(person).to respond_to(:favorites_attributes=)
24
26
  end
25
27
 
28
+ it "does not autosave if the association is embedded" do
29
+ expect(person).not_to respond_to(:autosave_documents_for_favorites)
30
+ end
31
+
32
+ it "autosaves if the association is not embedded" do
33
+ expect(person).to respond_to(:autosave_documents_for_children)
34
+ end
35
+
26
36
  it "adds the method name to the nested attributes list" do
27
37
  expect(Person.nested_attributes).to eq({
28
- "favorites_attributes" => "favorites_attributes="
38
+ "favorites_attributes" => "favorites_attributes=",
39
+ "children_attributes" => "children_attributes="
29
40
  })
30
41
  end
31
42
  end
@@ -202,7 +213,12 @@ describe Mongoid::Attributes::Nested do
202
213
  context "when the relation is a referenced in" do
203
214
 
204
215
  before do
205
- Post.accepts_nested_attributes_for :person
216
+ Post.accepts_nested_attributes_for :person, autosave: false
217
+ end
218
+
219
+ after do
220
+ Post.send(:undef_method, :person_attributes=)
221
+ Post.nested_attributes.clear
206
222
  end
207
223
 
208
224
  let(:post) do
@@ -132,8 +132,8 @@ describe Mongoid::Clients::Factory do
132
132
 
133
133
  let(:config) do
134
134
  {
135
- default: { hosts: [ "127.0.0.1:27017" ], database: database_id },
136
- secondary: { uri: "mongodb://127.0.0.1:27017,127.0.0.1:27018/mongoid_test" }
135
+ default: { hosts: [ "127.0.0.1:1234" ], database: database_id },
136
+ secondary: { uri: "mongodb://127.0.0.1:1234,127.0.0.1:5678/mongoid_test" }
137
137
  }
138
138
  end
139
139
 
@@ -162,7 +162,7 @@ describe Mongoid::Clients::Factory do
162
162
  end
163
163
 
164
164
  it "sets the cluster's seeds" do
165
- expect(seeds).to eq([ "127.0.0.1:27017", "127.0.0.1:27018" ])
165
+ expect(seeds).to eq([ "127.0.0.1:1234", "127.0.0.1:5678" ])
166
166
  end
167
167
  end
168
168
  end
@@ -188,25 +188,40 @@ describe Mongoid::Clients::Options do
188
188
 
189
189
  context 'when returning a criteria' do
190
190
 
191
- let(:context_and_criteria) do
192
- collection = nil
193
- cxt = TestModel.with(read: :secondary) do |klass|
194
- collection = klass.all.collection
195
- klass.persistence_context
191
+ shared_context 'applies secondary read preference' do
192
+
193
+ let(:context_and_criteria) do
194
+ collection = nil
195
+ cxt = TestModel.with(read_secondary_option) do |klass|
196
+ collection = klass.all.collection
197
+ klass.persistence_context
198
+ end
199
+ [ cxt, collection ]
196
200
  end
197
- [ cxt, collection ]
198
- end
199
201
 
200
- let(:persistence_context) do
201
- context_and_criteria[0]
202
+ let(:persistence_context) do
203
+ context_and_criteria[0]
204
+ end
205
+
206
+ let(:client) do
207
+ context_and_criteria[1].client
208
+ end
209
+
210
+ it 'applies the options to the criteria client' do
211
+ expect(client.options['read']).to eq('mode' => :secondary)
212
+ end
202
213
  end
203
214
 
204
- let(:client) do
205
- context_and_criteria[1].client
215
+ context 'read: :secondary shorthand' do
216
+ let(:read_secondary_option) { {read: :secondary} }
217
+
218
+ it_behaves_like 'applies secondary read preference'
206
219
  end
207
220
 
208
- it 'applies the options to the criteria client' do
209
- expect(client.options['read']).to eq(:secondary)
221
+ context 'read: {mode: :secondary}' do
222
+ let(:read_secondary_option) { {read: {mode: :secondary}} }
223
+
224
+ it_behaves_like 'applies secondary read preference'
210
225
  end
211
226
  end
212
227
 
@@ -16,17 +16,17 @@ describe Mongoid::Clients::Sessions do
16
16
  end
17
17
 
18
18
  let(:subscriber) do
19
- Mongoid::Clients.with_name(:other).instance_variable_get(:@monitoring).subscribers['Command'].find do |s|
19
+ Mongoid::Clients.with_name(:other).send(:monitoring).subscribers['Command'].find do |s|
20
20
  s.is_a?(EventSubscriber)
21
21
  end
22
22
  end
23
23
 
24
24
  let(:insert_events) do
25
- subscriber.started_events.select { |event| event.command_name == :insert }
25
+ subscriber.started_events.select { |event| event.command_name == 'insert' }
26
26
  end
27
27
 
28
28
  let(:update_events) do
29
- subscriber.started_events.select { |event| event.command_name == :update }
29
+ subscriber.started_events.select { |event| event.command_name == 'update' }
30
30
  end
31
31
 
32
32
  context 'when a session is used on a model class' do
@@ -0,0 +1,369 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Clients::Sessions do
4
+
5
+ before(:all) do
6
+ CONFIG[:clients][:other] = CONFIG[:clients][:default].dup
7
+ CONFIG[:clients][:other][:database] = 'other'
8
+ Mongoid::Clients.clients.values.each(&:close)
9
+ Mongoid::Config.send(:clients=, CONFIG[:clients])
10
+ Mongoid::Clients.with_name(:other).subscribe(Mongo::Monitoring::COMMAND, EventSubscriber.new)
11
+ end
12
+
13
+ after(:all) do
14
+ Mongoid::Clients.with_name(:other).close
15
+ Mongoid::Clients.clients.delete(:other)
16
+ end
17
+
18
+ let(:subscriber) do
19
+ Mongoid::Clients.with_name(:other).send(:monitoring).subscribers['Command'].find do |s|
20
+ s.is_a?(EventSubscriber)
21
+ end
22
+ end
23
+
24
+ let(:insert_events) do
25
+ subscriber.started_events.select { |event| event.command_name == 'insert' }
26
+ end
27
+
28
+ let(:insert_events_txn_numbers) do
29
+ insert_events.map { |i| i.instance_variable_get(:@integer) }
30
+ end
31
+
32
+ let(:update_events) do
33
+ subscriber.started_events.select { |event| event.command_name == 'update' }
34
+ end
35
+
36
+ let(:update_events_txn_numbers) do
37
+ update_events.map { |i| i.instance_variable_get(:@integer) }
38
+ end
39
+
40
+ let(:other_events) do
41
+ subscriber.started_events.reject { |event| ['insert', 'update'].include?(event.command_name) }
42
+ end
43
+
44
+ context 'when a transaction is used on a model class' do
45
+
46
+ context 'when transactions are supported', if: testing_transactions? do
47
+
48
+ around do |example|
49
+ Mongoid::Clients.with_name(:other).database.collections.each(&:drop)
50
+ Mongoid::Clients.with_name(:other).command(create: :people)
51
+ Mongoid::Clients.with_name(:other).command(create: :posts)
52
+ subscriber.clear_events!
53
+ Person.with(client: :other) do
54
+ example.run
55
+ end
56
+ Mongoid::Clients.with_name(:other).database.collections.each(&:drop)
57
+ end
58
+
59
+ context 'when another thread is started' do
60
+
61
+ let!(:last_use_diff) do
62
+ Person.with_session do |s|
63
+ s.start_transaction
64
+ Person.create
65
+ Person.create
66
+ Thread.new { Person.create }.value
67
+ s.commit_transaction
68
+ end
69
+ end
70
+
71
+ it 'does not use the transaction for that thread' do
72
+ expect(Person.count).to be(2)
73
+ expect(Person.with(client: :default) { Person.count }).to be(1)
74
+ expect(insert_events.count { |e| e.command['startTransaction'] }).to be(1)
75
+ expect(other_events.count { |e| e.command_name == 'commitTransaction' }).to be(1)
76
+ end
77
+ end
78
+
79
+ context 'when the operations in the transactions block are all on the class' do
80
+
81
+ before do
82
+ Person.with_session do |s|
83
+ s.start_transaction
84
+ Person.create
85
+ Person.create
86
+ s.commit_transaction
87
+ end
88
+ end
89
+
90
+ it 'uses a single transaction number for all operations on the class' do
91
+ expect(Person.count).to be(2)
92
+ expect(insert_events_txn_numbers.size).to eq(2)
93
+ expect(insert_events_txn_numbers.uniq.size).to eq(1)
94
+ end
95
+ end
96
+
97
+ context 'when the operations in the transactions block are also on another class' do
98
+
99
+ context 'when the other class uses the same client' do
100
+
101
+ before do
102
+ Post.with(client: :other) do
103
+ Person.with_session do |s|
104
+ s.start_transaction
105
+ Person.create
106
+ Person.create
107
+ Post.create
108
+ s.commit_transaction
109
+ end
110
+ end
111
+ end
112
+
113
+ it 'uses a single transaction number for all operations on the class' do
114
+ expect(Post.with(client: :other) { |klass| klass.count }).to be(1)
115
+ expect(insert_events_txn_numbers.size).to eq(3)
116
+ expect(insert_events_txn_numbers.uniq.size).to eq(1)
117
+ end
118
+ end
119
+
120
+ context 'when the other class uses a different client' do
121
+
122
+ let!(:error) do
123
+ e = nil
124
+ begin
125
+ Person.with_session do |s|
126
+ s.start_transaction
127
+ Person.create
128
+ Person.create
129
+ Post.create
130
+ s.commit_transaction
131
+ end
132
+ rescue => ex
133
+ e = ex
134
+ end
135
+ e
136
+ end
137
+
138
+ it 'raises an error' do
139
+ expect(error).to be_a(Mongoid::Errors::InvalidSessionUse)
140
+ end
141
+
142
+ it 'aborted the transaction' do
143
+ expect(Person.count).to be(0)
144
+ expect(Post.count).to be(0)
145
+ expect(insert_events_txn_numbers.size).to eq(2)
146
+ expect(other_events.count { |e| e.command_name == 'abortTransaction'}).to be(1)
147
+ expect(other_events.count { |e| e.command_name == 'commitTransaction'}).to be(0)
148
+ end
149
+ end
150
+
151
+ context 'when transactions are nested' do
152
+
153
+ let!(:error) do
154
+ e = nil
155
+ begin
156
+ Person.with_session do |s|
157
+ s.start_transaction
158
+ s.start_transaction
159
+ Person.create
160
+ Post.create
161
+ s.commit_transaction
162
+ end
163
+ rescue => ex
164
+ e = ex
165
+ end
166
+ e
167
+ end
168
+
169
+ it 'raises an error' do
170
+ expect(error).to be_a(Mongo::Error::InvalidTransactionOperation)
171
+ end
172
+
173
+ it 'does not execute any operations' do
174
+ expect(Person.count).to be(0)
175
+ expect(Post.count).to be(0)
176
+ expect(insert_events).to be_empty
177
+ end
178
+ end
179
+ end
180
+ end
181
+
182
+ context 'when sessions are supported but transactions are not', if: sessions_supported? && !testing_transactions? do
183
+
184
+ let!(:error) do
185
+ e = nil
186
+ begin
187
+ Person.with_session do |s|
188
+ s.start_transaction
189
+ Person.create
190
+ s.commit_transaction
191
+ end
192
+ rescue => ex
193
+ e = ex
194
+ end
195
+ e
196
+ end
197
+
198
+ it 'raises a transactions not supported error' do
199
+ expect(Person.count).to eq(0)
200
+ expect(error).to be_a(Mongo::Error::OperationFailure)
201
+ end
202
+ end
203
+ end
204
+
205
+ context 'when a transaction is used on a model instance' do
206
+
207
+ let!(:person) do
208
+ Person.with(client: :other) do |klass|
209
+ klass.create
210
+ end
211
+ end
212
+
213
+ context 'when transactions are supported', if: testing_transactions? do
214
+
215
+ around do |example|
216
+ Mongoid::Clients.with_name(:other).database.collections.each(&:drop)
217
+ Mongoid::Clients.with_name(:other).command(create: :people)
218
+ Mongoid::Clients.with_name(:other).command(create: :posts)
219
+ subscriber.clear_events!
220
+ person.with(client: :other) do
221
+ example.run
222
+ end
223
+ Mongoid::Clients.with_name(:other).database.collections.each(&:drop)
224
+ end
225
+
226
+ context 'when the operations in the transaction block are all on the instance' do
227
+
228
+ before do
229
+ person.with_session do |s|
230
+ s.start_transaction
231
+ person.username = 'Emily'
232
+ person.save
233
+ person.age = 80
234
+ person.save
235
+ s.commit_transaction
236
+ end
237
+ end
238
+
239
+ it 'uses a single transaction number for all operations on the class' do
240
+ expect(person.reload.username).to eq('Emily')
241
+ expect(person.reload.age).to eq(80)
242
+ expect(update_events_txn_numbers.size).to eq(2)
243
+ expect(update_events_txn_numbers.uniq.size).to eq(1)
244
+ end
245
+ end
246
+
247
+ context 'when the operations in the transaction block are also on another class' do
248
+
249
+ context 'when the other class uses the same client' do
250
+
251
+ before do
252
+ Post.with(client: :other) do
253
+ person.with_session do |s|
254
+ s.start_transaction
255
+ person.username = 'Emily'
256
+ person.save
257
+ person.posts << Post.create
258
+ s.commit_transaction
259
+ end
260
+ end
261
+ end
262
+
263
+ it 'uses a single transaction number for all operations on the class' do
264
+ expect(person.reload.username).to eq('Emily')
265
+ expect(Post.with(client: :other) { Post.count }).to be(1)
266
+ expect(update_events_txn_numbers.size).to eq(3) # person update, counter cache, post assignment
267
+ expect(update_events_txn_numbers.uniq.size).to eq(1) # person update, counter cache, post assignment
268
+ expect(insert_events_txn_numbers.size).to eq(2)
269
+ expect(insert_events_txn_numbers.uniq.size).to eq(1)
270
+ expect(update_events_txn_numbers.uniq).to eq(insert_events_txn_numbers.uniq)
271
+ end
272
+ end
273
+
274
+ context 'when the other class uses a different client' do
275
+
276
+ let!(:error) do
277
+ e = nil
278
+ begin
279
+ person.with_session do |s|
280
+ s.start_transaction
281
+ person.username = 'Emily'
282
+ person.save
283
+ person.posts << Post.create
284
+ s.commit_transaction
285
+ end
286
+ rescue => ex
287
+ e = ex
288
+ end
289
+ e
290
+ end
291
+
292
+ it 'raises an error' do
293
+ expect(error).to be_a(Mongoid::Errors::InvalidSessionUse)
294
+ end
295
+
296
+ it 'aborted the transction' do
297
+ expect(person.reload.username).not_to eq('Emily')
298
+ expect(Post.count).to be(0)
299
+ expect(update_events_txn_numbers.size).to eq(1)
300
+ expect(insert_events_txn_numbers.size).to eq(1)
301
+ end
302
+ end
303
+
304
+ context 'when transactions are nested' do
305
+
306
+ let!(:error) do
307
+ e = nil
308
+ begin
309
+ person.with_session do |s|
310
+ s.start_transaction
311
+ s.start_transaction
312
+ person.username = 'Emily'
313
+ person.save
314
+ person.posts << Post.create
315
+ s.commit_transaction
316
+ end
317
+ rescue => ex
318
+ e = ex
319
+ end
320
+ e
321
+ end
322
+
323
+ it 'raises an error' do
324
+ expect(error).to be_a(Mongo::Error::InvalidTransactionOperation)
325
+ end
326
+
327
+ it 'does not execute any operations' do
328
+ expect(person.reload.username).not_to eq('Emily')
329
+ expect(Post.count).to be(0)
330
+ expect(update_events).to be_empty
331
+ end
332
+ end
333
+ end
334
+ end
335
+
336
+ context 'when sessions are supported but transactions are not', if: sessions_supported? && !testing_transactions? do
337
+
338
+ around do |example|
339
+ Mongoid::Clients.with_name(:other).database.collections.each(&:drop)
340
+ Mongoid::Clients.with_name(:other).command(create: :people)
341
+ subscriber.clear_events!
342
+ person.with(client: :other) do
343
+ example.run
344
+ end
345
+ Mongoid::Clients.with_name(:other).database.collections.each(&:drop)
346
+ end
347
+
348
+ let!(:error) do
349
+ e = nil
350
+ begin
351
+ person.with_session do |s|
352
+ s.start_transaction
353
+ person.username = 'Emily'
354
+ person.save
355
+ s.commit_transaction
356
+ end
357
+ rescue => ex
358
+ e = ex
359
+ end
360
+ e
361
+ end
362
+
363
+ it 'raises a sessions not supported error' do
364
+ expect(person.reload.username).not_to be('Emily')
365
+ expect(error).to be_a(Mongo::Error::OperationFailure)
366
+ end
367
+ end
368
+ end
369
+ end