mongoid 9.1.1 → 9.1.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/lib/config/locales/en.yml +31 -11
  3. data/lib/mongoid/association/accessors.rb +18 -4
  4. data/lib/mongoid/association/eager_load/discriminated_inclusion.rb +10 -10
  5. data/lib/mongoid/association/eager_load/embedded_distributor.rb +6 -6
  6. data/lib/mongoid/association/eager_load/lookup_pipeline.rb +1 -1
  7. data/lib/mongoid/association/eager_loadable.rb +8 -1
  8. data/lib/mongoid/association/embedded/batchable.rb +17 -1
  9. data/lib/mongoid/association/nested/many.rb +1 -1
  10. data/lib/mongoid/association/referenced/has_and_belongs_to_many/buildable.rb +6 -0
  11. data/lib/mongoid/association/relatable.rb +18 -0
  12. data/lib/mongoid/association.rb +8 -0
  13. data/lib/mongoid/atomic/modifiers.rb +100 -16
  14. data/lib/mongoid/clients/factory.rb +2 -0
  15. data/lib/mongoid/config/encryption.rb +148 -17
  16. data/lib/mongoid/document.rb +3 -0
  17. data/lib/mongoid/encryptable.rb +42 -15
  18. data/lib/mongoid/errors/invalid_encryption_schema.rb +27 -0
  19. data/lib/mongoid/errors/no_encryption_schema.rb +5 -3
  20. data/lib/mongoid/errors.rb +1 -0
  21. data/lib/mongoid/fields.rb +7 -0
  22. data/lib/mongoid/persistable/creatable.rb +120 -14
  23. data/lib/mongoid/persistable.rb +15 -15
  24. data/lib/mongoid/persistence_context.rb +46 -4
  25. data/lib/mongoid/search_indexable.rb +5 -1
  26. data/lib/mongoid/tasks/database.rb +10 -7
  27. data/lib/mongoid/touchable.rb +28 -1
  28. data/lib/mongoid/version.rb +1 -1
  29. data/lib/mongoid/warnings.rb +4 -0
  30. data/spec/integration/associations/embedded_delayed_atomic_sets_spec.rb +216 -0
  31. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +14 -0
  32. data/spec/integration/atomic/modifiers_spec.rb +283 -0
  33. data/spec/integration/encryption_spec.rb +149 -0
  34. data/spec/mongoid/association/accessors_spec.rb +35 -0
  35. data/spec/mongoid/association/referenced/has_and_belongs_to_many/buildable_spec.rb +11 -0
  36. data/spec/mongoid/atomic/modifiers_spec.rb +347 -0
  37. data/spec/mongoid/attributes/nested_spec.rb +29 -0
  38. data/spec/mongoid/clients/factory_spec.rb +36 -0
  39. data/spec/mongoid/config/encryption_spec.rb +223 -0
  40. data/spec/mongoid/criteria/includable_spec.rb +346 -0
  41. data/spec/mongoid/encryptable_spec.rb +54 -0
  42. data/spec/mongoid/fields_spec.rb +8 -0
  43. data/spec/mongoid/search_indexable_spec.rb +26 -0
  44. data/spec/mongoid/tasks/database_spec.rb +13 -0
  45. data/spec/mongoid/touchable_spec.rb +217 -0
  46. data/spec/mongoid/touchable_spec_models.rb +37 -0
  47. data/spec/support/crypt/models.rb +96 -0
  48. data/spec/support/models/modifier_models.rb +65 -0
  49. metadata +5 -2
@@ -126,8 +126,11 @@ describe Mongoid::Criteria::Includable do
126
126
  end
127
127
 
128
128
  after(:all) do
129
+ Mongoid.deregister_model(A)
129
130
  Object.send(:remove_const, :A)
131
+ Mongoid.deregister_model(B)
130
132
  Object.send(:remove_const, :B)
133
+ Mongoid.deregister_model(C)
131
134
  Object.send(:remove_const, :C)
132
135
  end
133
136
 
@@ -186,9 +189,13 @@ describe Mongoid::Criteria::Includable do
186
189
  end
187
190
 
188
191
  after(:all) do
192
+ Mongoid.deregister_model(A)
189
193
  Object.send(:remove_const, :A)
194
+ Mongoid.deregister_model(B)
190
195
  Object.send(:remove_const, :B)
196
+ Mongoid.deregister_model(C)
191
197
  Object.send(:remove_const, :C)
198
+ Mongoid.deregister_model(D)
192
199
  Object.send(:remove_const, :D)
193
200
  end
194
201
 
@@ -255,9 +262,13 @@ describe Mongoid::Criteria::Includable do
255
262
  end
256
263
 
257
264
  after(:all) do
265
+ Mongoid.deregister_model(A)
258
266
  Object.send(:remove_const, :A)
267
+ Mongoid.deregister_model(B)
259
268
  Object.send(:remove_const, :B)
269
+ Mongoid.deregister_model(C)
260
270
  Object.send(:remove_const, :C)
271
+ Mongoid.deregister_model(D)
261
272
  Object.send(:remove_const, :D)
262
273
  end
263
274
 
@@ -1121,9 +1132,13 @@ describe Mongoid::Criteria::Includable do
1121
1132
  end
1122
1133
 
1123
1134
  after(:all) do
1135
+ Mongoid.deregister_model(A)
1124
1136
  Object.send(:remove_const, :A)
1137
+ Mongoid.deregister_model(B)
1125
1138
  Object.send(:remove_const, :B)
1139
+ Mongoid.deregister_model(C)
1126
1140
  Object.send(:remove_const, :C)
1141
+ Mongoid.deregister_model(D)
1127
1142
  Object.send(:remove_const, :D)
1128
1143
  end
1129
1144
 
@@ -1408,6 +1423,106 @@ describe Mongoid::Criteria::Includable do
1408
1423
 
1409
1424
  describe '#includes' do
1410
1425
  it_behaves_like 'eager loading', :includes
1426
+
1427
+ context 'when a nested association sits behind a parent whose class_name is not the resolved class name' do
1428
+ before(:all) do
1429
+ class IncEagerGallery
1430
+ include Mongoid::Document
1431
+
1432
+ has_many :rooted_photos, class_name: '::IncEagerPhoto', inverse_of: :gallery
1433
+ end
1434
+
1435
+ class IncEagerPhoto
1436
+ include Mongoid::Document
1437
+
1438
+ belongs_to :gallery, class_name: 'IncEagerGallery', inverse_of: :rooted_photos
1439
+ belongs_to :author, class_name: '::IncEagerAuthor'
1440
+ end
1441
+
1442
+ class IncEagerAuthor
1443
+ include Mongoid::Document
1444
+ end
1445
+
1446
+ module IncEagerShop
1447
+ class Order
1448
+ include Mongoid::Document
1449
+
1450
+ has_many :items, class_name: 'Item', inverse_of: :order
1451
+ end
1452
+
1453
+ class Item
1454
+ include Mongoid::Document
1455
+
1456
+ belongs_to :order, class_name: 'Order', inverse_of: :items
1457
+ belongs_to :author, class_name: '::IncEagerAuthor'
1458
+ end
1459
+ end
1460
+
1461
+ class IncEagerAlbum
1462
+ include Mongoid::Document
1463
+
1464
+ has_many :poly_photos, class_name: '::IncEagerPolyPhoto', as: :imageable, inverse_of: :imageable
1465
+ end
1466
+
1467
+ class IncEagerPolyPhoto
1468
+ include Mongoid::Document
1469
+
1470
+ belongs_to :imageable, polymorphic: true, inverse_of: :poly_photos
1471
+ belongs_to :author, class_name: '::IncEagerAuthor'
1472
+ end
1473
+ end
1474
+
1475
+ after(:all) do
1476
+ Mongoid.deregister_model(IncEagerAlbum)
1477
+ Object.send(:remove_const, :IncEagerAlbum)
1478
+ Mongoid.deregister_model(IncEagerPolyPhoto)
1479
+ Object.send(:remove_const, :IncEagerPolyPhoto)
1480
+ Mongoid.deregister_model(IncEagerShop::Order)
1481
+ Mongoid.deregister_model(IncEagerShop::Item)
1482
+ Object.send(:remove_const, :IncEagerShop)
1483
+ Mongoid.deregister_model(IncEagerAuthor)
1484
+ Object.send(:remove_const, :IncEagerAuthor)
1485
+ Mongoid.deregister_model(IncEagerPhoto)
1486
+ Object.send(:remove_const, :IncEagerPhoto)
1487
+ Mongoid.deregister_model(IncEagerGallery)
1488
+ Object.send(:remove_const, :IncEagerGallery)
1489
+ end
1490
+
1491
+ let!(:gallery) { IncEagerGallery.create! }
1492
+ let!(:order) { IncEagerShop::Order.create! }
1493
+
1494
+ before do
1495
+ IncEagerPhoto.create!(gallery: gallery, author: IncEagerAuthor.create!)
1496
+ IncEagerShop::Item.create!(order: order, author: IncEagerAuthor.create!)
1497
+ end
1498
+
1499
+ it 'eager loads a nested association behind a class_name with a leading ::' do
1500
+ result = IncEagerGallery.includes(rooted_photos: :author).first
1501
+
1502
+ expect_query(0) do
1503
+ expect(result.rooted_photos.first.author).to be_a(IncEagerAuthor)
1504
+ end
1505
+ end
1506
+
1507
+ it 'eager loads a nested association behind a module-relative class_name' do
1508
+ result = IncEagerShop::Order.includes(items: :author).first
1509
+
1510
+ expect_query(0) do
1511
+ expect(result.items.first.author).to be_a(IncEagerAuthor)
1512
+ end
1513
+ end
1514
+
1515
+ it 'eager loads a nested association behind a polymorphic parent with a leading ::' do
1516
+ album = IncEagerAlbum.create!
1517
+ IncEagerPolyPhoto.create!(imageable: album, author: IncEagerAuthor.create!)
1518
+
1519
+ result = IncEagerAlbum.includes(poly_photos: :author).first
1520
+
1521
+ expect_query(0) do
1522
+ expect(result.poly_photos.first.author).to be_a(IncEagerAuthor)
1523
+ end
1524
+ end
1525
+ end
1411
1526
  end
1412
1527
 
1413
1528
  describe '#eager_load' do
@@ -1442,9 +1557,13 @@ describe Mongoid::Criteria::Includable do
1442
1557
  end
1443
1558
 
1444
1559
  after(:all) do
1560
+ Mongoid.deregister_model(Mouse)
1445
1561
  Object.send(:remove_const, :Mouse)
1562
+ Mongoid.deregister_model(Webcam)
1446
1563
  Object.send(:remove_const, :Webcam)
1564
+ Mongoid.deregister_model(Peripheral)
1447
1565
  Object.send(:remove_const, :Peripheral)
1566
+ Mongoid.deregister_model(Machine)
1448
1567
  Object.send(:remove_const, :Machine)
1449
1568
  end
1450
1569
 
@@ -1484,10 +1603,15 @@ describe Mongoid::Criteria::Includable do
1484
1603
  end
1485
1604
 
1486
1605
  after(:all) do
1606
+ Mongoid.deregister_model(Mouse)
1487
1607
  Object.send(:remove_const, :Mouse)
1608
+ Mongoid.deregister_model(Peripheral)
1488
1609
  Object.send(:remove_const, :Peripheral)
1610
+ Mongoid.deregister_model(Laptop)
1489
1611
  Object.send(:remove_const, :Laptop)
1612
+ Mongoid.deregister_model(Machine)
1490
1613
  Object.send(:remove_const, :Machine)
1614
+ Mongoid.deregister_model(Brand)
1491
1615
  Object.send(:remove_const, :Brand)
1492
1616
  end
1493
1617
 
@@ -1522,8 +1646,11 @@ describe Mongoid::Criteria::Includable do
1522
1646
  end
1523
1647
 
1524
1648
  after(:all) do
1649
+ Mongoid.deregister_model(Cable)
1525
1650
  Object.send(:remove_const, :Cable)
1651
+ Mongoid.deregister_model(Speaker)
1526
1652
  Object.send(:remove_const, :Speaker)
1653
+ Mongoid.deregister_model(Gadget)
1527
1654
  Object.send(:remove_const, :Gadget)
1528
1655
  end
1529
1656
 
@@ -1556,8 +1683,11 @@ describe Mongoid::Criteria::Includable do
1556
1683
  end
1557
1684
 
1558
1685
  after(:all) do
1686
+ Mongoid.deregister_model(Cable)
1559
1687
  Object.send(:remove_const, :Cable)
1688
+ Mongoid.deregister_model(Speaker)
1560
1689
  Object.send(:remove_const, :Speaker)
1690
+ Mongoid.deregister_model(Gadget)
1561
1691
  Object.send(:remove_const, :Gadget)
1562
1692
  end
1563
1693
 
@@ -1590,8 +1720,11 @@ describe Mongoid::Criteria::Includable do
1590
1720
  end
1591
1721
 
1592
1722
  after(:all) do
1723
+ Mongoid.deregister_model(Speaker)
1593
1724
  Object.send(:remove_const, :Speaker)
1725
+ Mongoid.deregister_model(Gadget)
1594
1726
  Object.send(:remove_const, :Gadget)
1727
+ Mongoid.deregister_model(Cable)
1595
1728
  Object.send(:remove_const, :Cable)
1596
1729
  end
1597
1730
 
@@ -1627,8 +1760,11 @@ describe Mongoid::Criteria::Includable do
1627
1760
  end
1628
1761
 
1629
1762
  after(:all) do
1763
+ Mongoid.deregister_model(Computer)
1630
1764
  Object.send(:remove_const, :Computer)
1765
+ Mongoid.deregister_model(Port)
1631
1766
  Object.send(:remove_const, :Port)
1767
+ Mongoid.deregister_model(Device)
1632
1768
  Object.send(:remove_const, :Device)
1633
1769
  end
1634
1770
 
@@ -1675,8 +1811,11 @@ describe Mongoid::Criteria::Includable do
1675
1811
  end
1676
1812
 
1677
1813
  after(:all) do
1814
+ Mongoid.deregister_model(Computer)
1678
1815
  Object.send(:remove_const, :Computer)
1816
+ Mongoid.deregister_model(Port)
1679
1817
  Object.send(:remove_const, :Port)
1818
+ Mongoid.deregister_model(Device)
1680
1819
  Object.send(:remove_const, :Device)
1681
1820
  end
1682
1821
 
@@ -1726,9 +1865,13 @@ describe Mongoid::Criteria::Includable do
1726
1865
  end
1727
1866
 
1728
1867
  after(:all) do
1868
+ Mongoid.deregister_model(Computer)
1729
1869
  Object.send(:remove_const, :Computer)
1870
+ Mongoid.deregister_model(Panel)
1730
1871
  Object.send(:remove_const, :Panel)
1872
+ Mongoid.deregister_model(Port)
1731
1873
  Object.send(:remove_const, :Port)
1874
+ Mongoid.deregister_model(Device)
1732
1875
  Object.send(:remove_const, :Device)
1733
1876
  end
1734
1877
 
@@ -1767,9 +1910,13 @@ describe Mongoid::Criteria::Includable do
1767
1910
  end
1768
1911
 
1769
1912
  after(:all) do
1913
+ Mongoid.deregister_model(Computer)
1770
1914
  Object.send(:remove_const, :Computer)
1915
+ Mongoid.deregister_model(UsbPort)
1771
1916
  Object.send(:remove_const, :UsbPort)
1917
+ Mongoid.deregister_model(Port)
1772
1918
  Object.send(:remove_const, :Port)
1919
+ Mongoid.deregister_model(Device)
1773
1920
  Object.send(:remove_const, :Device)
1774
1921
  end
1775
1922
 
@@ -1805,8 +1952,11 @@ describe Mongoid::Criteria::Includable do
1805
1952
  end
1806
1953
 
1807
1954
  after(:all) do
1955
+ Mongoid.deregister_model(Computer)
1808
1956
  Object.send(:remove_const, :Computer)
1957
+ Mongoid.deregister_model(Port)
1809
1958
  Object.send(:remove_const, :Port)
1959
+ Mongoid.deregister_model(Device)
1810
1960
  Object.send(:remove_const, :Device)
1811
1961
  end
1812
1962
 
@@ -1843,8 +1993,11 @@ describe Mongoid::Criteria::Includable do
1843
1993
  end
1844
1994
 
1845
1995
  after(:all) do
1996
+ Mongoid.deregister_model(Computer)
1846
1997
  Object.send(:remove_const, :Computer)
1998
+ Mongoid.deregister_model(Port)
1847
1999
  Object.send(:remove_const, :Port)
2000
+ Mongoid.deregister_model(Device)
1848
2001
  Object.send(:remove_const, :Device)
1849
2002
  end
1850
2003
 
@@ -1879,8 +2032,11 @@ describe Mongoid::Criteria::Includable do
1879
2032
  end
1880
2033
 
1881
2034
  after(:all) do
2035
+ Mongoid.deregister_model(Port)
1882
2036
  Object.send(:remove_const, :Port)
2037
+ Mongoid.deregister_model(Computer)
1883
2038
  Object.send(:remove_const, :Computer)
2039
+ Mongoid.deregister_model(Device)
1884
2040
  Object.send(:remove_const, :Device)
1885
2041
  end
1886
2042
 
@@ -1922,9 +2078,13 @@ describe Mongoid::Criteria::Includable do
1922
2078
  end
1923
2079
 
1924
2080
  after(:all) do
2081
+ Mongoid.deregister_model(Desk)
1925
2082
  Object.send(:remove_const, :Desk)
2083
+ Mongoid.deregister_model(Computer)
1926
2084
  Object.send(:remove_const, :Computer)
2085
+ Mongoid.deregister_model(Port)
1927
2086
  Object.send(:remove_const, :Port)
2087
+ Mongoid.deregister_model(Device)
1928
2088
  Object.send(:remove_const, :Device)
1929
2089
  end
1930
2090
 
@@ -1974,10 +2134,15 @@ describe Mongoid::Criteria::Includable do
1974
2134
  end
1975
2135
 
1976
2136
  after(:all) do
2137
+ Mongoid.deregister_model(Desk)
1977
2138
  Object.send(:remove_const, :Desk)
2139
+ Mongoid.deregister_model(Computer)
1978
2140
  Object.send(:remove_const, :Computer)
2141
+ Mongoid.deregister_model(Port)
1979
2142
  Object.send(:remove_const, :Port)
2143
+ Mongoid.deregister_model(Pin)
1980
2144
  Object.send(:remove_const, :Pin)
2145
+ Mongoid.deregister_model(Device)
1981
2146
  Object.send(:remove_const, :Device)
1982
2147
  end
1983
2148
 
@@ -2027,10 +2192,15 @@ describe Mongoid::Criteria::Includable do
2027
2192
  end
2028
2193
 
2029
2194
  after(:all) do
2195
+ Mongoid.deregister_model(Desk)
2030
2196
  Object.send(:remove_const, :Desk)
2197
+ Mongoid.deregister_model(Computer)
2031
2198
  Object.send(:remove_const, :Computer)
2199
+ Mongoid.deregister_model(Card)
2032
2200
  Object.send(:remove_const, :Card)
2201
+ Mongoid.deregister_model(Port)
2033
2202
  Object.send(:remove_const, :Port)
2203
+ Mongoid.deregister_model(Device)
2034
2204
  Object.send(:remove_const, :Device)
2035
2205
  end
2036
2206
 
@@ -2072,8 +2242,11 @@ describe Mongoid::Criteria::Includable do
2072
2242
  end
2073
2243
 
2074
2244
  after(:all) do
2245
+ Mongoid.deregister_model(Cartridge)
2075
2246
  Object.send(:remove_const, :Cartridge)
2247
+ Mongoid.deregister_model(Scanner)
2076
2248
  Object.send(:remove_const, :Scanner)
2249
+ Mongoid.deregister_model(Printer)
2077
2250
  Object.send(:remove_const, :Printer)
2078
2251
  end
2079
2252
 
@@ -2111,8 +2284,11 @@ describe Mongoid::Criteria::Includable do
2111
2284
  end
2112
2285
 
2113
2286
  after(:all) do
2287
+ Mongoid.deregister_model(Accessory)
2114
2288
  Object.send(:remove_const, :Accessory)
2289
+ Mongoid.deregister_model(RemoteDevice)
2115
2290
  Object.send(:remove_const, :RemoteDevice)
2291
+ Mongoid.deregister_model(LocalDevice)
2116
2292
  Object.send(:remove_const, :LocalDevice)
2117
2293
  end
2118
2294
 
@@ -2155,7 +2331,9 @@ describe Mongoid::Criteria::Includable do
2155
2331
  end
2156
2332
 
2157
2333
  after(:all) do
2334
+ Mongoid.deregister_model(Workstation)
2158
2335
  Object.send(:remove_const, :Workstation)
2336
+ Mongoid.deregister_model(RemoteServer)
2159
2337
  Object.send(:remove_const, :RemoteServer)
2160
2338
  end
2161
2339
 
@@ -2214,11 +2392,17 @@ describe Mongoid::Criteria::Includable do
2214
2392
  end
2215
2393
 
2216
2394
  after(:all) do
2395
+ Mongoid.deregister_model(Press)
2217
2396
  Object.send(:remove_const, :Press)
2397
+ Mongoid.deregister_model(Lathe)
2218
2398
  Object.send(:remove_const, :Lathe)
2399
+ Mongoid.deregister_model(Machine)
2219
2400
  Object.send(:remove_const, :Machine)
2401
+ Mongoid.deregister_model(Belt)
2220
2402
  Object.send(:remove_const, :Belt)
2403
+ Mongoid.deregister_model(Cog)
2221
2404
  Object.send(:remove_const, :Cog)
2405
+ Mongoid.deregister_model(Supplier)
2222
2406
  Object.send(:remove_const, :Supplier)
2223
2407
  end
2224
2408
 
@@ -2272,9 +2456,13 @@ describe Mongoid::Criteria::Includable do
2272
2456
  end
2273
2457
 
2274
2458
  after(:all) do
2459
+ Mongoid.deregister_model(Board)
2275
2460
  Object.send(:remove_const, :Board)
2461
+ Mongoid.deregister_model(Cable)
2276
2462
  Object.send(:remove_const, :Cable)
2463
+ Mongoid.deregister_model(Chip)
2277
2464
  Object.send(:remove_const, :Chip)
2465
+ Mongoid.deregister_model(Part)
2278
2466
  Object.send(:remove_const, :Part)
2279
2467
  end
2280
2468
 
@@ -2289,6 +2477,164 @@ describe Mongoid::Criteria::Includable do
2289
2477
  end
2290
2478
  end
2291
2479
  end
2480
+
2481
+ context 'when a document is saved after eager loading' do
2482
+ context 'when the reference lives inside an embedded document' do
2483
+ before(:all) do
2484
+ class Device
2485
+ include Mongoid::Document
2486
+ end
2487
+
2488
+ class Port
2489
+ include Mongoid::Document
2490
+
2491
+ embedded_in :computer
2492
+ belongs_to :device
2493
+ end
2494
+
2495
+ class Computer
2496
+ include Mongoid::Document
2497
+
2498
+ embeds_many :ports
2499
+ end
2500
+ end
2501
+
2502
+ after(:all) do
2503
+ Object.send(:remove_const, :Computer)
2504
+ Object.send(:remove_const, :Port)
2505
+ Object.send(:remove_const, :Device)
2506
+ end
2507
+
2508
+ let!(:device) { Device.create! }
2509
+ let!(:computer) { Computer.create!(ports: [ Port.new(device: device) ]) }
2510
+
2511
+ it 'does not persist the eager-loaded association data' do
2512
+ loaded = Computer.eager_load(ports: :device).first
2513
+
2514
+ # Reassigning the collection makes the save rewrite the whole array,
2515
+ # serializing each already-hydrated port.
2516
+ loaded.ports = loaded.ports.to_a + [ Port.new(device: Device.create!) ]
2517
+ loaded.save!
2518
+
2519
+ # With the bug present, the persisted port comes back as:
2520
+ #
2521
+ # {
2522
+ # '_id' => ...,
2523
+ # 'device_id' => ...,
2524
+ # 'device' => [ { ...the whole device document... } ] # <-- the $lookup left this
2525
+ # }
2526
+ raw_ports = Computer.collection.find(_id: computer.id).first['ports']
2527
+ expect(raw_ports.first).to eq('_id' => computer.ports.first.id, 'device_id' => device.id)
2528
+ end
2529
+ end
2530
+
2531
+ context 'when the reference lives on a referenced document' do
2532
+ before(:all) do
2533
+ class Device
2534
+ include Mongoid::Document
2535
+ end
2536
+
2537
+ class Port
2538
+ include Mongoid::Document
2539
+
2540
+ belongs_to :computer
2541
+ belongs_to :device
2542
+ end
2543
+
2544
+ class Computer
2545
+ include Mongoid::Document
2546
+
2547
+ has_many :ports
2548
+ end
2549
+ end
2550
+
2551
+ after(:all) do
2552
+ Object.send(:remove_const, :Computer)
2553
+ Object.send(:remove_const, :Port)
2554
+ Object.send(:remove_const, :Device)
2555
+ end
2556
+
2557
+ let!(:device) { Device.create! }
2558
+ let!(:computer) { Computer.create!(ports: [ Port.new(device: device) ]) }
2559
+
2560
+ it 'does not persist the eager-loaded association data' do
2561
+ loaded = Computer.eager_load(ports: :device).first
2562
+ port = loaded.ports.first
2563
+
2564
+ # An upsert rewrites the whole document, so everything the port
2565
+ # carries gets serialized.
2566
+ port.upsert
2567
+
2568
+ # With the bug present, the persisted port comes back as:
2569
+ #
2570
+ # {
2571
+ # '_id' => ...,
2572
+ # 'computer_id' => ...,
2573
+ # 'device_id' => ...,
2574
+ # 'device' => [ { ...the whole device document... } ] # <-- the $lookup left this
2575
+ # }
2576
+ expect(Port.collection.find(_id: port.id).first)
2577
+ .to eq('_id' => port.id, 'computer_id' => computer.id, 'device_id' => device.id)
2578
+ end
2579
+ end
2580
+
2581
+ context 'when more than one subclass defines the association' do
2582
+ before(:all) do
2583
+ class Cog
2584
+ include Mongoid::Document
2585
+
2586
+ belongs_to :machine, optional: true
2587
+ end
2588
+
2589
+ class Belt
2590
+ include Mongoid::Document
2591
+
2592
+ belongs_to :machine, optional: true
2593
+ end
2594
+
2595
+ class Machine
2596
+ include Mongoid::Document
2597
+ end
2598
+
2599
+ class Lathe < Machine
2600
+ has_many :widgets, class_name: 'Cog', inverse_of: :machine
2601
+ end
2602
+
2603
+ class Press < Machine
2604
+ has_many :widgets, class_name: 'Belt', inverse_of: :machine
2605
+ end
2606
+ end
2607
+
2608
+ after(:all) do
2609
+ Object.send(:remove_const, :Press)
2610
+ Object.send(:remove_const, :Lathe)
2611
+ Object.send(:remove_const, :Machine)
2612
+ Object.send(:remove_const, :Belt)
2613
+ Object.send(:remove_const, :Cog)
2614
+ end
2615
+
2616
+ let!(:lathe) { Lathe.create! }
2617
+ let!(:cog) { Cog.create!(machine: lathe) }
2618
+
2619
+ it 'does not persist the eager-loaded association data' do
2620
+ loaded = Machine.eager_load(:widgets).first
2621
+
2622
+ # An upsert rewrites the whole document, so everything the lathe
2623
+ # carries gets serialized.
2624
+ loaded.upsert
2625
+
2626
+ # With the bug present, the persisted lathe comes back as:
2627
+ #
2628
+ # {
2629
+ # '_id' => ...,
2630
+ # '_type' => 'Lathe',
2631
+ # 'widgets' => [ { '_id' => ..., 'machine_id' => ... } ] # <-- the $lookup left this
2632
+ # }
2633
+ expect(Machine.collection.find(_id: lathe.id).first)
2634
+ .to eq('_id' => lathe.id, '_type' => 'Lathe')
2635
+ end
2636
+ end
2637
+ end
2292
2638
  end
2293
2639
 
2294
2640
  describe '#inclusions' do
@@ -29,6 +29,14 @@ describe Mongoid::Encryptable do
29
29
  end
30
30
  end
31
31
 
32
+ # Any subclass of the declared class can be assigned to an embeds_one
33
+ # association, so the check has to look at the subclasses too.
34
+ context 'when only a subclass of an embedded model is encrypted' do
35
+ it 'returns true' do
36
+ expect(Crypt::Locker.requires_encryption_schema?).to be true
37
+ end
38
+ end
39
+
32
40
  context 'when the model declares no encryption anywhere' do
33
41
  it 'returns false' do
34
42
  expect(Person.requires_encryption_schema?).to be false
@@ -57,5 +65,51 @@ describe Mongoid::Encryptable do
57
65
  expect(Crypt::Drawer.requires_encryption_schema?).to be false
58
66
  end
59
67
  end
68
+
69
+ context 'when every embedded association resolves' do
70
+ it 'memoizes a false answer' do
71
+ expect(Truck.requires_encryption_schema?).to be false
72
+ expect(Truck.instance_variable_defined?(:@requires_encryption_schema)).to be true
73
+ end
74
+ end
75
+
76
+ context 'when an embedded association does not resolve' do
77
+ it 'does not memoize a false answer' do
78
+ expect(Crypt::Drawer.requires_encryption_schema?).to be false
79
+ expect(Crypt::Drawer.instance_variable_defined?(:@requires_encryption_schema)).to be false
80
+ end
81
+ end
82
+
83
+ context 'when an embedded encrypted class is defined after the first check' do
84
+ after do
85
+ if Crypt.const_defined?(:MissingNote, false)
86
+ Mongoid.deregister_model(Crypt::MissingNote)
87
+ Crypt.send(:remove_const, :MissingNote)
88
+ end
89
+ # While the class above existed, resolving it through the association
90
+ # memoized the class on the association, and a true answer on the
91
+ # model. Drop both, so the other examples see the model as it was
92
+ # before the class was defined.
93
+ relation = Crypt::Drawer.relations['missing_note']
94
+ relation.remove_instance_variable(:@klass) if relation.instance_variable_defined?(:@klass)
95
+ if Crypt::Drawer.instance_variable_defined?(:@requires_encryption_schema)
96
+ Crypt::Drawer.remove_instance_variable(:@requires_encryption_schema)
97
+ end
98
+ end
99
+
100
+ it 'returns true once the class is loaded' do
101
+ expect(Crypt::Drawer.requires_encryption_schema?).to be false
102
+
103
+ missing_note = Class.new do
104
+ include Mongoid::Document
105
+
106
+ embedded_in :drawer, class_name: 'Crypt::Drawer'
107
+ field :text, type: String, encrypt: true
108
+ end
109
+ Crypt.const_set(:MissingNote, missing_note)
110
+
111
+ expect(Crypt::Drawer.requires_encryption_schema?).to be true
112
+ end
113
+ end
60
114
  end
61
115
  end
@@ -1814,6 +1814,14 @@ describe Mongoid::Fields do
1814
1814
  it { is_expected.to eq '' }
1815
1815
  end
1816
1816
 
1817
+ context 'when the returned name is mutated' do
1818
+ it 'does not corrupt the aliased field map' do
1819
+ Person.database_field_name('test') << '_mutated'
1820
+
1821
+ expect(Person.database_field_name('test')).to eq('t')
1822
+ end
1823
+ end
1824
+
1817
1825
  context 'given a String' do
1818
1826
  subject { Person.database_field_name(key.to_s) }
1819
1827
 
@@ -118,6 +118,32 @@ describe Mongoid::SearchIndexable do
118
118
  end
119
119
  end
120
120
 
121
+ describe '.wait_for_search_indexes' do
122
+ let(:model) do
123
+ Class.new do
124
+ include Mongoid::Document
125
+
126
+ store_in collection: BSON::ObjectId.new.to_s
127
+ search_index :idx, mappings: { dynamic: false }
128
+ end
129
+ end
130
+
131
+ it 'reads the index status inside collection management' do
132
+ managing = nil
133
+ search_indexes = [ { 'name' => 'idx', 'queryable' => true, 'status' => 'READY' } ]
134
+ collection = instance_double(Mongo::Collection)
135
+ allow(collection).to receive(:search_indexes) do
136
+ managing = Mongoid::Threaded.managing_collection?
137
+ search_indexes
138
+ end
139
+ allow(model).to receive(:collection).and_return(collection)
140
+
141
+ model.wait_for_search_indexes([ 'idx' ])
142
+
143
+ expect(managing).to be true
144
+ end
145
+ end
146
+
121
147
  describe '.vector_search_index' do
122
148
  let(:vector_model) do
123
149
  Class.new do