mongoid 8.0.10 → 8.1.0

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 (212) 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 +18 -67
  6. data/lib/config/locales/en.yml +46 -14
  7. data/lib/mongoid/association/accessors.rb +3 -7
  8. data/lib/mongoid/association/builders.rb +1 -1
  9. data/lib/mongoid/association/eager_loadable.rb +0 -3
  10. data/lib/mongoid/association/embedded/batchable.rb +2 -2
  11. data/lib/mongoid/association/embedded/embedded_in/buildable.rb +2 -2
  12. data/lib/mongoid/association/embedded/embedded_in/proxy.rb +2 -1
  13. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +3 -2
  14. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +6 -6
  15. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +1 -1
  16. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +1 -1
  17. data/lib/mongoid/association/macros.rb +0 -6
  18. data/lib/mongoid/association/nested/one.rb +40 -2
  19. data/lib/mongoid/association/proxy.rb +1 -1
  20. data/lib/mongoid/association/referenced/counter_cache.rb +2 -2
  21. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +1 -1
  22. data/lib/mongoid/association/referenced/has_many/enumerable.rb +6 -23
  23. data/lib/mongoid/association/referenced/has_many/proxy.rb +3 -3
  24. data/lib/mongoid/association/reflections.rb +2 -2
  25. data/lib/mongoid/atomic.rb +7 -16
  26. data/lib/mongoid/attributes/dynamic.rb +1 -1
  27. data/lib/mongoid/attributes/nested.rb +2 -2
  28. data/lib/mongoid/attributes/processing.rb +5 -29
  29. data/lib/mongoid/attributes/projector.rb +1 -1
  30. data/lib/mongoid/attributes/readonly.rb +1 -1
  31. data/lib/mongoid/attributes.rb +8 -2
  32. data/lib/mongoid/changeable.rb +107 -5
  33. data/lib/mongoid/clients/storage_options.rb +2 -5
  34. data/lib/mongoid/clients/validators/storage.rb +1 -13
  35. data/lib/mongoid/collection_configurable.rb +58 -0
  36. data/lib/mongoid/composable.rb +2 -0
  37. data/lib/mongoid/config/defaults.rb +60 -0
  38. data/lib/mongoid/config/options.rb +0 -3
  39. data/lib/mongoid/config/validators/async_query_executor.rb +24 -0
  40. data/lib/mongoid/config/validators.rb +1 -0
  41. data/lib/mongoid/config.rb +88 -27
  42. data/lib/mongoid/contextual/atomic.rb +1 -1
  43. data/lib/mongoid/contextual/memory.rb +233 -33
  44. data/lib/mongoid/contextual/mongo/documents_loader.rb +177 -0
  45. data/lib/mongoid/contextual/mongo.rb +370 -133
  46. data/lib/mongoid/contextual/none.rb +162 -7
  47. data/lib/mongoid/contextual.rb +12 -0
  48. data/lib/mongoid/criteria/findable.rb +2 -2
  49. data/lib/mongoid/criteria/includable.rb +4 -3
  50. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -15
  51. data/lib/mongoid/criteria/queryable/key.rb +1 -1
  52. data/lib/mongoid/criteria/queryable/mergeable.rb +1 -1
  53. data/lib/mongoid/criteria/queryable/optional.rb +8 -8
  54. data/lib/mongoid/criteria/queryable/selectable.rb +43 -12
  55. data/lib/mongoid/criteria/queryable/selector.rb +1 -1
  56. data/lib/mongoid/criteria/queryable/storable.rb +1 -1
  57. data/lib/mongoid/criteria.rb +6 -5
  58. data/lib/mongoid/deprecable.rb +1 -2
  59. data/lib/mongoid/deprecation.rb +3 -3
  60. data/lib/mongoid/document.rb +1 -8
  61. data/lib/mongoid/errors/create_collection_failure.rb +33 -0
  62. data/lib/mongoid/errors/drop_collection_failure.rb +27 -0
  63. data/lib/mongoid/errors/immutable_attribute.rb +26 -0
  64. data/lib/mongoid/errors/invalid_async_query_executor.rb +25 -0
  65. data/lib/mongoid/errors/invalid_global_executor_concurrency.rb +22 -0
  66. data/lib/mongoid/errors/invalid_storage_parent.rb +2 -0
  67. data/lib/mongoid/errors.rb +4 -1
  68. data/lib/mongoid/extensions/hash.rb +2 -24
  69. data/lib/mongoid/extensions/object.rb +2 -2
  70. data/lib/mongoid/extensions/time.rb +2 -0
  71. data/lib/mongoid/fields/localized.rb +10 -0
  72. data/lib/mongoid/fields/standard.rb +10 -0
  73. data/lib/mongoid/fields.rb +59 -35
  74. data/lib/mongoid/findable.rb +27 -3
  75. data/lib/mongoid/interceptable.rb +6 -116
  76. data/lib/mongoid/matcher/eq_impl.rb +1 -1
  77. data/lib/mongoid/matcher/type.rb +1 -1
  78. data/lib/mongoid/persistable/creatable.rb +1 -0
  79. data/lib/mongoid/persistable/deletable.rb +1 -1
  80. data/lib/mongoid/persistable/savable.rb +13 -1
  81. data/lib/mongoid/persistable/unsettable.rb +2 -2
  82. data/lib/mongoid/persistable/updatable.rb +51 -1
  83. data/lib/mongoid/persistable/upsertable.rb +20 -1
  84. data/lib/mongoid/persistable.rb +3 -0
  85. data/lib/mongoid/query_cache.rb +5 -1
  86. data/lib/mongoid/railties/database.rake +7 -2
  87. data/lib/mongoid/reloadable.rb +5 -3
  88. data/lib/mongoid/stateful.rb +22 -1
  89. data/lib/mongoid/tasks/database.rake +12 -0
  90. data/lib/mongoid/tasks/database.rb +20 -0
  91. data/lib/mongoid/timestamps/created.rb +1 -8
  92. data/lib/mongoid/traversable.rb +0 -12
  93. data/lib/mongoid/utils.rb +22 -0
  94. data/lib/mongoid/validatable/associated.rb +17 -98
  95. data/lib/mongoid/validatable/macros.rb +5 -5
  96. data/lib/mongoid/validatable.rb +4 -9
  97. data/lib/mongoid/version.rb +1 -1
  98. data/lib/mongoid/warnings.rb +17 -1
  99. data/lib/mongoid.rb +16 -3
  100. data/spec/integration/app_spec.rb +2 -6
  101. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +0 -40
  102. data/spec/integration/callbacks_spec.rb +99 -12
  103. data/spec/integration/discriminator_key_spec.rb +4 -5
  104. data/spec/integration/i18n_fallbacks_spec.rb +3 -2
  105. data/spec/mongoid/association/eager_spec.rb +2 -24
  106. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +27 -0
  107. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +20 -25
  108. data/spec/mongoid/association/embedded/embeds_many_models.rb +1 -0
  109. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +0 -4
  110. data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +15 -2
  111. data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -18
  112. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +42 -55
  113. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +9 -50
  114. data/spec/mongoid/association/syncable_spec.rb +1 -1
  115. data/spec/mongoid/association_spec.rb +0 -60
  116. data/spec/mongoid/attributes_spec.rb +3 -33
  117. data/spec/mongoid/changeable_spec.rb +299 -24
  118. data/spec/mongoid/clients_spec.rb +122 -13
  119. data/spec/mongoid/collection_configurable_spec.rb +158 -0
  120. data/spec/mongoid/config/defaults_spec.rb +160 -0
  121. data/spec/mongoid/config_spec.rb +154 -27
  122. data/spec/mongoid/contextual/memory_spec.rb +332 -76
  123. data/spec/mongoid/contextual/mongo/documents_loader_spec.rb +187 -0
  124. data/spec/mongoid/contextual/mongo_spec.rb +1009 -125
  125. data/spec/mongoid/contextual/none_spec.rb +49 -2
  126. data/spec/mongoid/copyable_spec.rb +2 -10
  127. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -10
  128. data/spec/mongoid/criteria/queryable/options_spec.rb +1 -1
  129. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +419 -0
  130. data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -1
  131. data/spec/mongoid/criteria/queryable/selector_spec.rb +3 -76
  132. data/spec/mongoid/criteria/queryable/storable_spec.rb +0 -72
  133. data/spec/mongoid/criteria_projection_spec.rb +1 -4
  134. data/spec/mongoid/criteria_spec.rb +5 -9
  135. data/spec/mongoid/document_spec.rb +0 -27
  136. data/spec/mongoid/errors/readonly_document_spec.rb +2 -2
  137. data/spec/mongoid/extensions/hash_spec.rb +3 -3
  138. data/spec/mongoid/extensions/time_spec.rb +8 -43
  139. data/spec/mongoid/extensions/time_with_zone_spec.rb +7 -52
  140. data/spec/mongoid/fields/localized_spec.rb +46 -28
  141. data/spec/mongoid/fields_spec.rb +136 -77
  142. data/spec/mongoid/findable_spec.rb +391 -34
  143. data/spec/mongoid/indexable_spec.rb +16 -10
  144. data/spec/mongoid/interceptable_spec.rb +153 -442
  145. data/spec/mongoid/interceptable_spec_models.rb +111 -51
  146. data/spec/mongoid/persistable/deletable_spec.rb +26 -6
  147. data/spec/mongoid/persistable/destroyable_spec.rb +26 -6
  148. data/spec/mongoid/persistable/incrementable_spec.rb +37 -0
  149. data/spec/mongoid/persistable/logical_spec.rb +37 -0
  150. data/spec/mongoid/persistable/poppable_spec.rb +36 -0
  151. data/spec/mongoid/persistable/pullable_spec.rb +72 -0
  152. data/spec/mongoid/persistable/pushable_spec.rb +72 -0
  153. data/spec/mongoid/persistable/renamable_spec.rb +36 -0
  154. data/spec/mongoid/persistable/savable_spec.rb +96 -0
  155. data/spec/mongoid/persistable/settable_spec.rb +37 -0
  156. data/spec/mongoid/persistable/unsettable_spec.rb +36 -0
  157. data/spec/mongoid/persistable/updatable_spec.rb +20 -28
  158. data/spec/mongoid/persistable/upsertable_spec.rb +80 -6
  159. data/spec/mongoid/persistence_context_spec.rb +7 -57
  160. data/spec/mongoid/query_cache_spec.rb +56 -61
  161. data/spec/mongoid/reloadable_spec.rb +24 -28
  162. data/spec/mongoid/scopable_spec.rb +70 -0
  163. data/spec/mongoid/serializable_spec.rb +23 -44
  164. data/spec/mongoid/stateful_spec.rb +122 -8
  165. data/spec/mongoid/tasks/database_rake_spec.rb +74 -0
  166. data/spec/mongoid/tasks/database_spec.rb +127 -0
  167. data/spec/mongoid/timestamps/created_spec.rb +0 -23
  168. data/spec/mongoid/timestamps_spec.rb +9 -11
  169. data/spec/mongoid/touchable_spec.rb +277 -5
  170. data/spec/mongoid/touchable_spec_models.rb +3 -1
  171. data/spec/mongoid/traversable_spec.rb +9 -24
  172. data/spec/mongoid/validatable/associated_spec.rb +34 -27
  173. data/spec/mongoid/validatable/uniqueness_spec.rb +2 -3
  174. data/spec/mongoid_spec.rb +36 -10
  175. data/spec/shared/LICENSE +20 -0
  176. data/spec/shared/bin/get-mongodb-download-url +17 -0
  177. data/spec/shared/bin/s3-copy +45 -0
  178. data/spec/shared/bin/s3-upload +69 -0
  179. data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
  180. data/spec/shared/lib/mrss/cluster_config.rb +231 -0
  181. data/spec/shared/lib/mrss/constraints.rb +378 -0
  182. data/spec/shared/lib/mrss/docker_runner.rb +298 -0
  183. data/spec/shared/lib/mrss/eg_config_utils.rb +51 -0
  184. data/spec/shared/lib/mrss/event_subscriber.rb +210 -0
  185. data/spec/shared/lib/mrss/lite_constraints.rb +238 -0
  186. data/spec/shared/lib/mrss/server_version_registry.rb +113 -0
  187. data/spec/shared/lib/mrss/session_registry.rb +69 -0
  188. data/spec/shared/lib/mrss/session_registry_legacy.rb +60 -0
  189. data/spec/shared/lib/mrss/spec_organizer.rb +179 -0
  190. data/spec/shared/lib/mrss/utils.rb +37 -0
  191. data/spec/shared/share/Dockerfile.erb +321 -0
  192. data/spec/shared/share/haproxy-1.conf +16 -0
  193. data/spec/shared/share/haproxy-2.conf +17 -0
  194. data/spec/shared/shlib/config.sh +27 -0
  195. data/spec/shared/shlib/distro.sh +74 -0
  196. data/spec/shared/shlib/server.sh +416 -0
  197. data/spec/shared/shlib/set_env.sh +169 -0
  198. data/spec/spec_helper.rb +5 -0
  199. data/spec/support/immutable_ids.rb +118 -0
  200. data/spec/support/macros.rb +47 -15
  201. data/spec/support/models/artist.rb +0 -1
  202. data/spec/support/models/band.rb +1 -0
  203. data/spec/support/models/building.rb +2 -0
  204. data/spec/support/models/name.rb +0 -10
  205. data/spec/support/models/person.rb +0 -1
  206. data/spec/support/models/product.rb +1 -0
  207. data.tar.gz.sig +0 -0
  208. metadata +745 -637
  209. metadata.gz.sig +2 -0
  210. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -60
  211. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -60
  212. 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 ]) }