mongoid 7.0.1 → 7.0.2
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
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/mongoid/association.rb +0 -1
- data/lib/mongoid/association/depending.rb +22 -1
- data/lib/mongoid/association/embedded/embeds_one/proxy.rb +3 -3
- data/lib/mongoid/association/referenced/has_many/proxy.rb +1 -1
- data/lib/mongoid/association/relatable.rb +16 -2
- data/lib/mongoid/attributes/nested.rb +14 -3
- data/lib/mongoid/contextual/map_reduce.rb +1 -1
- data/lib/mongoid/copyable.rb +3 -2
- data/lib/mongoid/document.rb +2 -0
- data/lib/mongoid/matchable.rb +3 -0
- data/lib/mongoid/matchable/nor.rb +37 -0
- data/lib/mongoid/persistable/settable.rb +58 -13
- data/lib/mongoid/persistence_context.rb +5 -1
- data/lib/mongoid/touchable.rb +102 -0
- data/lib/mongoid/version.rb +1 -1
- data/spec/app/models/array_field.rb +7 -0
- data/spec/app/models/updatable.rb +7 -0
- data/spec/mongoid/association/accessors_spec.rb +39 -0
- data/spec/mongoid/association/depending_spec.rb +253 -0
- data/spec/mongoid/association/polymorphic_spec.rb +59 -0
- data/spec/mongoid/association/referenced/belongs_to_spec.rb +3 -3
- data/spec/mongoid/association/referenced/has_one_spec.rb +59 -0
- data/spec/mongoid/attributes/nested_spec.rb +18 -2
- data/spec/mongoid/clients/factory_spec.rb +3 -3
- data/spec/mongoid/clients/options_spec.rb +28 -13
- data/spec/mongoid/clients/sessions_spec.rb +3 -3
- data/spec/mongoid/clients/transactions_spec.rb +369 -0
- data/spec/mongoid/copyable_spec.rb +16 -0
- data/spec/mongoid/matchable/nor_spec.rb +209 -0
- data/spec/mongoid/matchable_spec.rb +26 -1
- data/spec/mongoid/persistable/settable_spec.rb +128 -9
- data/spec/mongoid/{association/touchable_spec.rb → touchable_spec.rb} +28 -7
- data/spec/spec_helper.rb +8 -0
- metadata +457 -448
- metadata.gz.sig +0 -0
- 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:
|
136
|
-
secondary: { uri: "mongodb://127.0.0.1:
|
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:
|
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
|
-
|
192
|
-
|
193
|
-
|
194
|
-
collection =
|
195
|
-
klass
|
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
|
-
|
201
|
-
|
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
|
-
|
205
|
-
|
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
|
-
|
209
|
-
|
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).
|
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 ==
|
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 ==
|
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
|