mongoid 6.4.1 → 6.4.8

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 (58) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +1 -3
  4. data/Rakefile +26 -0
  5. data/lib/mongoid.rb +1 -1
  6. data/lib/mongoid/contextual/map_reduce.rb +1 -1
  7. data/lib/mongoid/criteria/modifiable.rb +12 -2
  8. data/lib/mongoid/criteria/queryable/selectable.rb +34 -7
  9. data/lib/mongoid/document.rb +4 -4
  10. data/lib/mongoid/extensions/big_decimal.rb +1 -1
  11. data/lib/mongoid/extensions/regexp.rb +1 -0
  12. data/lib/mongoid/extensions/string.rb +3 -1
  13. data/lib/mongoid/matchable.rb +3 -0
  14. data/lib/mongoid/matchable/nor.rb +37 -0
  15. data/lib/mongoid/persistable/settable.rb +5 -5
  16. data/lib/mongoid/persistence_context.rb +4 -0
  17. data/lib/mongoid/query_cache.rb +64 -19
  18. data/lib/mongoid/railtie.rb +17 -0
  19. data/lib/mongoid/railties/controller_runtime.rb +86 -0
  20. data/lib/mongoid/scopable.rb +3 -3
  21. data/lib/mongoid/threaded.rb +36 -0
  22. data/lib/mongoid/version.rb +1 -1
  23. data/spec/app/models/array_field.rb +7 -0
  24. data/spec/app/models/delegating_patient.rb +16 -0
  25. data/spec/integration/document_spec.rb +22 -0
  26. data/spec/mongoid/clients/factory_spec.rb +52 -28
  27. data/spec/mongoid/clients/options_spec.rb +30 -15
  28. data/spec/mongoid/clients/sessions_spec.rb +12 -3
  29. data/spec/mongoid/contextual/geo_near_spec.rb +1 -0
  30. data/spec/mongoid/contextual/mongo_spec.rb +2 -2
  31. data/spec/mongoid/criteria/modifiable_spec.rb +59 -10
  32. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +3 -3
  33. data/spec/mongoid/criteria/queryable/selectable_spec.rb +42 -3
  34. data/spec/mongoid/criteria/queryable/selector_spec.rb +2 -2
  35. data/spec/mongoid/criteria/scopable_spec.rb +81 -0
  36. data/spec/mongoid/criteria_spec.rb +4 -1
  37. data/spec/mongoid/document_spec.rb +54 -0
  38. data/spec/mongoid/extensions/big_decimal_spec.rb +9 -9
  39. data/spec/mongoid/extensions/regexp_spec.rb +23 -0
  40. data/spec/mongoid/extensions/string_spec.rb +35 -7
  41. data/spec/mongoid/fields_spec.rb +1 -1
  42. data/spec/mongoid/findable_spec.rb +1 -1
  43. data/spec/mongoid/matchable/nor_spec.rb +209 -0
  44. data/spec/mongoid/matchable_spec.rb +26 -1
  45. data/spec/mongoid/persistable/incrementable_spec.rb +6 -6
  46. data/spec/mongoid/persistable/settable_spec.rb +19 -0
  47. data/spec/mongoid/query_cache_spec.rb +87 -18
  48. data/spec/mongoid/scopable_spec.rb +13 -0
  49. data/spec/mongoid/threaded_spec.rb +68 -0
  50. data/spec/rails/controller_extension/controller_runtime_spec.rb +110 -0
  51. data/spec/spec_helper.rb +10 -0
  52. data/spec/support/cluster_config.rb +158 -0
  53. data/spec/support/constraints.rb +101 -0
  54. data/spec/support/macros.rb +20 -0
  55. data/spec/support/session_registry.rb +50 -0
  56. data/spec/support/spec_config.rb +42 -0
  57. metadata +43 -23
  58. metadata.gz.sig +0 -0
@@ -66,15 +66,15 @@ describe Mongoid::Persistable::Incrementable do
66
66
  context "when providing big decimal values" do
67
67
 
68
68
  let(:five) do
69
- BigDecimal.new("5.0")
69
+ BigDecimal("5.0")
70
70
  end
71
71
 
72
72
  let(:neg_ten) do
73
- BigDecimal.new("-10.0")
73
+ BigDecimal("-10.0")
74
74
  end
75
75
 
76
76
  let(:thirty) do
77
- BigDecimal.new("30.0")
77
+ BigDecimal("30.0")
78
78
  end
79
79
 
80
80
  let!(:inc) do
@@ -151,15 +151,15 @@ describe Mongoid::Persistable::Incrementable do
151
151
  context "when providing big decimal values" do
152
152
 
153
153
  let(:five) do
154
- BigDecimal.new("5.0")
154
+ BigDecimal("5.0")
155
155
  end
156
156
 
157
157
  let(:neg_ten) do
158
- BigDecimal.new("-10.0")
158
+ BigDecimal("-10.0")
159
159
  end
160
160
 
161
161
  let(:thirty) do
162
- BigDecimal.new("30.0")
162
+ BigDecimal("30.0")
163
163
  end
164
164
 
165
165
  let!(:inc) do
@@ -315,6 +315,25 @@ describe Mongoid::Persistable::Settable do
315
315
  end
316
316
  end
317
317
 
318
+ context 'when a leaf value in the nested hash is updated to a number' do
319
+
320
+ let(:church) do
321
+ Church.new.tap do |a|
322
+ a.location = {'address' => {'city' => 'Berlin', 'street' => 'Yorckstr'}}
323
+ a.name = 'Church1'
324
+ a.save
325
+ end
326
+ end
327
+
328
+ before do
329
+ church.set('location.address.city' => 12345)
330
+ end
331
+
332
+ it 'updates the nested value to the correct value' do
333
+ expect(church.name).to eq('Church1')
334
+ expect(church.location).to eql({'address' => {'city' => 12345, 'street' => 'Yorckstr'}})
335
+ end
336
+ end
318
337
 
319
338
  context 'when the nested hash is many levels deep' do
320
339
 
@@ -7,6 +7,20 @@ describe Mongoid::QueryCache do
7
7
  Mongoid::QueryCache.cache { spec.run }
8
8
  end
9
9
 
10
+ before(:all) do
11
+ # It is likely that there are other session leaks in the driver
12
+ # and/or Mongoid that are unrelated to the query cache. Clear the
13
+ # SessionRegistry at the start of these tests in order to detect leaks that
14
+ # occur only within the scope of these tests.
15
+ #
16
+ # Other session leaks will be detected and addressed as part of RUBY-2391.
17
+ SessionRegistry.instance.clear_registry
18
+ end
19
+
20
+ after do
21
+ SessionRegistry.instance.verify_sessions_ended!
22
+ end
23
+
10
24
  context 'when iterating over objects sharing the same base' do
11
25
 
12
26
  let(:server) do
@@ -38,9 +52,10 @@ describe Mongoid::QueryCache do
38
52
  end
39
53
 
40
54
  it 'queries for each access to the base' do
41
- expect(server).to receive(:with_connection).exactly(relations.size).times.and_call_original
42
- relations.each do |object|
43
- object.person
55
+ expect_query(relations.size) do
56
+ relations.each do |object|
57
+ object.person
58
+ end
44
59
  end
45
60
  end
46
61
  end
@@ -52,9 +67,10 @@ describe Mongoid::QueryCache do
52
67
  end
53
68
 
54
69
  it 'queries only once for the base' do
55
- expect(server).to receive(:with_connection).exactly(1).times.and_call_original
56
- relations.each do |object|
57
- object.person
70
+ expect_query(1) do
71
+ relations.each do |object|
72
+ object.person
73
+ end
58
74
  end
59
75
  end
60
76
  end
@@ -205,6 +221,10 @@ describe Mongoid::QueryCache do
205
221
  end
206
222
 
207
223
  before do
224
+ 10.times do |i|
225
+ game.ratings << Rating.create!(value: i+1)
226
+ end
227
+
208
228
  game.ratings.where(:value.gt => 5).asc(:id).all.to_a
209
229
  end
210
230
 
@@ -212,7 +232,9 @@ describe Mongoid::QueryCache do
212
232
 
213
233
  it "uses the cache" do
214
234
  expect_no_queries do
215
- game.ratings.where(:value.gt => 5).limit(2).asc(:id).to_a
235
+ result = game.ratings.where(:value.gt => 5).limit(2).asc(:id).to_a
236
+ expect(result.length).to eq(2)
237
+ expect(result.map { |r| r['value'] }).to eq([6, 7])
216
238
  end
217
239
  end
218
240
  end
@@ -225,14 +247,23 @@ describe Mongoid::QueryCache do
225
247
  end
226
248
 
227
249
  before do
250
+ 10.times do |i|
251
+ game.ratings << Rating.create!(value: i+1)
252
+ end
253
+
228
254
  game.ratings.where(:value.gt => 5).limit(3).asc(:id).all.to_a
229
255
  end
230
256
 
231
257
  context "when the next query has a limit" do
258
+ # Server versions older than 3.2 also perform a killCursors operation,
259
+ # which causes this test to fail.
260
+ min_server_version '3.2'
232
261
 
233
262
  it "queries again" do
234
263
  expect_query(1) do
235
- game.ratings.where(:value.gt => 5).limit(2).asc(:id).to_a
264
+ result = game.ratings.where(:value.gt => 5).limit(2).asc(:id).to_a
265
+ expect(result.length).to eq(2)
266
+ expect(result.map { |r| r['value'] }).to eq([6, 7])
236
267
  end
237
268
  end
238
269
  end
@@ -241,7 +272,9 @@ describe Mongoid::QueryCache do
241
272
 
242
273
  it "queries again" do
243
274
  expect_query(1) do
244
- game.ratings.where(:value.gt => 5).asc(:id).to_a
275
+ result = game.ratings.where(:value.gt => 5).asc(:id).to_a
276
+ expect(result.length).to eq(5)
277
+ expect(result.map { |r| r['value'] }).to eq([6, 7, 8, 9, 10])
245
278
  end
246
279
  end
247
280
  end
@@ -254,21 +287,34 @@ describe Mongoid::QueryCache do
254
287
  end
255
288
 
256
289
  before do
290
+ 10.times do |i|
291
+ game.ratings << Rating.create!(value: i+1)
292
+ end
293
+
257
294
  game.ratings.where(:value.gt => 5).asc(:id).all.to_a
258
295
  end
259
296
 
260
297
  it "does not query again" do
261
298
  expect_no_queries do
262
- game.ratings.where(:value.gt => 5).asc(:id).first
299
+ result = game.ratings.where(:value.gt => 5).asc(:id).first
300
+ expect(result['value']).to eq(6)
263
301
  end
264
302
  end
265
303
  end
266
304
 
267
305
  context "when limiting the result" do
306
+ before do
307
+ Band.destroy_all
308
+
309
+ 5.times { |i| Band.create!(name: "Band #{i}") }
310
+ Band.all.to_a
311
+ end
268
312
 
269
313
  it "does not query again" do
270
314
  expect_query(0) do
271
- Band.limit(2).all.to_a
315
+ result = Band.limit(2).all.to_a
316
+ expect(result.length).to eq(2)
317
+ expect(result.map { |r| r["name"] }).to eq(["Band 0", "Band 1"])
272
318
  end
273
319
  end
274
320
  end
@@ -276,12 +322,16 @@ describe Mongoid::QueryCache do
276
322
  context "when specifying a different skip value" do
277
323
 
278
324
  before do
279
- Band.limit(2).skip(1).all.to_a
325
+ Band.destroy_all
326
+
327
+ 5.times { |i| Band.create!(name: "Band #{i}") }
280
328
  end
281
329
 
282
330
  it "queries again" do
283
331
  expect_query(1) do
284
- Band.limit(2).skip(3).all.to_a
332
+ result = Band.limit(2).skip(3).all.to_a
333
+ expect(result.length).to eq(2)
334
+ expect(result.map { |r| r["name"] }).to eq(["Band 3", "Band 4"])
285
335
  end
286
336
  end
287
337
  end
@@ -317,6 +367,25 @@ describe Mongoid::QueryCache do
317
367
  end
318
368
  end
319
369
 
370
+ context 'when querying colleciton larger than the batch size' do
371
+ before do
372
+ Band.destroy_all
373
+ 101.times { |i| Band.create!(_id: i) }
374
+ end
375
+
376
+ it 'does not raise an exception when querying multiple times' do
377
+ expect do
378
+ results1 = Band.all.to_a
379
+ expect(results1.length).to eq(101)
380
+ expect(results1.map { |band| band["_id"] }).to eq([*0..100])
381
+
382
+ results2 = Band.all.to_a
383
+ expect(results2.length).to eq(101)
384
+ expect(results2.map { |band| band["_id"] }).to eq([*0..100])
385
+ end.not_to raise_error
386
+ end
387
+ end
388
+
320
389
  context "when query caching is enabled and the batch_size is set" do
321
390
 
322
391
  around(:each) do |example|
@@ -428,15 +497,15 @@ describe Mongoid::QueryCache do
428
497
  context "when querying a very large collection" do
429
498
 
430
499
  before do
431
- 123.times { Band.create! }
500
+ 100.times { Band.create! }
432
501
  end
433
502
 
434
503
  it "returns the right number of records" do
435
- expect(Band.all.to_a.length).to eq(123)
504
+ expect(Band.all.to_a.length).to eq(100)
436
505
  end
437
506
 
438
507
  it "#pluck returns the same count of objects" do
439
- expect(Band.pluck(:name).length).to eq(123)
508
+ expect(Band.pluck(:name).length).to eq(100)
440
509
  end
441
510
 
442
511
  context "when loading all the documents" do
@@ -447,12 +516,12 @@ describe Mongoid::QueryCache do
447
516
 
448
517
  it "caches the complete result of the query" do
449
518
  expect_no_queries do
450
- expect(Band.all.to_a.length).to eq(123)
519
+ expect(Band.all.to_a.length).to eq(100)
451
520
  end
452
521
  end
453
522
 
454
523
  it "returns the same count of objects when using #pluck" do
455
- expect(Band.pluck(:name).length).to eq(123)
524
+ expect(Band.pluck(:name).length).to eq(100)
456
525
  end
457
526
  end
458
527
  end
@@ -1130,6 +1130,19 @@ describe Mongoid::Scopable do
1130
1130
  it "sets the threading options" do
1131
1131
  Band.without_default_scope do
1132
1132
  expect(Mongoid::Threaded).to be_executing(:without_default_scope)
1133
+ expect(Mongoid::Threaded.without_default_scope?(Band)).to be(true)
1134
+ end
1135
+ end
1136
+
1137
+ it "suppresses default scope on the given model within the given block" do
1138
+ Appointment.without_default_scope do
1139
+ expect(Appointment.all.selector).to be_empty
1140
+ end
1141
+ end
1142
+
1143
+ it "does not affect other models' default scopes within the given block" do
1144
+ Appointment.without_default_scope do
1145
+ expect(Audio.all.selector).not_to be_empty
1133
1146
  end
1134
1147
  end
1135
1148
  end
@@ -233,4 +233,72 @@ describe Mongoid::Threaded do
233
233
  end
234
234
  end
235
235
  end
236
+
237
+ describe "#begin_without_default_scope" do
238
+
239
+ let(:klass) do
240
+ Appointment
241
+ end
242
+
243
+ after do
244
+ described_class.exit_without_default_scope(klass)
245
+ end
246
+
247
+ it "adds the given class to the without_default_scope stack" do
248
+ described_class.begin_without_default_scope(klass)
249
+
250
+ expect(described_class.stack(:without_default_scope)).to include(klass)
251
+ end
252
+ end
253
+
254
+ describe "#exit_without_default_scope" do
255
+
256
+ let(:klass) do
257
+ Appointment
258
+ end
259
+
260
+ before do
261
+ described_class.begin_without_default_scope(klass)
262
+ end
263
+
264
+ it "removes the given class from the without_default_scope stack" do
265
+ described_class.exit_without_default_scope(klass)
266
+
267
+ expect(described_class.stack(:without_default_scope)).not_to include(klass)
268
+ end
269
+ end
270
+
271
+ describe "#without_default_scope?" do
272
+
273
+ let(:klass) do
274
+ Appointment
275
+ end
276
+
277
+ context "when klass has begun without_default_scope" do
278
+
279
+ before do
280
+ described_class.begin_without_default_scope(klass)
281
+ end
282
+
283
+ after do
284
+ described_class.exit_without_default_scope(klass)
285
+ end
286
+
287
+ it "returns true" do
288
+ expect(described_class.without_default_scope?(klass)).to be(true)
289
+ end
290
+ end
291
+
292
+ context "when klass has exited without_default_scope" do
293
+
294
+ before do
295
+ described_class.begin_without_default_scope(klass)
296
+ described_class.exit_without_default_scope(klass)
297
+ end
298
+
299
+ it "returns false" do
300
+ expect(described_class.without_default_scope?(klass)).to be(false)
301
+ end
302
+ end
303
+ end
236
304
  end
@@ -0,0 +1,110 @@
1
+ require "spec_helper"
2
+ require "mongoid/railties/controller_runtime"
3
+
4
+ describe "Mongoid::Railties::ControllerRuntime" do
5
+ controller_runtime = Mongoid::Railties::ControllerRuntime
6
+ collector = controller_runtime::Collector
7
+
8
+ def set_metric value
9
+ Thread.current["Mongoid.controller_runtime"] = value
10
+ end
11
+
12
+ def clear_metric!
13
+ set_metric 0
14
+ end
15
+
16
+ describe "Collector" do
17
+
18
+ it "stores the metric in thread-safe manner" do
19
+ clear_metric!
20
+ expect(collector.runtime).to eq(0)
21
+ set_metric 42
22
+ expect(collector.runtime).to eq(42)
23
+ end
24
+
25
+ it "sets metric on both succeeded and failed" do
26
+ instance = collector.new
27
+ event_payload = OpenStruct.new duration: 42
28
+
29
+ clear_metric!
30
+ instance.succeeded event_payload
31
+ expect(collector.runtime).to eq(42)
32
+
33
+ clear_metric!
34
+ instance.failed event_payload
35
+ expect(collector.runtime).to eq(42)
36
+ end
37
+
38
+ it "resets the metric and returns the value" do
39
+ clear_metric!
40
+ expect(collector.reset_runtime).to eq(0)
41
+ set_metric 42
42
+ expect(collector.reset_runtime).to eq(42)
43
+ expect(collector.runtime).to eq(0)
44
+ end
45
+
46
+ end
47
+
48
+ reference_controller_class = Class.new do
49
+ def process_action *_
50
+ @process_action = true
51
+ end
52
+
53
+ def cleanup_view_runtime *_
54
+ @cleanup_view_runtime.call
55
+ end
56
+
57
+ def append_info_to_payload *_
58
+ @append_info_to_payload = true
59
+ end
60
+
61
+ def self.log_process_action *_
62
+ @log_process_action.call
63
+ end
64
+ end
65
+
66
+ controller_class = Class.new reference_controller_class do
67
+ include controller_runtime::ControllerExtension
68
+ end
69
+
70
+ let(:controller){ controller_class.new }
71
+
72
+ it "resets the metric before each action" do
73
+ set_metric 42
74
+ controller.send(:process_action, 'foo')
75
+ expect(collector.runtime).to be(0)
76
+ expect(controller.instance_variable_get "@process_action").to be(true)
77
+ end
78
+
79
+ it "strips the metric of other sources of the runtime" do
80
+ set_metric 1
81
+ controller.instance_variable_set "@cleanup_view_runtime", ->{
82
+ controller.instance_variable_set "@cleanup_view_runtime", true
83
+ set_metric 13
84
+ 42
85
+ }
86
+ returned = controller.send :cleanup_view_runtime
87
+ expect(controller.instance_variable_get "@cleanup_view_runtime").to be(true)
88
+ expect(controller.mongoid_runtime).to eq(14)
89
+ expect(returned).to be(29)
90
+ end
91
+
92
+ it "appends the metric to payload" do
93
+ payload = {}
94
+ set_metric 42
95
+ controller.send :append_info_to_payload, payload
96
+ expect(controller.instance_variable_get "@append_info_to_payload").to be(true)
97
+ expect(payload[:mongoid_runtime]).to eq(42)
98
+ end
99
+
100
+ it "adds metric to log message" do
101
+ controller_class.instance_variable_set "@log_process_action", ->{
102
+ controller_class.instance_variable_set "@log_process_action", true
103
+ []
104
+ }
105
+ messages = controller_class.log_process_action mongoid_runtime: 42.101
106
+ expect(controller_class.instance_variable_get "@log_process_action").to be(true)
107
+ expect(messages).to eq(["MongoDB: 42.1ms"])
108
+ end
109
+
110
+ end