mongoid 6.4.0 → 6.4.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Rakefile +26 -0
  5. data/lib/mongoid.rb +1 -1
  6. data/lib/mongoid/clients/sessions.rb +2 -2
  7. data/lib/mongoid/contextual/aggregable/mongo.rb +1 -1
  8. data/lib/mongoid/contextual/map_reduce.rb +4 -4
  9. data/lib/mongoid/contextual/memory.rb +4 -4
  10. data/lib/mongoid/contextual/mongo.rb +3 -3
  11. data/lib/mongoid/criteria/modifiable.rb +12 -2
  12. data/lib/mongoid/criteria/queryable/selectable.rb +34 -7
  13. data/lib/mongoid/document.rb +4 -4
  14. data/lib/mongoid/extensions/big_decimal.rb +1 -1
  15. data/lib/mongoid/extensions/regexp.rb +1 -0
  16. data/lib/mongoid/extensions/string.rb +3 -1
  17. data/lib/mongoid/indexable.rb +4 -4
  18. data/lib/mongoid/matchable.rb +3 -0
  19. data/lib/mongoid/matchable/nor.rb +37 -0
  20. data/lib/mongoid/persistable.rb +1 -1
  21. data/lib/mongoid/persistable/creatable.rb +2 -2
  22. data/lib/mongoid/persistable/deletable.rb +2 -2
  23. data/lib/mongoid/persistable/settable.rb +5 -5
  24. data/lib/mongoid/persistable/updatable.rb +2 -2
  25. data/lib/mongoid/persistable/upsertable.rb +1 -1
  26. data/lib/mongoid/persistence_context.rb +4 -0
  27. data/lib/mongoid/query_cache.rb +21 -10
  28. data/lib/mongoid/railtie.rb +17 -0
  29. data/lib/mongoid/railties/controller_runtime.rb +86 -0
  30. data/lib/mongoid/relations/embedded/batchable.rb +4 -4
  31. data/lib/mongoid/relations/embedded/many.rb +23 -0
  32. data/lib/mongoid/relations/many.rb +2 -2
  33. data/lib/mongoid/relations/referenced/many.rb +1 -1
  34. data/lib/mongoid/relations/touchable.rb +1 -1
  35. data/lib/mongoid/reloadable.rb +1 -1
  36. data/lib/mongoid/scopable.rb +3 -3
  37. data/lib/mongoid/tasks/database.rb +2 -2
  38. data/lib/mongoid/threaded.rb +36 -0
  39. data/lib/mongoid/version.rb +1 -1
  40. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +4 -0
  41. data/spec/app/models/array_field.rb +7 -0
  42. data/spec/app/models/delegating_patient.rb +16 -0
  43. data/spec/integration/document_spec.rb +22 -0
  44. data/spec/mongoid/clients/factory_spec.rb +52 -28
  45. data/spec/mongoid/clients/options_spec.rb +30 -15
  46. data/spec/mongoid/clients/sessions_spec.rb +12 -3
  47. data/spec/mongoid/contextual/geo_near_spec.rb +1 -0
  48. data/spec/mongoid/contextual/mongo_spec.rb +2 -2
  49. data/spec/mongoid/criteria/modifiable_spec.rb +59 -10
  50. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +3 -3
  51. data/spec/mongoid/criteria/queryable/selectable_spec.rb +42 -3
  52. data/spec/mongoid/criteria/queryable/selector_spec.rb +2 -2
  53. data/spec/mongoid/criteria/scopable_spec.rb +81 -0
  54. data/spec/mongoid/criteria_spec.rb +4 -1
  55. data/spec/mongoid/document_spec.rb +54 -0
  56. data/spec/mongoid/extensions/big_decimal_spec.rb +9 -9
  57. data/spec/mongoid/extensions/regexp_spec.rb +23 -0
  58. data/spec/mongoid/extensions/string_spec.rb +35 -7
  59. data/spec/mongoid/fields_spec.rb +1 -1
  60. data/spec/mongoid/findable_spec.rb +1 -1
  61. data/spec/mongoid/matchable/nor_spec.rb +209 -0
  62. data/spec/mongoid/matchable_spec.rb +26 -1
  63. data/spec/mongoid/persistable/incrementable_spec.rb +6 -6
  64. data/spec/mongoid/persistable/settable_spec.rb +35 -1
  65. data/spec/mongoid/query_cache_spec.rb +73 -18
  66. data/spec/mongoid/relations/embedded/many_spec.rb +246 -16
  67. data/spec/mongoid/scopable_spec.rb +13 -0
  68. data/spec/mongoid/threaded_spec.rb +68 -0
  69. data/spec/rails/controller_extension/controller_runtime_spec.rb +110 -0
  70. data/spec/spec_helper.rb +9 -0
  71. data/spec/support/cluster_config.rb +158 -0
  72. data/spec/support/constraints.rb +101 -0
  73. data/spec/support/macros.rb +20 -0
  74. data/spec/support/spec_config.rb +42 -0
  75. metadata +41 -23
  76. metadata.gz.sig +0 -0
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ArrayField
4
+ include Mongoid::Document
5
+
6
+ field :af, type: Array
7
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ class DelegatingPatient
5
+ include Mongoid::Document
6
+
7
+ embeds_one :email
8
+
9
+ # Instance level delegation
10
+ delegate :address, to: :email
11
+
12
+ class << self
13
+ # Class level delegation
14
+ delegate :default_client, to: ::Mongoid
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require 'spec_helper'
5
+
6
+ describe Mongoid::Document do
7
+ context 'when including class uses delegate' do
8
+ let(:patient) do
9
+ DelegatingPatient.new(
10
+ email: Email.new(address: 'test@example.com'),
11
+ )
12
+ end
13
+
14
+ it 'works for instance level delegation' do
15
+ patient.address.should == 'test@example.com'
16
+ end
17
+
18
+ it 'works for class level delegation' do
19
+ DelegatingPatient.default_client.should be Mongoid.default_client
20
+ end
21
+ end
22
+ end
@@ -1,7 +1,35 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require "spec_helper"
2
5
 
3
6
  describe Mongoid::Clients::Factory do
4
7
 
8
+ shared_examples_for 'includes seed address' do
9
+ let(:configured_address) do
10
+ address = SpecConfig.instance.addresses.first
11
+ unless address.include?(':')
12
+ address = "#{address}:27017"
13
+ end
14
+ address
15
+ end
16
+
17
+ let(:expected_addresses) do
18
+ [
19
+ configured_address,
20
+ configured_address.sub(/\Alocalhost:/, '127.0.0.1:'),
21
+ configured_address.sub(/\A127\.0\.0\.1:/, 'localhost:'),
22
+ ].uniq
23
+ end
24
+
25
+ it 'includes seed address' do
26
+ ok = cluster_addresses.any? do |address|
27
+ expected_addresses.include?(address)
28
+ end
29
+ expect(ok).to be true
30
+ end
31
+ end
32
+
5
33
  describe ".create" do
6
34
 
7
35
  context "when provided a name" do
@@ -12,8 +40,8 @@ describe Mongoid::Clients::Factory do
12
40
 
13
41
  let(:config) do
14
42
  {
15
- default: { hosts: [ "127.0.0.1:27017" ], database: database_id },
16
- secondary: { hosts: [ "127.0.0.1:27017" ], database: database_id }
43
+ default: { hosts: SpecConfig.instance.addresses, database: database_id },
44
+ secondary: { hosts: SpecConfig.instance.addresses, database: database_id }
17
45
  }
18
46
  end
19
47
 
@@ -37,10 +65,12 @@ describe Mongoid::Clients::Factory do
37
65
  expect(client).to be_a(Mongo::Client)
38
66
  end
39
67
 
40
- it "sets the cluster's seeds" do
41
- expect(cluster.addresses.first.to_s).to eq("127.0.0.1:27017")
68
+ let(:cluster_addresses) do
69
+ cluster.addresses.map(&:to_s)
42
70
  end
43
71
 
72
+ it_behaves_like 'includes seed address'
73
+
44
74
  it "sets the platform to Mongoid's platform constant" do
45
75
  expect(client.options[:platform]).to eq(Mongoid::PLATFORM_DETAILS)
46
76
  end
@@ -80,11 +110,11 @@ describe Mongoid::Clients::Factory do
80
110
  end
81
111
 
82
112
  it "sets the cluster's seed ports to 27017" do
83
- expect(cluster.addresses.first.to_s).to eq("127.0.0.1:27017")
113
+ expect(%w(127.0.0.1:27017 localhost:27017)).to include(cluster.addresses.first.to_s)
84
114
  end
85
115
 
86
116
  it "sets ips with no ports to 27017" do
87
- expect(default.cluster.addresses.first.to_s).to eq("127.0.0.1:27017")
117
+ expect(%w(127.0.0.1:27017 localhost:27017)).to include(cluster.addresses.first.to_s)
88
118
  end
89
119
  end
90
120
 
@@ -120,7 +150,7 @@ describe Mongoid::Clients::Factory do
120
150
  end
121
151
 
122
152
  it "sets the cluster's seeds" do
123
- expect(cluster.addresses.first.to_s).to eq("127.0.0.1:27017")
153
+ expect(%w(127.0.0.1:27017 localhost:27017)).to include(cluster.addresses.first.to_s)
124
154
  end
125
155
 
126
156
  it "sets the database" do
@@ -132,8 +162,8 @@ describe Mongoid::Clients::Factory do
132
162
 
133
163
  let(:config) do
134
164
  {
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" }
165
+ default: { hosts: [ "127.0.0.1:1234" ], database: database_id, server_selection_timeout: 1 },
166
+ secondary: { uri: "mongodb://127.0.0.1:1234,127.0.0.1:5678/mongoid_test?serverSelectionTimeoutMS=1000" }
137
167
  }
138
168
  end
139
169
 
@@ -162,7 +192,7 @@ describe Mongoid::Clients::Factory do
162
192
  end
163
193
 
164
194
  it "sets the cluster's seeds" do
165
- expect(seeds).to eq([ "127.0.0.1:27017", "127.0.0.1:27018" ])
195
+ expect(seeds).to eq([ "127.0.0.1:1234", "127.0.0.1:5678" ])
166
196
  end
167
197
  end
168
198
  end
@@ -181,7 +211,7 @@ describe Mongoid::Clients::Factory do
181
211
  context "when no name is provided" do
182
212
 
183
213
  let(:config) do
184
- { default: { hosts: ["127.0.0.1:27017"], database: database_id }}
214
+ { default: { hosts: SpecConfig.instance.addresses, database: database_id }}
185
215
  end
186
216
 
187
217
  before do
@@ -200,17 +230,15 @@ describe Mongoid::Clients::Factory do
200
230
  client.cluster
201
231
  end
202
232
 
203
- let(:seeds) do
204
- cluster.addresses.map{ |address| address.to_s }
233
+ let(:cluster_addresses) do
234
+ cluster.addresses.map(&:to_s)
205
235
  end
206
236
 
207
237
  it "returns the default client" do
208
238
  expect(client).to be_a(Mongo::Client)
209
239
  end
210
240
 
211
- it "sets the cluster's seeds" do
212
- expect(seeds).to eq([ "127.0.0.1:27017" ])
213
- end
241
+ it_behaves_like 'includes seed address'
214
242
  end
215
243
 
216
244
  context "when nil is provided and no default config" do
@@ -230,7 +258,7 @@ describe Mongoid::Clients::Factory do
230
258
  describe ".default" do
231
259
 
232
260
  let(:config) do
233
- { default: { hosts: ["127.0.0.1:27017"], database: database_id }}
261
+ { default: { hosts: SpecConfig.instance.addresses, database: database_id }}
234
262
  end
235
263
 
236
264
  before do
@@ -249,17 +277,15 @@ describe Mongoid::Clients::Factory do
249
277
  client.cluster
250
278
  end
251
279
 
252
- let(:seeds) do
253
- cluster.addresses.map{ |address| address.to_s }
280
+ let(:cluster_addresses) do
281
+ cluster.addresses.map(&:to_s)
254
282
  end
255
283
 
256
284
  it "returns the default client" do
257
285
  expect(client).to be_a(Mongo::Client)
258
286
  end
259
287
 
260
- it "sets the cluster's seeds" do
261
- expect(seeds).to eq([ "127.0.0.1:27017" ])
262
- end
288
+ it_behaves_like 'includes seed address'
263
289
  end
264
290
 
265
291
  context "when options are provided with string keys" do
@@ -267,7 +293,7 @@ describe Mongoid::Clients::Factory do
267
293
  let(:config) do
268
294
  {
269
295
  default: {
270
- hosts: [ "127.0.0.1:27017" ],
296
+ hosts: SpecConfig.instance.addresses,
271
297
  database: database_id,
272
298
  options: {
273
299
  "server_selection_timeout" => 10,
@@ -293,17 +319,15 @@ describe Mongoid::Clients::Factory do
293
319
  client.cluster
294
320
  end
295
321
 
296
- let(:seeds) do
297
- cluster.addresses.map{ |address| address.to_s }
322
+ let(:cluster_addresses) do
323
+ cluster.addresses.map(&:to_s)
298
324
  end
299
325
 
300
326
  it "returns the default client" do
301
327
  expect(client).to be_a(Mongo::Client)
302
328
  end
303
329
 
304
- it "sets the cluster's seeds" do
305
- expect(seeds).to eq([ "127.0.0.1:27017" ])
306
- end
330
+ it_behaves_like 'includes seed address'
307
331
 
308
332
  it "sets the server selection timeout" do
309
333
  expect(cluster.options[:server_selection_timeout]).to eq(10)
@@ -177,25 +177,40 @@ describe Mongoid::Clients::Options do
177
177
 
178
178
  context 'when returning a criteria' do
179
179
 
180
- let(:context_and_criteria) do
181
- collection = nil
182
- cxt = Band.with(read: :secondary) do |klass|
183
- collection = klass.all.collection
184
- klass.persistence_context
180
+ shared_context 'applies secondary read preference' do
181
+
182
+ let(:context_and_criteria) do
183
+ collection = nil
184
+ cxt = Band.with(read_secondary_option) do |klass|
185
+ collection = klass.all.collection
186
+ klass.persistence_context
187
+ end
188
+ [ cxt, collection ]
185
189
  end
186
- [ cxt, collection ]
187
- end
188
190
 
189
- let(:persistence_context) do
190
- context_and_criteria[0]
191
+ let(:persistence_context) do
192
+ context_and_criteria[0]
193
+ end
194
+
195
+ let(:client) do
196
+ context_and_criteria[1].client
197
+ end
198
+
199
+ it 'applies the options to the criteria client' do
200
+ expect(client.options['read']).to eq('mode' => :secondary)
201
+ end
191
202
  end
192
203
 
193
- let(:client) do
194
- context_and_criteria[1].client
204
+ context 'read: :secondary shorthand' do
205
+ let(:read_secondary_option) { {read: :secondary} }
206
+
207
+ it_behaves_like 'applies secondary read preference'
195
208
  end
196
209
 
197
- it 'applies the options to the criteria client' do
198
- expect(client.options['read']).to eq(:secondary)
210
+ context 'read: {mode: :secondary}' do
211
+ let(:read_secondary_option) { {read: {mode: :secondary}} }
212
+
213
+ it_behaves_like 'applies secondary read preference'
199
214
  end
200
215
  end
201
216
 
@@ -339,7 +354,7 @@ describe Mongoid::Clients::Options do
339
354
  band.mongo_client.database.command(serverStatus: 1).first['connections']['current']
340
355
  end
341
356
 
342
- let!(:connections_and_cluster_during) do
357
+ let(:connections_and_cluster_during) do
343
358
  connections = nil
344
359
  cluster = band.with(options) do |b|
345
360
  b.reload
@@ -381,7 +396,7 @@ describe Mongoid::Clients::Options do
381
396
  end
382
397
 
383
398
  it 'disconnects the new cluster when the block exits' do
384
- expect(connections_before).to eq(connections_after)
399
+ expect(connections_after).to eq(connections_before)
385
400
  end
386
401
  end
387
402
 
@@ -16,17 +16,26 @@ 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
+ client = Mongoid::Clients.with_name(:other)
20
+ monitoring = if client.respond_to?(:monitoring, true)
21
+ client.send(:monitoring)
22
+ else
23
+ # driver 2.5
24
+ client.instance_variable_get('@monitoring')
25
+ end
26
+ monitoring.subscribers['Command'].find do |s|
20
27
  s.is_a?(EventSubscriber)
21
28
  end
22
29
  end
23
30
 
24
31
  let(:insert_events) do
25
- subscriber.started_events.select { |event| event.command_name == :insert }
32
+ # Driver 2.5 sends command_name as a symbol
33
+ subscriber.started_events.select { |event| event.command_name.to_s == 'insert' }
26
34
  end
27
35
 
28
36
  let(:update_events) do
29
- subscriber.started_events.select { |event| event.command_name == :update }
37
+ # Driver 2.5 sends command_name as a symbol
38
+ subscriber.started_events.select { |event| event.command_name.to_s == 'update' }
30
39
  end
31
40
 
32
41
  context 'when a session is used on a model class' do
@@ -1,6 +1,7 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Mongoid::Contextual::GeoNear do
4
+ max_server_version '4.0'
4
5
 
5
6
  describe "#average_distance" do
6
7
 
@@ -451,7 +451,7 @@ describe Mongoid::Contextual::Mongo do
451
451
  end
452
452
 
453
453
  it "returns the distinct field values" do
454
- expect(context.distinct(:years)).to eq([ 30, 25 ])
454
+ expect(context.distinct(:years).sort).to eq([ 25, 30 ])
455
455
  end
456
456
  end
457
457
 
@@ -2363,7 +2363,7 @@ describe Mongoid::Contextual::Mongo do
2363
2363
  end
2364
2364
 
2365
2365
  it 'creates a pipeline with the selector as one of the $match criteria' do
2366
- expect(pipeline_match).to include({ :'$text' => { :'$search' => "New Order" } })
2366
+ expect(pipeline_match).to include({ '$text' => { '$search' => "New Order" } })
2367
2367
  end
2368
2368
 
2369
2369
  it 'creates a pipeline with the $exists operator as one of the $match criteria' do
@@ -1471,11 +1471,15 @@ describe Mongoid::Criteria::Modifiable do
1471
1471
  { 'username' => 'Turnip' }
1472
1472
  end
1473
1473
 
1474
- it 'returns a criteria with the defined attributes' do
1475
- expect(Person.create_with(attrs).selector).to eq(attrs)
1474
+ it 'does not modify the selector' do
1475
+ expect(Person.create_with(attrs).selector[:username]).to be_nil
1476
1476
  end
1477
1477
 
1478
- context 'when a method is chained' do
1478
+ it 'create_attrs is modified' do
1479
+ expect(Person.create_with(attrs).create_attrs).to eq(attrs)
1480
+ end
1481
+
1482
+ context 'when a create is chained' do
1479
1483
 
1480
1484
  context 'when a write method is chained' do
1481
1485
 
@@ -1499,6 +1503,25 @@ describe Mongoid::Criteria::Modifiable do
1499
1503
  expect(new_person.age).to eq(50)
1500
1504
  end
1501
1505
 
1506
+ context 'when a matching document is already in the collection' do
1507
+ let(:query) do
1508
+ { 'username' => 'foo', 'age' => 12 }
1509
+ end
1510
+
1511
+ let(:person) do
1512
+ Person.create!(query)
1513
+ end
1514
+
1515
+ let(:found_person) do
1516
+ Person.create_with(attrs).find_or_create_by(query)
1517
+ end
1518
+
1519
+ it 'finds the matching document' do
1520
+ person
1521
+ expect(found_person.id).to eq(person.id)
1522
+ end
1523
+ end
1524
+
1502
1525
  context 'when the attributes are shared with the write method args' do
1503
1526
 
1504
1527
  let(:query) do
@@ -1509,7 +1532,7 @@ describe Mongoid::Criteria::Modifiable do
1509
1532
  Person.create_with(attrs).find_or_create_by(query)
1510
1533
  end
1511
1534
 
1512
- it 'gives the write method args precedence' do
1535
+ it 'gives the find method args precedence' do
1513
1536
  expect(new_person.username).to eq('Beet')
1514
1537
  expect(new_person.age).to eq(50)
1515
1538
  end
@@ -1536,8 +1559,12 @@ describe Mongoid::Criteria::Modifiable do
1536
1559
  { 'username' => 'Beet', 'age' => 50 }
1537
1560
  end
1538
1561
 
1562
+ it 'does not modify the selector' do
1563
+ expect(criteria.create_with(attrs).selector).to eq(criteria_selector)
1564
+ end
1565
+
1539
1566
  it 'overwrites all the original attributes' do
1540
- expect(criteria.create_with(attrs).selector).to eq(attrs)
1567
+ expect(criteria.create_with(attrs).create_attrs).to eq(attrs)
1541
1568
  end
1542
1569
  end
1543
1570
  end
@@ -1548,8 +1575,12 @@ describe Mongoid::Criteria::Modifiable do
1548
1575
  { 'username' => 'Beet' }
1549
1576
  end
1550
1577
 
1578
+ it 'does not modify the selector' do
1579
+ expect(criteria.create_with(attrs).selector).to eq(criteria_selector)
1580
+ end
1581
+
1551
1582
  it 'only overwrites the shared attributes' do
1552
- expect(criteria.create_with(attrs).selector).to eq(criteria_selector.merge!(attrs))
1583
+ expect(criteria.create_with(attrs).create_attrs).to eq(attrs)
1553
1584
  end
1554
1585
  end
1555
1586
 
@@ -1558,12 +1589,11 @@ describe Mongoid::Criteria::Modifiable do
1558
1589
  let(:attrs) do
1559
1590
  { 'username' => 'Turnip' }
1560
1591
  end
1561
-
1562
1592
  let(:query) do
1563
1593
  { 'username' => 'Beet', 'age' => 50 }
1564
1594
  end
1565
1595
 
1566
- context 'when a write method is chained' do
1596
+ context 'when a create method is chained' do
1567
1597
 
1568
1598
  it 'executes the method' do
1569
1599
  expect(criteria.create_with(attrs).new.username).to eq('Turnip')
@@ -1577,9 +1607,28 @@ describe Mongoid::Criteria::Modifiable do
1577
1607
  criteria.create_with(attrs).find_or_create_by(query)
1578
1608
  end
1579
1609
 
1580
- it 'executes the query' do
1610
+ it 'gives the find method arg precedence' do
1581
1611
  expect(new_person.username).to eq('Beet')
1582
- expect(new_person.age).to eq(50)
1612
+ expect(new_person.age).to be(50)
1613
+ end
1614
+
1615
+ context 'when a matching document is already in the collection' do
1616
+ let(:query) do
1617
+ { 'username' => 'foo', 'age' => 12 }
1618
+ end
1619
+
1620
+ let(:person) do
1621
+ Person.create!(query)
1622
+ end
1623
+
1624
+ let(:found_person) do
1625
+ criteria.create_with(attrs).find_or_create_by(query)
1626
+ end
1627
+
1628
+ it 'finds the matching document' do
1629
+ person
1630
+ expect(found_person.id).to eq(person.id)
1631
+ end
1583
1632
  end
1584
1633
  end
1585
1634
  end