mongoid 7.4.3 → 7.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/config/locales/en.yml +7 -0
  4. data/lib/mongoid/association/embedded/batchable.rb +3 -20
  5. data/lib/mongoid/association/macros.rb +20 -0
  6. data/lib/mongoid/association/referenced/has_many/enumerable.rb +12 -8
  7. data/lib/mongoid/association/referenced/has_many/proxy.rb +2 -2
  8. data/lib/mongoid/atomic/paths/embedded/many.rb +0 -19
  9. data/lib/mongoid/config.rb +6 -1
  10. data/lib/mongoid/contextual/memory.rb +144 -12
  11. data/lib/mongoid/contextual/mongo.rb +118 -26
  12. data/lib/mongoid/contextual/none.rb +45 -1
  13. data/lib/mongoid/criteria/queryable/extensions/array.rb +2 -0
  14. data/lib/mongoid/criteria/queryable/extensions/hash.rb +2 -0
  15. data/lib/mongoid/criteria/queryable/mergeable.rb +21 -0
  16. data/lib/mongoid/criteria/queryable/selectable.rb +26 -10
  17. data/lib/mongoid/criteria.rb +2 -0
  18. data/lib/mongoid/document.rb +2 -0
  19. data/lib/mongoid/equality.rb +4 -4
  20. data/lib/mongoid/errors/document_not_found.rb +23 -6
  21. data/lib/mongoid/fields.rb +145 -21
  22. data/lib/mongoid/findable.rb +20 -5
  23. data/lib/mongoid/version.rb +1 -1
  24. data/lib/mongoid/warnings.rb +29 -0
  25. data/lib/mongoid.rb +1 -0
  26. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +4 -3
  27. data/spec/integration/i18n_fallbacks_spec.rb +15 -1
  28. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +0 -21
  29. data/spec/mongoid/association/embedded/embeds_many_models.rb +0 -121
  30. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +0 -8
  31. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +54 -0
  32. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +8 -24
  33. data/spec/mongoid/clients/options_spec.rb +1 -0
  34. data/spec/mongoid/config_spec.rb +10 -4
  35. data/spec/mongoid/contextual/memory_spec.rb +826 -65
  36. data/spec/mongoid/contextual/mongo_spec.rb +781 -18
  37. data/spec/mongoid/contextual/none_spec.rb +46 -0
  38. data/spec/mongoid/criteria/queryable/selectable_spec.rb +212 -39
  39. data/spec/mongoid/criteria_spec.rb +8 -0
  40. data/spec/mongoid/equality_spec.rb +12 -12
  41. data/spec/mongoid/errors/document_not_found_spec.rb +49 -0
  42. data/spec/mongoid/findable_spec.rb +30 -0
  43. data/spec/support/models/code.rb +2 -0
  44. data.tar.gz.sig +0 -0
  45. metadata +3 -2
  46. metadata.gz.sig +0 -0
@@ -379,36 +379,77 @@ describe Mongoid::Contextual::Memory do
379
379
 
380
380
  describe "#distinct" do
381
381
 
382
- let(:hobrecht) do
383
- Address.new(street: "hobrecht")
384
- end
382
+ context "when legacy_pluck_distinct is true" do
383
+ config_override :legacy_pluck_distinct, true
385
384
 
386
- let(:friedel) do
387
- Address.new(street: "friedel")
388
- end
385
+ let(:hobrecht) do
386
+ Address.new(street: "hobrecht")
387
+ end
389
388
 
390
- context "when limiting the result set" do
389
+ let(:friedel) do
390
+ Address.new(street: "friedel")
391
+ end
391
392
 
392
- let(:criteria) do
393
- Address.where(street: "hobrecht").tap do |crit|
394
- crit.documents = [ hobrecht, hobrecht, friedel ]
393
+ context "when limiting the result set" do
394
+
395
+ let(:criteria) do
396
+ Address.where(street: "hobrecht").tap do |crit|
397
+ crit.documents = [ hobrecht, hobrecht, friedel ]
398
+ end
399
+ end
400
+
401
+ let(:context) do
402
+ described_class.new(criteria)
403
+ end
404
+
405
+ it "returns the distinct field values" do
406
+ expect(context.distinct(:street)).to eq([ "hobrecht" ])
395
407
  end
396
408
  end
397
409
 
398
- let(:context) do
399
- described_class.new(criteria)
410
+ context "when not limiting the result set" do
411
+
412
+ let(:criteria) do
413
+ Address.all.tap do |crit|
414
+ crit.documents = [ hobrecht, friedel, friedel ]
415
+ end
416
+ end
417
+
418
+ let(:context) do
419
+ described_class.new(criteria)
420
+ end
421
+
422
+ it "returns the distinct field values" do
423
+ expect(context.distinct(:street)).to eq([ "hobrecht", "friedel" ])
424
+ end
400
425
  end
401
426
 
402
- it "returns the distinct field values" do
403
- expect(context.distinct(:street)).to eq([ "hobrecht" ])
427
+ context 'when there is a collation on the criteria' do
428
+
429
+ let(:criteria) do
430
+ Address.where(street: "hobrecht").tap do |crit|
431
+ crit.documents = [ hobrecht, hobrecht, friedel ]
432
+ end.collation(locale: 'en_US', strength: 2)
433
+ end
434
+
435
+ it "raises an exception" do
436
+ expect {
437
+ context.distinct(:street)
438
+ }.to raise_exception(Mongoid::Errors::InMemoryCollationNotSupported)
439
+ end
404
440
  end
405
441
  end
406
442
 
407
- context "when not limiting the result set" do
443
+ context "when legacy_pluck_distinct is false" do
444
+ config_override :legacy_pluck_distinct, false
445
+
446
+ let(:depeche) { Band.create!(name: "Depeche Mode", years: 30, sales: "1E2") }
447
+ let(:new_order) { Band.create!(name: "New Order", years: 25, sales: "2E3") }
448
+ let(:maniacs) { Band.create!(name: "10,000 Maniacs", years: 20, sales: "1E2") }
408
449
 
409
450
  let(:criteria) do
410
- Address.all.tap do |crit|
411
- crit.documents = [ hobrecht, friedel, friedel ]
451
+ Band.all.tap do |crit|
452
+ crit.documents = [ depeche, new_order, maniacs ]
412
453
  end
413
454
  end
414
455
 
@@ -416,23 +457,180 @@ describe Mongoid::Contextual::Memory do
416
457
  described_class.new(criteria)
417
458
  end
418
459
 
419
- it "returns the distinct field values" do
420
- expect(context.distinct(:street)).to eq([ "hobrecht", "friedel" ])
460
+ context "when limiting the result set" do
461
+
462
+ let(:criteria) do
463
+ Band.where(name: "Depeche Mode").tap do |crit|
464
+ crit.documents = [ depeche ]
465
+ end
466
+ end
467
+
468
+ it "returns the distinct matching fields" do
469
+ expect(context.distinct(:name)).to eq([ "Depeche Mode" ])
470
+ end
421
471
  end
422
- end
423
472
 
424
- context 'when there is a collation on the criteria' do
473
+ context "when not limiting the result set" do
425
474
 
426
- let(:criteria) do
427
- Address.where(street: "hobrecht").tap do |crit|
428
- crit.documents = [ hobrecht, hobrecht, friedel ]
429
- end.collation(locale: 'en_US', strength: 2)
475
+ it "returns the distinct field values" do
476
+ expect(context.distinct(:name).sort).to eq([ "10,000 Maniacs", "Depeche Mode", "New Order" ].sort)
477
+ end
430
478
  end
431
479
 
432
- it "raises an exception" do
433
- expect {
434
- context.distinct(:street)
435
- }.to raise_exception(Mongoid::Errors::InMemoryCollationNotSupported)
480
+ context "when providing an aliased field" do
481
+
482
+ it "returns the distinct field values" do
483
+ expect(context.distinct(:years).sort).to eq([ 20, 25, 30 ])
484
+ end
485
+ end
486
+
487
+ context "when providing a demongoizable field" do
488
+
489
+ it "returns the non-demongoized distinct field values" do
490
+ expect(context.distinct(:sales).sort).to eq([ BigDecimal("1E2"), BigDecimal("2E3") ])
491
+ end
492
+ end
493
+
494
+ context "when getting a localized field" do
495
+ before do
496
+ I18n.locale = :en
497
+ d = Dictionary.create!(description: 'english-text')
498
+ I18n.locale = :de
499
+ d.description = 'deutsch-text'
500
+ d.save!
501
+ end
502
+
503
+ after do
504
+ I18n.locale = :en
505
+ end
506
+
507
+ let(:criteria) do
508
+ Dictionary.all.tap do |crit|
509
+ crit.documents = [ Dictionary.first ]
510
+ end
511
+ end
512
+
513
+ context "when getting the field without _translations" do
514
+ it "gets the demongoized localized field" do
515
+ expect(context.distinct(:description)).to eq([ 'deutsch-text' ])
516
+ end
517
+ end
518
+
519
+ context "when getting the field with _translations" do
520
+
521
+ it "gets the full hash" do
522
+ expect(context.distinct(:description_translations)).to eq([ { "de" => "deutsch-text", "en" => "english-text" } ])
523
+ end
524
+ end
525
+
526
+ context 'when plucking a specific locale' do
527
+
528
+ let(:distinct) do
529
+ context.distinct(:'description.de')
530
+ end
531
+
532
+ it 'returns the specific translation' do
533
+ expect(distinct).to eq([ "deutsch-text" ])
534
+ end
535
+ end
536
+
537
+ context 'when plucking a specific locale from _translations field' do
538
+
539
+ let(:distinct) do
540
+ context.distinct(:'description_translations.de')
541
+ end
542
+
543
+ it 'returns the specific translations' do
544
+ expect(distinct).to eq(['deutsch-text'])
545
+ end
546
+ end
547
+
548
+ context 'when fallbacks are enabled with a locale list' do
549
+ require_fallbacks
550
+
551
+ around(:all) do |example|
552
+ prev_fallbacks = I18n.fallbacks.dup
553
+ I18n.fallbacks[:he] = [ :en ]
554
+ example.run
555
+ I18n.fallbacks = prev_fallbacks
556
+ end
557
+
558
+ after do
559
+ I18n.locale = :en
560
+ end
561
+
562
+ let(:distinct) do
563
+ context.distinct(:description).first
564
+ end
565
+
566
+ it "correctly uses the fallback" do
567
+ I18n.locale = :en
568
+ d = Dictionary.create!(description: 'english-text')
569
+ I18n.locale = :he
570
+ distinct.should == "english-text"
571
+ end
572
+ end
573
+
574
+ context "when the localized field is embedded" do
575
+ let(:person) do
576
+ p = Passport.new
577
+ I18n.locale = :en
578
+ p.name = "Neil"
579
+ I18n.locale = :he
580
+ p.name = "Nissim"
581
+
582
+ Person.create!(passport: p, employer_id: 12345)
583
+ end
584
+
585
+ after do
586
+ I18n.locale = :en
587
+ end
588
+
589
+ let(:criteria) do
590
+ Person.where(employer_id: 12345).tap do |crit|
591
+ crit.documents = [ person ]
592
+ end
593
+ end
594
+
595
+ let(:distinct) do
596
+ context.distinct("pass.name").first
597
+ end
598
+
599
+ let(:distinct_translations) do
600
+ context.distinct("pass.name_translations").first
601
+ end
602
+
603
+ let(:distinct_translations_field) do
604
+ context.distinct("pass.name_translations.en").first
605
+ end
606
+
607
+ it "returns the translation for the current locale" do
608
+ expect(distinct).to eq("Nissim")
609
+ end
610
+
611
+ it "returns the full _translation hash" do
612
+ expect(distinct_translations).to eq({ "en" => "Neil", "he" => "Nissim" })
613
+ end
614
+
615
+ it "returns the translation for the requested locale" do
616
+ expect(distinct_translations_field).to eq("Neil")
617
+ end
618
+ end
619
+ end
620
+
621
+ context "when getting an embedded field" do
622
+
623
+ let(:label) { Label.new(sales: "1E2") }
624
+ let!(:band) { Band.create!(label: label) }
625
+ let(:criteria) do
626
+ Band.where(_id: band.id).tap do |crit|
627
+ crit.documents = [ band ]
628
+ end
629
+ end
630
+
631
+ it "returns the distinct matching fields" do
632
+ expect(context.distinct("label.sales")).to eq([ BigDecimal("1E2") ])
633
+ end
436
634
  end
437
635
  end
438
636
  end
@@ -624,6 +822,18 @@ describe Mongoid::Contextual::Memory do
624
822
  expect(context.send(method)).to eq(hobrecht)
625
823
  end
626
824
 
825
+ it "returns a list when passing a limit" do
826
+ expect(context.send(method, 2)).to eq([ hobrecht, friedel ])
827
+ end
828
+
829
+ it "returns a list when passing 1" do
830
+ expect(context.send(method, 1)).to eq([ hobrecht ])
831
+ end
832
+
833
+ it "returns the matching document when passing deprecated options" do
834
+ expect(context.send(method, id_sort: :none)).to eq(hobrecht)
835
+ end
836
+
627
837
  context 'when there is a collation on the criteria' do
628
838
 
629
839
  let(:criteria) do
@@ -641,6 +851,108 @@ describe Mongoid::Contextual::Memory do
641
851
  end
642
852
  end
643
853
 
854
+ describe "#take" do
855
+
856
+ let(:hobrecht) do
857
+ Address.new(street: "hobrecht")
858
+ end
859
+
860
+ let(:friedel) do
861
+ Address.new(street: "friedel")
862
+ end
863
+
864
+ let(:criteria) do
865
+ Address.where(:street.in => [ "hobrecht", "friedel" ]).tap do |crit|
866
+ crit.documents = [ hobrecht, friedel ]
867
+ end
868
+ end
869
+
870
+ let(:context) do
871
+ described_class.new(criteria)
872
+ end
873
+
874
+ it "returns the first matching document" do
875
+ expect(context.take).to eq(hobrecht)
876
+ end
877
+
878
+ it "returns an array when passing a limit" do
879
+ expect(context.take(2)).to eq([ hobrecht, friedel ])
880
+ end
881
+
882
+ it "returns an array when passing a limit as 1" do
883
+ expect(context.take(1)).to eq([ hobrecht ])
884
+ end
885
+
886
+ context 'when there is a collation on the criteria' do
887
+
888
+ let(:criteria) do
889
+ Address.where(:street.in => [ "hobrecht", "friedel" ]).tap do |crit|
890
+ crit.documents = [ hobrecht, friedel ]
891
+ end.collation(locale: 'en_US', strength: 2)
892
+ end
893
+
894
+ it "raises an exception" do
895
+ expect {
896
+ context.take
897
+ }.to raise_exception(Mongoid::Errors::InMemoryCollationNotSupported)
898
+ end
899
+ end
900
+ end
901
+
902
+ describe "#take!" do
903
+
904
+ let(:hobrecht) do
905
+ Address.new(street: "hobrecht")
906
+ end
907
+
908
+ let(:friedel) do
909
+ Address.new(street: "friedel")
910
+ end
911
+
912
+ let(:criteria) do
913
+ Address.where(:street.in => [ "hobrecht", "friedel" ]).tap do |crit|
914
+ crit.documents = [ hobrecht, friedel ]
915
+ end
916
+ end
917
+
918
+ let(:context) do
919
+ described_class.new(criteria)
920
+ end
921
+
922
+ it "returns the first matching document" do
923
+ expect(context.take!).to eq(hobrecht)
924
+ end
925
+
926
+ context "when the criteria is empty" do
927
+ let(:criteria) do
928
+ Address.where(street: "bogus").tap do |crit|
929
+ crit.documents = []
930
+ end
931
+ end
932
+
933
+ it "raise an error" do
934
+ expect do
935
+ context.take!
936
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Address./)
937
+ end
938
+ end
939
+
940
+ context 'when there is a collation on the criteria' do
941
+
942
+ let(:criteria) do
943
+ Address.where(:street.in => [ "hobrecht", "friedel" ]).tap do |crit|
944
+ crit.documents = [ hobrecht, friedel ]
945
+ end.collation(locale: 'en_US', strength: 2)
946
+ end
947
+
948
+ it "raises an exception" do
949
+ expect {
950
+ context.take
951
+ }.to raise_exception(Mongoid::Errors::InMemoryCollationNotSupported)
952
+ end
953
+ end
954
+ end
955
+
644
956
  describe "#initialize" do
645
957
 
646
958
  context "when the criteria has no options" do
@@ -766,6 +1078,18 @@ describe Mongoid::Contextual::Memory do
766
1078
  expect(context.last).to eq(friedel)
767
1079
  end
768
1080
 
1081
+ it "returns a list when a limit is passed" do
1082
+ expect(context.last(2)).to eq([ hobrecht, friedel ])
1083
+ end
1084
+
1085
+ it "returns a list when the limit is 1" do
1086
+ expect(context.last(1)).to eq([ friedel ])
1087
+ end
1088
+
1089
+ it "returns the matching document when passing deprecated options" do
1090
+ expect(context.last(id_sort: :none)).to eq(friedel)
1091
+ end
1092
+
769
1093
  context 'when there is a collation on the criteria' do
770
1094
 
771
1095
  let(:criteria) do
@@ -919,72 +1243,509 @@ describe Mongoid::Contextual::Memory do
919
1243
 
920
1244
  describe "#pluck" do
921
1245
 
922
- let(:hobrecht) do
923
- Address.new(street: "hobrecht")
1246
+ let(:context) do
1247
+ described_class.new(criteria)
924
1248
  end
925
1249
 
926
- let(:friedel) do
927
- Address.new(street: "friedel")
928
- end
1250
+ context "when legacy_pluck_distinct is true" do
1251
+ config_override :legacy_pluck_distinct, true
929
1252
 
930
- let(:criteria) do
931
- Address.all.tap do |crit|
932
- crit.documents = [ hobrecht, friedel ]
1253
+ let(:hobrecht) do
1254
+ Address.new(street: "hobrecht")
933
1255
  end
934
- end
935
1256
 
936
- let(:context) do
937
- described_class.new(criteria)
938
- end
1257
+ let(:friedel) do
1258
+ Address.new(street: "friedel")
1259
+ end
1260
+
1261
+ let(:criteria) do
1262
+ Address.all.tap do |crit|
1263
+ crit.documents = [ hobrecht, friedel ]
1264
+ end
1265
+ end
1266
+
1267
+ context "when plucking" do
1268
+
1269
+ let!(:plucked) do
1270
+ context.pluck(:street)
1271
+ end
1272
+
1273
+ it "returns the values" do
1274
+ expect(plucked).to eq([ "hobrecht", "friedel" ])
1275
+ end
1276
+ end
1277
+
1278
+ context "when plucking a mix of empty and non-empty values" do
1279
+
1280
+ let(:empty_doc) do
1281
+ Address.new(street: nil)
1282
+ end
1283
+
1284
+ let(:criteria) do
1285
+ Address.all.tap do |crit|
1286
+ crit.documents = [ hobrecht, friedel, empty_doc ]
1287
+ end
1288
+ end
1289
+
1290
+ let!(:plucked) do
1291
+ context.pluck(:street)
1292
+ end
1293
+
1294
+ it "returns the values" do
1295
+ expect(plucked).to eq([ "hobrecht", "friedel", nil ])
1296
+ end
1297
+ end
1298
+
1299
+ context "when plucking a field that doesnt exist" do
1300
+
1301
+ context "when pluck one field" do
1302
+
1303
+ let(:plucked) do
1304
+ context.pluck(:foo)
1305
+ end
1306
+
1307
+ it "returns a empty array" do
1308
+ expect(plucked).to eq([nil, nil])
1309
+ end
1310
+ end
939
1311
 
940
- context "when plucking" do
1312
+ context "when pluck multiple fields" do
941
1313
 
942
- let!(:plucked) do
943
- context.pluck(:street)
1314
+ let(:plucked) do
1315
+ context.pluck(:foo, :bar)
1316
+ end
1317
+
1318
+ it "returns a empty array" do
1319
+ expect(plucked).to eq([[nil, nil], [nil, nil]])
1320
+ end
1321
+ end
944
1322
  end
945
1323
 
946
- it "returns the values" do
947
- expect(plucked).to eq([ "hobrecht", "friedel" ])
1324
+ context 'when there is a collation on the criteria' do
1325
+
1326
+ let(:criteria) do
1327
+ Address.all.tap do |crit|
1328
+ crit.documents = [ hobrecht, friedel ]
1329
+ end.collation(locale: 'en_US', strength: 2)
1330
+ end
1331
+
1332
+ it "raises an exception" do
1333
+ expect {
1334
+ context.pluck(:foo, :bar)
1335
+ }.to raise_exception(Mongoid::Errors::InMemoryCollationNotSupported)
1336
+ end
948
1337
  end
949
1338
  end
950
1339
 
951
- context "when plucking a field that doesnt exist" do
1340
+ context "when legacy_pluck_distinct is false" do
1341
+ config_override :legacy_pluck_distinct, false
952
1342
 
953
- context "when pluck one field" do
1343
+ let!(:depeche) do
1344
+ Band.create!(name: "Depeche Mode", likes: 3)
1345
+ end
1346
+
1347
+ let!(:tool) do
1348
+ Band.create!(name: "Tool", likes: 3)
1349
+ end
1350
+
1351
+ let!(:photek) do
1352
+ Band.create!(name: "Photek", likes: 1)
1353
+ end
1354
+
1355
+ let(:maniacs) do
1356
+ Band.create!(name: "10,000 Maniacs", likes: 1, sales: "1E2")
1357
+ end
1358
+
1359
+ let(:criteria) do
1360
+ Band.all.tap do |crit|
1361
+ crit.documents = [ depeche, tool, photek, maniacs ]
1362
+ end
1363
+ end
1364
+
1365
+ context "when the field is aliased" do
1366
+
1367
+ let!(:expensive) do
1368
+ Product.create!(price: 100000)
1369
+ end
1370
+
1371
+ let!(:cheap) do
1372
+ Product.create!(price: 1)
1373
+ end
1374
+
1375
+ let(:criteria) do
1376
+ Product.all.tap do |crit|
1377
+ crit.documents = [ expensive, cheap ]
1378
+ end
1379
+ end
1380
+
1381
+ context "when using alias_attribute" do
1382
+
1383
+ let(:plucked) do
1384
+ context.pluck(:price)
1385
+ end
1386
+
1387
+ it "uses the aliases" do
1388
+ expect(plucked).to eq([ 100000, 1 ])
1389
+ end
1390
+ end
1391
+ end
1392
+
1393
+ context "when the criteria matches" do
1394
+
1395
+ context "when there are no duplicate values" do
1396
+
1397
+ let!(:plucked) do
1398
+ context.pluck(:name)
1399
+ end
1400
+
1401
+ it "returns the values" do
1402
+ expect(plucked).to contain_exactly("10,000 Maniacs", "Depeche Mode", "Tool", "Photek")
1403
+ end
1404
+
1405
+ context "when subsequently executing the criteria without a pluck" do
1406
+
1407
+ it "does not limit the fields" do
1408
+ expect(context.first.likes).to eq(3)
1409
+ end
1410
+ end
1411
+
1412
+ context 'when the field is a subdocument' do
1413
+
1414
+ context 'when a top-level field and a subdocument field are plucked' do
1415
+ let(:criteria) do
1416
+ Band.where(name: 'FKA Twigs').tap do |crit|
1417
+ crit.documents = [
1418
+ Band.create!(name: 'FKA Twigs'),
1419
+ Band.create!(name: 'FKA Twigs', records: [ Record.new(name: 'LP1') ])
1420
+ ]
1421
+ end
1422
+ end
1423
+
1424
+ let(:embedded_pluck) do
1425
+ context.pluck(:name, 'records.name')
1426
+ end
1427
+
1428
+ let(:expected) do
1429
+ [
1430
+ ["FKA Twigs", []],
1431
+ ['FKA Twigs', ["LP1"]]
1432
+ ]
1433
+ end
1434
+
1435
+ it 'returns the list of top-level field and subdocument values' do
1436
+ expect(embedded_pluck).to eq(expected)
1437
+ end
1438
+ end
1439
+
1440
+ context 'when only a subdocument field is plucked' do
1441
+
1442
+ let(:criteria) do
1443
+ Band.where(name: 'FKA Twigs').tap do |crit|
1444
+ crit.documents = [
1445
+ Band.create!(name: 'FKA Twigs'),
1446
+ Band.create!(name: 'FKA Twigs', records: [ Record.new(name: 'LP1') ])
1447
+ ]
1448
+ end
1449
+ end
1450
+
1451
+ let(:embedded_pluck) do
1452
+ context.pluck('records.name')
1453
+ end
1454
+
1455
+ let(:expected) do
1456
+ [
1457
+ [],
1458
+ ["LP1"]
1459
+ ]
1460
+ end
1461
+
1462
+ it 'returns the list of subdocument values' do
1463
+ expect(embedded_pluck).to eq(expected)
1464
+ end
1465
+ end
1466
+ end
1467
+ end
1468
+
1469
+ context "when plucking multi-fields" do
1470
+
1471
+ let(:plucked) do
1472
+ context.pluck(:name, :likes)
1473
+ end
1474
+
1475
+ it "returns the values" do
1476
+ expect(plucked).to contain_exactly(["10,000 Maniacs", 1], ["Depeche Mode", 3], ["Tool", 3], ["Photek", 1])
1477
+ end
1478
+ end
1479
+
1480
+ context "when there are duplicate values" do
1481
+
1482
+ let(:plucked) do
1483
+ context.pluck(:likes)
1484
+ end
1485
+
1486
+ it "returns the duplicates" do
1487
+ expect(plucked).to contain_exactly(1, 3, 3, 1)
1488
+ end
1489
+ end
1490
+ end
1491
+
1492
+ context "when plucking an aliased field" do
954
1493
 
955
1494
  let(:plucked) do
956
- context.pluck(:foo)
1495
+ context.pluck(:id)
957
1496
  end
958
1497
 
959
- it "returns a empty array" do
960
- expect(plucked).to eq([])
1498
+ it "returns the field values" do
1499
+ expect(plucked).to eq([ depeche.id, tool.id, photek.id, maniacs.id ])
961
1500
  end
962
1501
  end
963
1502
 
964
- context "when pluck multiple fields" do
1503
+ context "when plucking existent and non-existent fields" do
965
1504
 
966
1505
  let(:plucked) do
967
- context.pluck(:foo, :bar)
1506
+ context.pluck(:id, :fooz)
968
1507
  end
969
1508
 
970
- it "returns a empty array" do
971
- expect(plucked).to eq([[], []])
1509
+ it "returns nil for the field that doesnt exist" do
1510
+ expect(plucked).to eq([[depeche.id, nil], [tool.id, nil], [photek.id, nil], [maniacs.id, nil] ])
972
1511
  end
973
1512
  end
974
- end
975
1513
 
976
- context 'when there is a collation on the criteria' do
1514
+ context "when plucking a field that doesnt exist" do
977
1515
 
978
- let(:criteria) do
979
- Address.all.tap do |crit|
980
- crit.documents = [ hobrecht, friedel ]
981
- end.collation(locale: 'en_US', strength: 2)
1516
+ context "when pluck one field" do
1517
+
1518
+ let(:plucked) do
1519
+ context.pluck(:foo)
1520
+ end
1521
+
1522
+ it "returns an array with nil values" do
1523
+ expect(plucked).to eq([nil, nil, nil, nil])
1524
+ end
1525
+ end
1526
+
1527
+ context "when pluck multiple fields" do
1528
+
1529
+ let(:plucked) do
1530
+ context.pluck(:foo, :bar)
1531
+ end
1532
+
1533
+ it "returns an array of arrays with nil values" do
1534
+ expect(plucked).to eq([[nil, nil], [nil, nil], [nil, nil], [nil, nil]])
1535
+ end
1536
+ end
982
1537
  end
983
1538
 
984
- it "raises an exception" do
985
- expect {
986
- context.pluck(:foo, :bar)
987
- }.to raise_exception(Mongoid::Errors::InMemoryCollationNotSupported)
1539
+ context 'when plucking a localized field' do
1540
+
1541
+ before do
1542
+ I18n.locale = :en
1543
+ d = Dictionary.create!(description: 'english-text')
1544
+ I18n.locale = :de
1545
+ d.description = 'deutsch-text'
1546
+ d.save!
1547
+ end
1548
+
1549
+ after do
1550
+ I18n.locale = :en
1551
+ end
1552
+
1553
+ let(:criteria) do
1554
+ Dictionary.all.tap do |crit|
1555
+ crit.documents = [ Dictionary.first ]
1556
+ end
1557
+ end
1558
+
1559
+ context 'when plucking the entire field' do
1560
+
1561
+ let(:plucked) do
1562
+ context.pluck(:description)
1563
+ end
1564
+
1565
+ let(:plucked_translations) do
1566
+ context.pluck(:description_translations)
1567
+ end
1568
+
1569
+ let(:plucked_translations_both) do
1570
+ context.pluck(:description_translations, :description)
1571
+ end
1572
+
1573
+ it 'returns the demongoized translations' do
1574
+ expect(plucked.first).to eq('deutsch-text')
1575
+ end
1576
+
1577
+ it 'returns the full translations hash to _translations' do
1578
+ expect(plucked_translations.first).to eq({"de"=>"deutsch-text", "en"=>"english-text"})
1579
+ end
1580
+
1581
+ it 'returns both' do
1582
+ expect(plucked_translations_both.first).to eq([{"de"=>"deutsch-text", "en"=>"english-text"}, "deutsch-text"])
1583
+ end
1584
+ end
1585
+
1586
+ context 'when plucking a specific locale' do
1587
+
1588
+ let(:plucked) do
1589
+ context.pluck(:'description.de')
1590
+ end
1591
+
1592
+ it 'returns the specific translations' do
1593
+ expect(plucked.first).to eq('deutsch-text')
1594
+ end
1595
+ end
1596
+
1597
+ context 'when plucking a specific locale from _translations field' do
1598
+
1599
+ let(:plucked) do
1600
+ context.pluck(:'description_translations.de')
1601
+ end
1602
+
1603
+ it 'returns the specific translations' do
1604
+ expect(plucked.first).to eq('deutsch-text')
1605
+ end
1606
+ end
1607
+
1608
+ context 'when fallbacks are enabled with a locale list' do
1609
+ require_fallbacks
1610
+
1611
+ around(:all) do |example|
1612
+ prev_fallbacks = I18n.fallbacks.dup
1613
+ I18n.fallbacks[:he] = [ :en ]
1614
+ example.run
1615
+ I18n.fallbacks = prev_fallbacks
1616
+ end
1617
+
1618
+ after do
1619
+ I18n.locale = :en
1620
+ end
1621
+
1622
+ let(:plucked) do
1623
+ context.pluck(:description).first
1624
+ end
1625
+
1626
+ it "correctly uses the fallback" do
1627
+ I18n.locale = :en
1628
+ d = Dictionary.create!(description: 'english-text')
1629
+ I18n.locale = :he
1630
+ plucked.should == "english-text"
1631
+ end
1632
+ end
1633
+
1634
+ context "when the localized field is embedded" do
1635
+ before do
1636
+ p = Passport.new
1637
+ I18n.locale = :en
1638
+ p.name = "Neil"
1639
+ I18n.locale = :he
1640
+ p.name = "Nissim"
1641
+
1642
+ Person.create!(passport: p, employer_id: 12345)
1643
+ end
1644
+
1645
+ after do
1646
+ I18n.locale = :en
1647
+ end
1648
+
1649
+ let(:plucked) do
1650
+ Person.where(employer_id: 12345).pluck("pass.name").first
1651
+ end
1652
+
1653
+ let(:plucked_translations) do
1654
+ Person.where(employer_id: 12345).pluck("pass.name_translations").first
1655
+ end
1656
+
1657
+ let(:plucked_translations_field) do
1658
+ Person.where(employer_id: 12345).pluck("pass.name_translations.en").first
1659
+ end
1660
+
1661
+ it "returns the translation for the current locale" do
1662
+ expect(plucked).to eq("Nissim")
1663
+ end
1664
+
1665
+ it "returns the full _translation hash" do
1666
+ expect(plucked_translations).to eq({ "en" => "Neil", "he" => "Nissim" })
1667
+ end
1668
+
1669
+ it "returns the translation for the requested locale" do
1670
+ expect(plucked_translations_field).to eq("Neil")
1671
+ end
1672
+ end
1673
+ end
1674
+
1675
+ context 'when plucking a field to be demongoized' do
1676
+
1677
+ let(:plucked) do
1678
+ Band.where(name: maniacs.name).pluck(:sales)
1679
+ end
1680
+
1681
+ it "demongoizes the field" do
1682
+ expect(plucked.first).to be_a(BigDecimal)
1683
+ expect(plucked.first).to eq(BigDecimal("1E2"))
1684
+ end
1685
+ end
1686
+
1687
+ context "when plucking an embedded field" do
1688
+ let(:label) { Label.new(sales: "1E2") }
1689
+ let!(:band) { Band.create!(label: label) }
1690
+
1691
+ let(:plucked) do
1692
+ Band.where(_id: band.id).tap do |crit|
1693
+ crit.documents = [band]
1694
+ end.pluck("label.sales")
1695
+ end
1696
+
1697
+ it "demongoizes the field" do
1698
+ expect(plucked).to eq([ BigDecimal("1E2") ])
1699
+ end
1700
+ end
1701
+
1702
+ context "when plucking an embeds_many field" do
1703
+ let(:label) { Label.new(sales: "1E2") }
1704
+ let!(:band) { Band.create!(labels: [label]) }
1705
+
1706
+ let(:plucked) { Band.where(_id: band.id).pluck("labels.sales") }
1707
+
1708
+ it "demongoizes the field" do
1709
+ expect(plucked.first).to eq([ BigDecimal("1E2") ])
1710
+ end
1711
+ end
1712
+
1713
+ context "when plucking a nonexistent embedded field" do
1714
+ let(:label) { Label.new(sales: "1E2") }
1715
+ let!(:band) { Band.create!(label: label) }
1716
+
1717
+ let(:plucked) do
1718
+ Band.where(_id: band.id).tap do |crit|
1719
+ crit.documents = band
1720
+ end.pluck("label.qwerty")
1721
+ end
1722
+
1723
+ it "returns nil" do
1724
+ expect(plucked.first).to eq(nil)
1725
+ end
1726
+ end
1727
+
1728
+ context "when tallying deeply nested arrays/embedded associations" do
1729
+
1730
+ let(:criteria) do
1731
+ Person.all.tap do |crit|
1732
+ crit.documents = [
1733
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ]),
1734
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ]),
1735
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 3 } } ]))) ]),
1736
+ ]
1737
+ end
1738
+ end
1739
+
1740
+ let(:plucked) do
1741
+ context.pluck("addresses.code.deepest.array.y.z")
1742
+ end
1743
+
1744
+ it "returns the correct hash" do
1745
+ expect(plucked).to eq([
1746
+ [ [ 1, 2 ] ], [ [ 1, 2 ] ], [ [ 1, 3 ] ]
1747
+ ])
1748
+ end
988
1749
  end
989
1750
  end
990
1751
  end