mongoid 8.0.7 → 8.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (177) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +3 -3
  4. data/README.md +3 -3
  5. data/Rakefile +0 -25
  6. data/lib/config/locales/en.yml +46 -14
  7. data/lib/mongoid/association/accessors.rb +2 -2
  8. data/lib/mongoid/association/builders.rb +1 -1
  9. data/lib/mongoid/association/embedded/batchable.rb +2 -2
  10. data/lib/mongoid/association/embedded/embedded_in/buildable.rb +2 -2
  11. data/lib/mongoid/association/embedded/embedded_in/proxy.rb +2 -1
  12. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +3 -2
  13. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +6 -6
  14. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +1 -1
  15. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +1 -1
  16. data/lib/mongoid/association/macros.rb +0 -6
  17. data/lib/mongoid/association/nested/one.rb +40 -2
  18. data/lib/mongoid/association/proxy.rb +1 -1
  19. data/lib/mongoid/association/referenced/counter_cache.rb +2 -2
  20. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +1 -1
  21. data/lib/mongoid/association/referenced/has_many/enumerable.rb +2 -2
  22. data/lib/mongoid/association/referenced/has_many/proxy.rb +3 -3
  23. data/lib/mongoid/association/reflections.rb +2 -2
  24. data/lib/mongoid/atomic.rb +0 -7
  25. data/lib/mongoid/attributes/dynamic.rb +1 -1
  26. data/lib/mongoid/attributes/nested.rb +2 -2
  27. data/lib/mongoid/attributes/processing.rb +5 -29
  28. data/lib/mongoid/attributes/projector.rb +1 -1
  29. data/lib/mongoid/attributes/readonly.rb +1 -1
  30. data/lib/mongoid/attributes.rb +8 -2
  31. data/lib/mongoid/changeable.rb +107 -5
  32. data/lib/mongoid/clients/storage_options.rb +2 -5
  33. data/lib/mongoid/clients/validators/storage.rb +1 -13
  34. data/lib/mongoid/collection_configurable.rb +58 -0
  35. data/lib/mongoid/composable.rb +2 -0
  36. data/lib/mongoid/config/defaults.rb +60 -0
  37. data/lib/mongoid/config/options.rb +0 -3
  38. data/lib/mongoid/config/validators/async_query_executor.rb +24 -0
  39. data/lib/mongoid/config/validators.rb +1 -0
  40. data/lib/mongoid/config.rb +99 -28
  41. data/lib/mongoid/contextual/atomic.rb +1 -1
  42. data/lib/mongoid/contextual/memory.rb +233 -33
  43. data/lib/mongoid/contextual/mongo/documents_loader.rb +177 -0
  44. data/lib/mongoid/contextual/mongo.rb +370 -133
  45. data/lib/mongoid/contextual/none.rb +162 -7
  46. data/lib/mongoid/contextual.rb +12 -0
  47. data/lib/mongoid/criteria/findable.rb +2 -2
  48. data/lib/mongoid/criteria/includable.rb +4 -3
  49. data/lib/mongoid/criteria/queryable/key.rb +1 -1
  50. data/lib/mongoid/criteria/queryable/mergeable.rb +1 -1
  51. data/lib/mongoid/criteria/queryable/optional.rb +8 -8
  52. data/lib/mongoid/criteria/queryable/selectable.rb +43 -12
  53. data/lib/mongoid/criteria/queryable/selector.rb +1 -1
  54. data/lib/mongoid/criteria/queryable/storable.rb +1 -1
  55. data/lib/mongoid/criteria.rb +6 -5
  56. data/lib/mongoid/deprecable.rb +1 -2
  57. data/lib/mongoid/deprecation.rb +3 -3
  58. data/lib/mongoid/errors/create_collection_failure.rb +33 -0
  59. data/lib/mongoid/errors/drop_collection_failure.rb +27 -0
  60. data/lib/mongoid/errors/immutable_attribute.rb +26 -0
  61. data/lib/mongoid/errors/invalid_async_query_executor.rb +25 -0
  62. data/lib/mongoid/errors/invalid_global_executor_concurrency.rb +22 -0
  63. data/lib/mongoid/errors/invalid_storage_parent.rb +2 -0
  64. data/lib/mongoid/errors.rb +4 -1
  65. data/lib/mongoid/extensions/hash.rb +2 -24
  66. data/lib/mongoid/extensions/object.rb +2 -2
  67. data/lib/mongoid/extensions/time.rb +2 -0
  68. data/lib/mongoid/fields/localized.rb +10 -0
  69. data/lib/mongoid/fields/standard.rb +10 -0
  70. data/lib/mongoid/fields.rb +53 -24
  71. data/lib/mongoid/findable.rb +27 -3
  72. data/lib/mongoid/interceptable.rb +10 -118
  73. data/lib/mongoid/matcher/eq_impl.rb +1 -1
  74. data/lib/mongoid/matcher/type.rb +1 -1
  75. data/lib/mongoid/persistable/creatable.rb +1 -0
  76. data/lib/mongoid/persistable/deletable.rb +1 -1
  77. data/lib/mongoid/persistable/savable.rb +13 -1
  78. data/lib/mongoid/persistable/unsettable.rb +2 -2
  79. data/lib/mongoid/persistable/updatable.rb +51 -1
  80. data/lib/mongoid/persistable/upsertable.rb +20 -1
  81. data/lib/mongoid/persistable.rb +3 -0
  82. data/lib/mongoid/query_cache.rb +5 -1
  83. data/lib/mongoid/railties/database.rake +7 -2
  84. data/lib/mongoid/reloadable.rb +5 -3
  85. data/lib/mongoid/stateful.rb +22 -1
  86. data/lib/mongoid/tasks/database.rake +12 -0
  87. data/lib/mongoid/tasks/database.rb +20 -0
  88. data/lib/mongoid/utils.rb +22 -0
  89. data/lib/mongoid/validatable/macros.rb +5 -5
  90. data/lib/mongoid/validatable.rb +4 -1
  91. data/lib/mongoid/version.rb +1 -1
  92. data/lib/mongoid/warnings.rb +17 -1
  93. data/lib/mongoid.rb +16 -3
  94. data/spec/integration/app_spec.rb +2 -2
  95. data/spec/integration/callbacks_models.rb +37 -0
  96. data/spec/integration/callbacks_spec.rb +126 -12
  97. data/spec/integration/discriminator_key_spec.rb +4 -5
  98. data/spec/integration/i18n_fallbacks_spec.rb +3 -2
  99. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +27 -0
  100. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +20 -25
  101. data/spec/mongoid/association/embedded/embeds_many_models.rb +1 -0
  102. data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +15 -2
  103. data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -18
  104. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +5 -27
  105. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +9 -50
  106. data/spec/mongoid/association/syncable_spec.rb +1 -1
  107. data/spec/mongoid/attributes_spec.rb +3 -33
  108. data/spec/mongoid/changeable_spec.rb +299 -24
  109. data/spec/mongoid/clients_spec.rb +122 -13
  110. data/spec/mongoid/collection_configurable_spec.rb +158 -0
  111. data/spec/mongoid/config/defaults_spec.rb +160 -0
  112. data/spec/mongoid/config_spec.rb +154 -27
  113. data/spec/mongoid/contextual/memory_spec.rb +332 -76
  114. data/spec/mongoid/contextual/mongo/documents_loader_spec.rb +187 -0
  115. data/spec/mongoid/contextual/mongo_spec.rb +1009 -125
  116. data/spec/mongoid/contextual/none_spec.rb +49 -2
  117. data/spec/mongoid/copyable_spec.rb +2 -10
  118. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -10
  119. data/spec/mongoid/criteria/queryable/options_spec.rb +1 -1
  120. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +419 -0
  121. data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -1
  122. data/spec/mongoid/criteria/queryable/selector_spec.rb +3 -76
  123. data/spec/mongoid/criteria/queryable/storable_spec.rb +0 -72
  124. data/spec/mongoid/criteria_projection_spec.rb +1 -4
  125. data/spec/mongoid/criteria_spec.rb +5 -9
  126. data/spec/mongoid/errors/readonly_document_spec.rb +2 -2
  127. data/spec/mongoid/extensions/hash_spec.rb +3 -3
  128. data/spec/mongoid/extensions/time_spec.rb +8 -43
  129. data/spec/mongoid/extensions/time_with_zone_spec.rb +7 -52
  130. data/spec/mongoid/fields/localized_spec.rb +46 -28
  131. data/spec/mongoid/fields_spec.rb +136 -77
  132. data/spec/mongoid/findable_spec.rb +391 -34
  133. data/spec/mongoid/indexable_spec.rb +16 -10
  134. data/spec/mongoid/interceptable_spec.rb +173 -362
  135. data/spec/mongoid/persistable/deletable_spec.rb +26 -6
  136. data/spec/mongoid/persistable/destroyable_spec.rb +26 -6
  137. data/spec/mongoid/persistable/incrementable_spec.rb +37 -0
  138. data/spec/mongoid/persistable/logical_spec.rb +37 -0
  139. data/spec/mongoid/persistable/poppable_spec.rb +36 -0
  140. data/spec/mongoid/persistable/pullable_spec.rb +72 -0
  141. data/spec/mongoid/persistable/pushable_spec.rb +72 -0
  142. data/spec/mongoid/persistable/renamable_spec.rb +36 -0
  143. data/spec/mongoid/persistable/savable_spec.rb +96 -0
  144. data/spec/mongoid/persistable/settable_spec.rb +37 -0
  145. data/spec/mongoid/persistable/unsettable_spec.rb +36 -0
  146. data/spec/mongoid/persistable/updatable_spec.rb +20 -28
  147. data/spec/mongoid/persistable/upsertable_spec.rb +80 -6
  148. data/spec/mongoid/persistence_context_spec.rb +7 -57
  149. data/spec/mongoid/query_cache_spec.rb +56 -61
  150. data/spec/mongoid/reloadable_spec.rb +24 -28
  151. data/spec/mongoid/scopable_spec.rb +70 -0
  152. data/spec/mongoid/serializable_spec.rb +9 -30
  153. data/spec/mongoid/stateful_spec.rb +122 -8
  154. data/spec/mongoid/tasks/database_rake_spec.rb +74 -0
  155. data/spec/mongoid/tasks/database_spec.rb +127 -0
  156. data/spec/mongoid/timestamps_spec.rb +9 -11
  157. data/spec/mongoid/touchable_spec.rb +277 -5
  158. data/spec/mongoid/touchable_spec_models.rb +3 -1
  159. data/spec/mongoid/traversable_spec.rb +9 -24
  160. data/spec/mongoid/validatable/uniqueness_spec.rb +2 -3
  161. data/spec/mongoid_spec.rb +36 -10
  162. data/spec/spec_helper.rb +5 -0
  163. data/spec/support/immutable_ids.rb +118 -0
  164. data/spec/support/macros.rb +47 -15
  165. data/spec/support/models/artist.rb +0 -1
  166. data/spec/support/models/band.rb +1 -0
  167. data/spec/support/models/book.rb +1 -0
  168. data/spec/support/models/building.rb +2 -0
  169. data/spec/support/models/cover.rb +10 -0
  170. data/spec/support/models/person.rb +0 -1
  171. data/spec/support/models/product.rb +1 -0
  172. data.tar.gz.sig +0 -0
  173. metadata +685 -651
  174. metadata.gz.sig +0 -0
  175. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -60
  176. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -60
  177. data/spec/support/models/purse.rb +0 -9
@@ -4,6 +4,36 @@ require "spec_helper"
4
4
 
5
5
  describe Mongoid::Contextual::Memory do
6
6
 
7
+ shared_examples "raises an error when no documents" do
8
+ context "when there are no matching documents" do
9
+ let(:criteria) do
10
+ Address.all.tap do |crit|
11
+ crit.documents = []
12
+ end
13
+ end
14
+
15
+ it "returns nil" do
16
+ expect do
17
+ context.send(method)
18
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Address./)
19
+ end
20
+ end
21
+ end
22
+
23
+ shared_examples "returns nil when no documents" do
24
+ context "when there are no matching documents" do
25
+ let(:criteria) do
26
+ Address.all.tap do |crit|
27
+ crit.documents = []
28
+ end
29
+ end
30
+
31
+ it "returns nil" do
32
+ expect(context.send(method)).to be_nil
33
+ end
34
+ end
35
+ end
36
+
7
37
  [ :blank?, :empty? ].each do |method|
8
38
 
9
39
  describe "##{method}" do
@@ -492,6 +522,8 @@ describe Mongoid::Contextual::Memory do
492
522
  end
493
523
 
494
524
  context "when getting a localized field" do
525
+ with_default_i18n_configs
526
+
495
527
  before do
496
528
  I18n.locale = :en
497
529
  d = Dictionary.create!(description: 'english-text')
@@ -500,10 +532,6 @@ describe Mongoid::Contextual::Memory do
500
532
  d.save!
501
533
  end
502
534
 
503
- after do
504
- I18n.locale = :en
505
- end
506
-
507
535
  let(:criteria) do
508
536
  Dictionary.all.tap do |crit|
509
537
  crit.documents = [ Dictionary.first ]
@@ -546,17 +574,11 @@ describe Mongoid::Contextual::Memory do
546
574
  end
547
575
 
548
576
  context 'when fallbacks are enabled with a locale list' do
549
- with_i18n_fallbacks
577
+ require_fallbacks
578
+ with_default_i18n_configs
550
579
 
551
- around(:all) do |example|
552
- prev_fallbacks = I18n.fallbacks.dup
580
+ before do
553
581
  I18n.fallbacks[:he] = [ :en ]
554
- example.run
555
- I18n.fallbacks = prev_fallbacks
556
- end
557
-
558
- after do
559
- I18n.locale = :en
560
582
  end
561
583
 
562
584
  let(:distinct) do
@@ -572,6 +594,8 @@ describe Mongoid::Contextual::Memory do
572
594
  end
573
595
 
574
596
  context "when the localized field is embedded" do
597
+ with_default_i18n_configs
598
+
575
599
  let(:person) do
576
600
  p = Passport.new
577
601
  I18n.locale = :en
@@ -582,10 +606,6 @@ describe Mongoid::Contextual::Memory do
582
606
  Person.create!(passport: p, employer_id: 12345)
583
607
  end
584
608
 
585
- after do
586
- I18n.locale = :en
587
- end
588
-
589
609
  let(:criteria) do
590
610
  Person.where(employer_id: 12345).tap do |crit|
591
611
  crit.documents = [ person ]
@@ -746,52 +766,158 @@ describe Mongoid::Contextual::Memory do
746
766
  Address.new(street: "friedel")
747
767
  end
748
768
 
749
- context "when there are matching documents" do
769
+ let(:criteria) do
770
+ Address.where(street: "hobrecht").tap do |crit|
771
+ crit.documents = [ hobrecht, friedel ]
772
+ end
773
+ end
750
774
 
751
- let(:criteria) do
752
- Address.where(street: "hobrecht").tap do |crit|
753
- crit.documents = [ hobrecht, friedel ]
775
+ context "when not passing options" do
776
+
777
+ context "when there are matching documents" do
778
+
779
+ let(:context) do
780
+ described_class.new(criteria)
781
+ end
782
+
783
+ it "returns true" do
784
+ expect(context).to be_exists
754
785
  end
755
786
  end
756
787
 
757
- let(:context) do
758
- described_class.new(criteria)
788
+ context "when there are no matching documents" do
789
+
790
+ let(:criteria) do
791
+ Address.where(street: "pfluger").tap do |crit|
792
+ crit.documents = [ hobrecht, friedel ]
793
+ end
794
+ end
795
+
796
+ let(:context) do
797
+ described_class.new(criteria)
798
+ end
799
+
800
+ it "returns false" do
801
+ expect(context).to_not be_exists
802
+ end
759
803
  end
760
804
 
761
- it "returns true" do
762
- expect(context).to be_exists
805
+ context 'when there is a collation on the criteria' do
806
+
807
+ let(:criteria) do
808
+ Address.where(street: "pfluger").tap do |crit|
809
+ crit.documents = [ hobrecht, friedel ]
810
+ end.collation(locale: 'en_US', strength: 2)
811
+ end
812
+
813
+ it "raises an exception" do
814
+ expect {
815
+ context
816
+ }.to raise_exception(Mongoid::Errors::InMemoryCollationNotSupported)
817
+ end
763
818
  end
764
819
  end
765
820
 
766
- context "when there are no matching documents" do
821
+ context "when passing an _id" do
767
822
 
768
- let(:criteria) do
769
- Address.where(street: "pfluger").tap do |crit|
770
- crit.documents = [ hobrecht, friedel ]
823
+ context "when its of type BSON::ObjectId" do
824
+
825
+ context "when calling it on an empty criteria" do
826
+
827
+ it "returns true" do
828
+ expect(criteria.exists?(hobrecht._id)).to be true
829
+ end
830
+ end
831
+
832
+ context "when calling it on a criteria that includes the object" do
833
+
834
+ it "returns true" do
835
+ expect(criteria.where(street: hobrecht.street).exists?(hobrecht._id)).to be true
836
+ end
837
+ end
838
+
839
+ context "when calling it on a criteria that does not include the object" do
840
+
841
+ it "returns false" do
842
+ expect(criteria.where(street: "bogus").exists?(hobrecht._id)).to be false
843
+ end
844
+ end
845
+
846
+ context "when the id does not exist" do
847
+
848
+ it "returns false" do
849
+ expect(criteria.exists?(BSON::ObjectId.new)).to be false
850
+ end
771
851
  end
772
852
  end
773
853
 
774
- let(:context) do
775
- described_class.new(criteria)
854
+ context "when its of type String" do
855
+
856
+ context "when the id exists" do
857
+
858
+ it "returns true" do
859
+ expect(criteria.exists?(hobrecht._id.to_s)).to be true
860
+ end
861
+ end
862
+
863
+ context "when the id does not exist" do
864
+
865
+ it "returns false" do
866
+ expect(criteria.exists?(BSON::ObjectId.new.to_s)).to be false
867
+ end
868
+ end
776
869
  end
870
+ end
871
+
872
+ context "when passing a hash" do
873
+
874
+ context "when calling it on an empty criteria" do
875
+
876
+ it "returns true" do
877
+ expect(criteria.exists?(street: hobrecht.street)).to be true
878
+ end
879
+ end
880
+
881
+ context "when calling it on a criteria that includes the object" do
882
+
883
+ it "returns true" do
884
+ expect(criteria.where(_id: hobrecht._id).exists?(street: hobrecht.street)).to be true
885
+ end
886
+ end
887
+
888
+ context "when calling it on a criteria that does not include the object" do
889
+
890
+ it "returns false" do
891
+ expect(criteria.where(_id: BSON::ObjectId.new).exists?(street: hobrecht.street)).to be false
892
+ end
893
+ end
894
+
895
+ context "when the conditions don't match" do
896
+
897
+ it "returns false" do
898
+ expect(criteria.exists?(street: "bogus")).to be false
899
+ end
900
+ end
901
+ end
902
+
903
+ context "when passing false" do
777
904
 
778
905
  it "returns false" do
779
- expect(context).to_not be_exists
906
+ expect(criteria.exists?(false)).to be false
780
907
  end
781
908
  end
782
909
 
783
- context 'when there is a collation on the criteria' do
910
+ context "when passing nil" do
784
911
 
785
- let(:criteria) do
786
- Address.where(street: "pfluger").tap do |crit|
787
- crit.documents = [ hobrecht, friedel ]
788
- end.collation(locale: 'en_US', strength: 2)
912
+ it "returns false" do
913
+ expect(criteria.exists?(nil)).to be false
789
914
  end
915
+ end
790
916
 
791
- it "raises an exception" do
792
- expect {
793
- context
794
- }.to raise_exception(Mongoid::Errors::InMemoryCollationNotSupported)
917
+ context "when the limit is 0" do
918
+
919
+ it "returns false" do
920
+ expect(criteria.limit(0).exists?).to be false
795
921
  end
796
922
  end
797
923
  end
@@ -800,6 +926,8 @@ describe Mongoid::Contextual::Memory do
800
926
 
801
927
  describe "##{method}" do
802
928
 
929
+ let(:method) { method }
930
+
803
931
  let(:hobrecht) do
804
932
  Address.new(street: "hobrecht")
805
933
  end
@@ -830,6 +958,8 @@ describe Mongoid::Contextual::Memory do
830
958
  expect(context.send(method, 1)).to eq([ hobrecht ])
831
959
  end
832
960
 
961
+ include_examples "returns nil when no documents"
962
+
833
963
  context 'when there is a collation on the criteria' do
834
964
 
835
965
  let(:criteria) do
@@ -847,8 +977,39 @@ describe Mongoid::Contextual::Memory do
847
977
  end
848
978
  end
849
979
 
980
+ describe "#first!" do
981
+
982
+ let(:method) { :first! }
983
+
984
+ let(:hobrecht) do
985
+ Address.new(street: "hobrecht")
986
+ end
987
+
988
+ let(:friedel) do
989
+ Address.new(street: "friedel")
990
+ end
991
+
992
+ let(:criteria) do
993
+ Address.where(:street.in => [ "hobrecht", "friedel" ]).tap do |crit|
994
+ crit.documents = [ hobrecht, friedel ]
995
+ end
996
+ end
997
+
998
+ let(:context) do
999
+ described_class.new(criteria)
1000
+ end
1001
+
1002
+ it "returns the first matching document" do
1003
+ expect(context.first!).to eq(hobrecht)
1004
+ end
1005
+
1006
+ include_examples "raises an error when no documents"
1007
+ end
1008
+
850
1009
  describe "#take" do
851
1010
 
1011
+ let(:method) { :take }
1012
+
852
1013
  let(:hobrecht) do
853
1014
  Address.new(street: "hobrecht")
854
1015
  end
@@ -879,6 +1040,8 @@ describe Mongoid::Contextual::Memory do
879
1040
  expect(context.take(1)).to eq([ hobrecht ])
880
1041
  end
881
1042
 
1043
+ include_examples "returns nil when no documents"
1044
+
882
1045
  context 'when there is a collation on the criteria' do
883
1046
 
884
1047
  let(:criteria) do
@@ -897,6 +1060,8 @@ describe Mongoid::Contextual::Memory do
897
1060
 
898
1061
  describe "#take!" do
899
1062
 
1063
+ let(:method) { :take! }
1064
+
900
1065
  let(:hobrecht) do
901
1066
  Address.new(street: "hobrecht")
902
1067
  end
@@ -919,19 +1084,7 @@ describe Mongoid::Contextual::Memory do
919
1084
  expect(context.take!).to eq(hobrecht)
920
1085
  end
921
1086
 
922
- context "when the criteria is empty" do
923
- let(:criteria) do
924
- Address.where(street: "bogus").tap do |crit|
925
- crit.documents = []
926
- end
927
- end
928
-
929
- it "raise an error" do
930
- expect do
931
- context.take!
932
- end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Address./)
933
- end
934
- end
1087
+ include_examples "raises an error when no documents"
935
1088
 
936
1089
  context 'when there is a collation on the criteria' do
937
1090
 
@@ -1052,6 +1205,8 @@ describe Mongoid::Contextual::Memory do
1052
1205
 
1053
1206
  describe "#last" do
1054
1207
 
1208
+ let(:method) { :last }
1209
+
1055
1210
  let(:hobrecht) do
1056
1211
  Address.new(street: "hobrecht")
1057
1212
  end
@@ -1082,6 +1237,8 @@ describe Mongoid::Contextual::Memory do
1082
1237
  expect(context.last(1)).to eq([ friedel ])
1083
1238
  end
1084
1239
 
1240
+ include_examples "returns nil when no documents"
1241
+
1085
1242
  context 'when there is a collation on the criteria' do
1086
1243
 
1087
1244
  let(:criteria) do
@@ -1098,6 +1255,102 @@ describe Mongoid::Contextual::Memory do
1098
1255
  end
1099
1256
  end
1100
1257
 
1258
+ describe "#last!" do
1259
+ let(:method) { :last! }
1260
+
1261
+ let(:hobrecht) do
1262
+ Address.new(street: "hobrecht")
1263
+ end
1264
+
1265
+ let(:friedel) do
1266
+ Address.new(street: "friedel")
1267
+ end
1268
+
1269
+ let(:criteria) do
1270
+ Address.where(:street.in => [ "hobrecht", "friedel" ]).tap do |crit|
1271
+ crit.documents = [ hobrecht, friedel ]
1272
+ end
1273
+ end
1274
+
1275
+ let(:context) do
1276
+ described_class.new(criteria)
1277
+ end
1278
+
1279
+ it "returns the last matching document" do
1280
+ expect(context.last!).to eq(friedel)
1281
+ end
1282
+
1283
+ include_examples "raises an error when no documents"
1284
+ end
1285
+
1286
+ [ :second,
1287
+ :third,
1288
+ :fourth,
1289
+ :fifth,
1290
+ :second_to_last,
1291
+ :third_to_last
1292
+ ].each do |meth|
1293
+ describe "##{meth}" do
1294
+ let(:method) { meth }
1295
+
1296
+ let(:addresses) do
1297
+ [
1298
+ Address.new,
1299
+ Address.new,
1300
+ Address.new,
1301
+ Address.new,
1302
+ Address.new,
1303
+ ]
1304
+ end
1305
+
1306
+ let(:criteria) do
1307
+ Address.all.tap do |crit|
1308
+ crit.documents = addresses
1309
+ end
1310
+ end
1311
+
1312
+ let(:context) do
1313
+ described_class.new(criteria)
1314
+ end
1315
+
1316
+ it "returns the matching document" do
1317
+ expect(context.send(method)).to eq(addresses.send(method))
1318
+ end
1319
+
1320
+ include_examples "returns nil when no documents"
1321
+ end
1322
+
1323
+ describe "##{meth}!" do
1324
+ let(:method) { "#{meth}!" }
1325
+
1326
+ let(:addresses) do
1327
+ [
1328
+ Address.new,
1329
+ Address.new,
1330
+ Address.new,
1331
+ Address.new,
1332
+ Address.new,
1333
+ ]
1334
+ end
1335
+
1336
+ let(:criteria) do
1337
+ Address.all.tap do |crit|
1338
+ crit.documents = addresses
1339
+ end
1340
+ end
1341
+
1342
+ let(:context) do
1343
+ described_class.new(criteria)
1344
+ end
1345
+
1346
+ it "returns the matching document" do
1347
+ expect(context.send(method)).to eq(addresses.send(meth))
1348
+ end
1349
+
1350
+ include_examples "raises an error when no documents"
1351
+ end
1352
+ end
1353
+
1101
1354
  [ :length, :size ].each do |method|
1102
1355
 
1103
1356
  describe "##{method}" do
@@ -1243,11 +1496,11 @@ describe Mongoid::Contextual::Memory do
1243
1496
  config_override :legacy_pluck_distinct, true
1244
1497
 
1245
1498
  let(:hobrecht) do
1246
- Address.new(street: "hobrecht")
1499
+ Address.new(street: "hobrecht", number: 213)
1247
1500
  end
1248
1501
 
1249
1502
  let(:friedel) do
1250
- Address.new(street: "friedel")
1503
+ Address.new(street: "friedel", number: 11)
1251
1504
  end
1252
1505
 
1253
1506
  let(:criteria) do
@@ -1267,6 +1520,17 @@ describe Mongoid::Contextual::Memory do
1267
1520
  end
1268
1521
  end
1269
1522
 
1523
+ context "when plucking multiple fields" do
1524
+
1525
+ let!(:plucked) do
1526
+ context.pluck(:street, :number)
1527
+ end
1528
+
1529
+ it "returns the values as an array" do
1530
+ expect(plucked).to eq([ ["hobrecht", 213], ["friedel", 11] ])
1531
+ end
1532
+ end
1533
+
1270
1534
  context "when plucking a mix of empty and non-empty values" do
1271
1535
 
1272
1536
  let(:empty_doc) do
@@ -1296,7 +1560,7 @@ describe Mongoid::Contextual::Memory do
1296
1560
  context.pluck(:foo)
1297
1561
  end
1298
1562
 
1299
- it "returns a empty array" do
1563
+ it "returns an empty array" do
1300
1564
  expect(plucked).to eq([nil, nil])
1301
1565
  end
1302
1566
  end
@@ -1307,7 +1571,7 @@ describe Mongoid::Contextual::Memory do
1307
1571
  context.pluck(:foo, :bar)
1308
1572
  end
1309
1573
 
1310
- it "returns a empty array" do
1574
+ it "returns an empty array" do
1311
1575
  expect(plucked).to eq([[nil, nil], [nil, nil]])
1312
1576
  end
1313
1577
  end
@@ -1529,6 +1793,7 @@ describe Mongoid::Contextual::Memory do
1529
1793
  end
1530
1794
 
1531
1795
  context 'when plucking a localized field' do
1796
+ with_default_i18n_configs
1532
1797
 
1533
1798
  before do
1534
1799
  I18n.locale = :en
@@ -1538,10 +1803,6 @@ describe Mongoid::Contextual::Memory do
1538
1803
  d.save!
1539
1804
  end
1540
1805
 
1541
- after do
1542
- I18n.locale = :en
1543
- end
1544
-
1545
1806
  let(:criteria) do
1546
1807
  Dictionary.all.tap do |crit|
1547
1808
  crit.documents = [ Dictionary.first ]
@@ -1598,17 +1859,11 @@ describe Mongoid::Contextual::Memory do
1598
1859
  end
1599
1860
 
1600
1861
  context 'when fallbacks are enabled with a locale list' do
1601
- with_i18n_fallbacks
1862
+ require_fallbacks
1863
+ with_default_i18n_configs
1602
1864
 
1603
- around(:all) do |example|
1604
- prev_fallbacks = I18n.fallbacks.dup
1865
+ before do
1605
1866
  I18n.fallbacks[:he] = [ :en ]
1606
- example.run
1607
- I18n.fallbacks = prev_fallbacks
1608
- end
1609
-
1610
- after do
1611
- I18n.locale = :en
1612
1867
  end
1613
1868
 
1614
1869
  let(:plucked) do
@@ -1624,6 +1879,8 @@ describe Mongoid::Contextual::Memory do
1624
1879
  end
1625
1880
 
1626
1881
  context "when the localized field is embedded" do
1882
+ with_default_i18n_configs
1883
+
1627
1884
  before do
1628
1885
  p = Passport.new
1629
1886
  I18n.locale = :en
@@ -1634,10 +1891,6 @@ describe Mongoid::Contextual::Memory do
1634
1891
  Person.create!(passport: p, employer_id: 12345)
1635
1892
  end
1636
1893
 
1637
- after do
1638
- I18n.locale = :en
1639
- end
1640
-
1641
1894
  let(:plucked) do
1642
1895
  Person.where(employer_id: 12345).pluck("pass.name").first
1643
1896
  end
@@ -1720,7 +1973,7 @@ describe Mongoid::Contextual::Memory do
1720
1973
  end
1721
1974
  end
1722
1975
 
1723
- context "when tallying deeply nested arrays/embedded associations" do
1976
+ context "when plucking deeply nested arrays/embedded associations" do
1724
1977
 
1725
1978
  let(:criteria) do
1726
1979
  Person.all.tap do |crit|
@@ -1877,6 +2130,8 @@ describe Mongoid::Contextual::Memory do
1877
2130
  end
1878
2131
 
1879
2132
  context "when tallying a localized field" do
2133
+ with_default_i18n_configs
2134
+
1880
2135
  let(:d1) { Dictionary.new(description: 'en1') }
1881
2136
  let(:d2) { Dictionary.new(description: 'en1') }
1882
2137
  let(:d3) { Dictionary.new(description: 'en1') }
@@ -1938,6 +2193,7 @@ describe Mongoid::Contextual::Memory do
1938
2193
  end
1939
2194
 
1940
2195
  context "when tallying an embedded localized field" do
2196
+ with_default_i18n_configs
1941
2197
 
1942
2198
  let(:person1) { Person.create!(addresses: [ address1a, address1b ]) }
1943
2199
  let(:person2) { Person.create!(addresses: [ address2a, address2b ]) }