mongoid 8.0.13 → 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 (225) 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 +17 -66
  6. data/lib/config/locales/en.yml +46 -40
  7. data/lib/mongoid/association/accessors.rb +3 -7
  8. data/lib/mongoid/association/builders.rb +1 -1
  9. data/lib/mongoid/association/depending.rb +4 -8
  10. data/lib/mongoid/association/eager_loadable.rb +0 -3
  11. data/lib/mongoid/association/embedded/batchable.rb +2 -2
  12. data/lib/mongoid/association/embedded/embedded_in/buildable.rb +2 -2
  13. data/lib/mongoid/association/embedded/embedded_in/proxy.rb +2 -1
  14. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +3 -2
  15. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +6 -6
  16. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +1 -1
  17. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +2 -2
  18. data/lib/mongoid/association/macros.rb +0 -6
  19. data/lib/mongoid/association/nested/many.rb +3 -38
  20. data/lib/mongoid/association/nested/nested_buildable.rb +0 -14
  21. data/lib/mongoid/association/nested/one.rb +40 -2
  22. data/lib/mongoid/association/proxy.rb +1 -1
  23. data/lib/mongoid/association/referenced/counter_cache.rb +2 -2
  24. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +1 -1
  25. data/lib/mongoid/association/referenced/has_many/enumerable.rb +6 -23
  26. data/lib/mongoid/association/referenced/has_many/proxy.rb +4 -103
  27. data/lib/mongoid/association/reflections.rb +2 -2
  28. data/lib/mongoid/atomic.rb +7 -16
  29. data/lib/mongoid/attributes/dynamic.rb +1 -1
  30. data/lib/mongoid/attributes/nested.rb +2 -2
  31. data/lib/mongoid/attributes/processing.rb +5 -29
  32. data/lib/mongoid/attributes/projector.rb +1 -1
  33. data/lib/mongoid/attributes/readonly.rb +1 -1
  34. data/lib/mongoid/attributes.rb +8 -2
  35. data/lib/mongoid/changeable.rb +107 -5
  36. data/lib/mongoid/clients/factory.rb +0 -4
  37. data/lib/mongoid/clients/storage_options.rb +2 -5
  38. data/lib/mongoid/clients/validators/storage.rb +1 -13
  39. data/lib/mongoid/collection_configurable.rb +58 -0
  40. data/lib/mongoid/composable.rb +2 -0
  41. data/lib/mongoid/config/defaults.rb +60 -0
  42. data/lib/mongoid/config/options.rb +0 -3
  43. data/lib/mongoid/config/validators/async_query_executor.rb +24 -0
  44. data/lib/mongoid/config/validators.rb +1 -0
  45. data/lib/mongoid/config.rb +88 -89
  46. data/lib/mongoid/contextual/aggregable/memory.rb +2 -5
  47. data/lib/mongoid/contextual/atomic.rb +1 -1
  48. data/lib/mongoid/contextual/memory.rb +240 -51
  49. data/lib/mongoid/contextual/mongo/documents_loader.rb +177 -0
  50. data/lib/mongoid/contextual/mongo.rb +370 -133
  51. data/lib/mongoid/contextual/none.rb +162 -7
  52. data/lib/mongoid/contextual.rb +12 -0
  53. data/lib/mongoid/criteria/findable.rb +2 -2
  54. data/lib/mongoid/criteria/includable.rb +4 -3
  55. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -15
  56. data/lib/mongoid/criteria/queryable/key.rb +1 -1
  57. data/lib/mongoid/criteria/queryable/mergeable.rb +1 -13
  58. data/lib/mongoid/criteria/queryable/optional.rb +8 -8
  59. data/lib/mongoid/criteria/queryable/selectable.rb +43 -121
  60. data/lib/mongoid/criteria/queryable/selector.rb +1 -1
  61. data/lib/mongoid/criteria/queryable/storable.rb +1 -1
  62. data/lib/mongoid/criteria.rb +6 -5
  63. data/lib/mongoid/deprecable.rb +1 -2
  64. data/lib/mongoid/deprecation.rb +3 -3
  65. data/lib/mongoid/document.rb +1 -8
  66. data/lib/mongoid/errors/create_collection_failure.rb +33 -0
  67. data/lib/mongoid/errors/drop_collection_failure.rb +27 -0
  68. data/lib/mongoid/errors/immutable_attribute.rb +26 -0
  69. data/lib/mongoid/errors/invalid_async_query_executor.rb +25 -0
  70. data/lib/mongoid/errors/invalid_global_executor_concurrency.rb +22 -0
  71. data/lib/mongoid/errors/invalid_storage_parent.rb +2 -0
  72. data/lib/mongoid/errors.rb +4 -2
  73. data/lib/mongoid/extensions/hash.rb +3 -51
  74. data/lib/mongoid/extensions/object.rb +2 -2
  75. data/lib/mongoid/extensions/time.rb +2 -0
  76. data/lib/mongoid/fields/localized.rb +10 -0
  77. data/lib/mongoid/fields/standard.rb +10 -0
  78. data/lib/mongoid/fields.rb +59 -35
  79. data/lib/mongoid/findable.rb +27 -3
  80. data/lib/mongoid/interceptable.rb +6 -116
  81. data/lib/mongoid/matchable.rb +1 -6
  82. data/lib/mongoid/matcher/eq_impl.rb +1 -1
  83. data/lib/mongoid/matcher/eq_impl_with_regexp.rb +5 -3
  84. data/lib/mongoid/matcher/regex.rb +9 -8
  85. data/lib/mongoid/matcher/type.rb +1 -1
  86. data/lib/mongoid/matcher.rb +0 -1
  87. data/lib/mongoid/persistable/creatable.rb +1 -0
  88. data/lib/mongoid/persistable/deletable.rb +1 -1
  89. data/lib/mongoid/persistable/savable.rb +13 -1
  90. data/lib/mongoid/persistable/unsettable.rb +2 -2
  91. data/lib/mongoid/persistable/updatable.rb +51 -1
  92. data/lib/mongoid/persistable/upsertable.rb +20 -1
  93. data/lib/mongoid/persistable.rb +3 -0
  94. data/lib/mongoid/query_cache.rb +5 -1
  95. data/lib/mongoid/railties/database.rake +7 -2
  96. data/lib/mongoid/reloadable.rb +5 -3
  97. data/lib/mongoid/stateful.rb +22 -1
  98. data/lib/mongoid/tasks/database.rake +12 -0
  99. data/lib/mongoid/tasks/database.rb +20 -0
  100. data/lib/mongoid/threaded.rb +0 -3
  101. data/lib/mongoid/timestamps/created.rb +1 -8
  102. data/lib/mongoid/traversable.rb +0 -12
  103. data/lib/mongoid/utils.rb +22 -0
  104. data/lib/mongoid/validatable/associated.rb +17 -98
  105. data/lib/mongoid/validatable/macros.rb +5 -20
  106. data/lib/mongoid/validatable.rb +4 -10
  107. data/lib/mongoid/version.rb +1 -5
  108. data/lib/mongoid/warnings.rb +17 -2
  109. data/lib/mongoid.rb +16 -3
  110. data/spec/integration/app_spec.rb +2 -20
  111. data/spec/integration/associations/embeds_one_spec.rb +5 -25
  112. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +0 -55
  113. data/spec/integration/callbacks_spec.rb +99 -12
  114. data/spec/integration/discriminator_key_spec.rb +4 -5
  115. data/spec/integration/dots_and_dollars_spec.rb +2 -12
  116. data/spec/integration/i18n_fallbacks_spec.rb +3 -2
  117. data/spec/integration/matcher_operator_data/regex.yml +0 -21
  118. data/spec/mongoid/association/eager_spec.rb +2 -24
  119. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +27 -0
  120. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +20 -25
  121. data/spec/mongoid/association/embedded/embeds_many_models.rb +1 -0
  122. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +0 -4
  123. data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +15 -2
  124. data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -18
  125. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +42 -103
  126. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +9 -195
  127. data/spec/mongoid/association/syncable_spec.rb +1 -1
  128. data/spec/mongoid/attributes/nested_spec.rb +10 -204
  129. data/spec/mongoid/attributes_spec.rb +3 -33
  130. data/spec/mongoid/changeable_spec.rb +299 -24
  131. data/spec/mongoid/clients/factory_spec.rb +0 -30
  132. data/spec/mongoid/clients_spec.rb +122 -13
  133. data/spec/mongoid/collection_configurable_spec.rb +158 -0
  134. data/spec/mongoid/config/defaults_spec.rb +160 -0
  135. data/spec/mongoid/config_spec.rb +154 -27
  136. data/spec/mongoid/contextual/aggregable/memory_spec.rb +0 -91
  137. data/spec/mongoid/contextual/memory_spec.rb +332 -253
  138. data/spec/mongoid/contextual/mongo/documents_loader_spec.rb +187 -0
  139. data/spec/mongoid/contextual/mongo_spec.rb +1009 -125
  140. data/spec/mongoid/contextual/none_spec.rb +49 -2
  141. data/spec/mongoid/copyable_spec.rb +2 -10
  142. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -10
  143. data/spec/mongoid/criteria/queryable/options_spec.rb +1 -1
  144. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +419 -2
  145. data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -1
  146. data/spec/mongoid/criteria/queryable/selectable_where_spec.rb +0 -235
  147. data/spec/mongoid/criteria/queryable/selector_spec.rb +3 -76
  148. data/spec/mongoid/criteria/queryable/storable_spec.rb +0 -72
  149. data/spec/mongoid/criteria_projection_spec.rb +1 -4
  150. data/spec/mongoid/criteria_spec.rb +5 -11
  151. data/spec/mongoid/document_spec.rb +0 -27
  152. data/spec/mongoid/errors/readonly_document_spec.rb +2 -2
  153. data/spec/mongoid/extensions/hash_spec.rb +3 -239
  154. data/spec/mongoid/extensions/time_spec.rb +8 -43
  155. data/spec/mongoid/extensions/time_with_zone_spec.rb +7 -52
  156. data/spec/mongoid/fields/localized_spec.rb +46 -28
  157. data/spec/mongoid/fields_spec.rb +136 -77
  158. data/spec/mongoid/findable_spec.rb +391 -34
  159. data/spec/mongoid/indexable_spec.rb +16 -10
  160. data/spec/mongoid/interceptable_spec.rb +153 -442
  161. data/spec/mongoid/interceptable_spec_models.rb +111 -51
  162. data/spec/mongoid/persistable/deletable_spec.rb +26 -6
  163. data/spec/mongoid/persistable/destroyable_spec.rb +26 -6
  164. data/spec/mongoid/persistable/incrementable_spec.rb +37 -0
  165. data/spec/mongoid/persistable/logical_spec.rb +37 -0
  166. data/spec/mongoid/persistable/poppable_spec.rb +36 -0
  167. data/spec/mongoid/persistable/pullable_spec.rb +72 -0
  168. data/spec/mongoid/persistable/pushable_spec.rb +72 -0
  169. data/spec/mongoid/persistable/renamable_spec.rb +36 -0
  170. data/spec/mongoid/persistable/savable_spec.rb +96 -0
  171. data/spec/mongoid/persistable/settable_spec.rb +37 -0
  172. data/spec/mongoid/persistable/unsettable_spec.rb +36 -0
  173. data/spec/mongoid/persistable/updatable_spec.rb +20 -28
  174. data/spec/mongoid/persistable/upsertable_spec.rb +80 -6
  175. data/spec/mongoid/persistence_context_spec.rb +7 -57
  176. data/spec/mongoid/query_cache_spec.rb +56 -61
  177. data/spec/mongoid/reloadable_spec.rb +24 -28
  178. data/spec/mongoid/scopable_spec.rb +70 -0
  179. data/spec/mongoid/serializable_spec.rb +23 -44
  180. data/spec/mongoid/stateful_spec.rb +122 -8
  181. data/spec/mongoid/tasks/database_rake_spec.rb +74 -0
  182. data/spec/mongoid/tasks/database_spec.rb +127 -0
  183. data/spec/mongoid/timestamps/created_spec.rb +0 -23
  184. data/spec/mongoid/timestamps_spec.rb +9 -11
  185. data/spec/mongoid/touchable_spec.rb +277 -5
  186. data/spec/mongoid/touchable_spec_models.rb +3 -1
  187. data/spec/mongoid/traversable_spec.rb +9 -24
  188. data/spec/mongoid/validatable/associated_spec.rb +34 -27
  189. data/spec/mongoid/validatable/numericality_spec.rb +0 -16
  190. data/spec/mongoid/validatable/uniqueness_spec.rb +2 -3
  191. data/spec/mongoid_spec.rb +36 -10
  192. data/spec/shared/lib/mrss/docker_runner.rb +1 -1
  193. data/spec/shared/lib/mrss/server_version_registry.rb +1 -1
  194. data/spec/shared/lib/mrss/spec_organizer.rb +3 -32
  195. data/spec/shared/share/Dockerfile.erb +85 -15
  196. data/spec/shared/shlib/distro.sh +0 -10
  197. data/spec/shared/shlib/server.sh +27 -34
  198. data/spec/shared/shlib/set_env.sh +68 -9
  199. data/spec/spec_helper.rb +5 -0
  200. data/spec/support/expectations.rb +17 -20
  201. data/spec/support/immutable_ids.rb +118 -0
  202. data/spec/support/macros.rb +47 -15
  203. data/spec/support/models/artist.rb +0 -1
  204. data/spec/support/models/band.rb +1 -0
  205. data/spec/support/models/building.rb +2 -0
  206. data/spec/support/models/name.rb +0 -10
  207. data/spec/support/models/person.rb +0 -1
  208. data/spec/support/models/product.rb +1 -0
  209. data.tar.gz.sig +0 -0
  210. metadata +715 -671
  211. metadata.gz.sig +2 -0
  212. data/lib/mongoid/errors/in_memory_regexp_timeout.rb +0 -26
  213. data/lib/mongoid/field_readable.rb +0 -70
  214. data/lib/mongoid/matcher/regexp_budget.rb +0 -389
  215. data/lib/mongoid/validatable/numericality.rb +0 -19
  216. data/spec/integration/matcher_regexp_timeout_spec.rb +0 -215
  217. data/spec/integration/query_operator_guard_spec.rb +0 -89
  218. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -61
  219. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -61
  220. data/spec/mongoid/matcher/regexp_budget_spec.rb +0 -570
  221. data/spec/shared/CANDIDATE.md +0 -28
  222. data/spec/shared/lib/mrss/release/candidate.rb +0 -281
  223. data/spec/shared/lib/mrss/release/product_data.rb +0 -144
  224. data/spec/shared/lib/tasks/candidate.rake +0 -64
  225. 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 ]
@@ -633,27 +653,6 @@ describe Mongoid::Contextual::Memory do
633
653
  end
634
654
  end
635
655
  end
636
-
637
- context 'when the field name is a method name' do
638
- let!(:person) do
639
- Person.create!(ssn: 'secret-ssn')
640
- end
641
-
642
- let!(:address) do
643
- person.addresses.create!(street: 'hobrecht')
644
- end
645
-
646
- let(:criteria) do
647
- Address.all.tap do |crit|
648
- crit.documents = [ address ]
649
- end
650
- end
651
-
652
- it 'does not call the method' do
653
- expect(address).not_to receive(:destroy)
654
- expect(context.distinct(:destroy)).to eq([ nil ])
655
- end
656
- end
657
656
  end
658
657
 
659
658
  describe "#each" do
@@ -767,52 +766,158 @@ describe Mongoid::Contextual::Memory do
767
766
  Address.new(street: "friedel")
768
767
  end
769
768
 
770
- 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
771
774
 
772
- let(:criteria) do
773
- Address.where(street: "hobrecht").tap do |crit|
774
- 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
775
785
  end
776
786
  end
777
787
 
778
- let(:context) do
779
- 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
780
803
  end
781
804
 
782
- it "returns true" do
783
- 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
784
818
  end
785
819
  end
786
820
 
787
- context "when there are no matching documents" do
821
+ context "when passing an _id" do
788
822
 
789
- let(:criteria) do
790
- Address.where(street: "pfluger").tap do |crit|
791
- 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
792
851
  end
793
852
  end
794
853
 
795
- let(:context) do
796
- 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
797
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
798
904
 
799
905
  it "returns false" do
800
- expect(context).to_not be_exists
906
+ expect(criteria.exists?(false)).to be false
801
907
  end
802
908
  end
803
909
 
804
- context 'when there is a collation on the criteria' do
910
+ context "when passing nil" do
805
911
 
806
- let(:criteria) do
807
- Address.where(street: "pfluger").tap do |crit|
808
- crit.documents = [ hobrecht, friedel ]
809
- end.collation(locale: 'en_US', strength: 2)
912
+ it "returns false" do
913
+ expect(criteria.exists?(nil)).to be false
810
914
  end
915
+ end
811
916
 
812
- it "raises an exception" do
813
- expect {
814
- context
815
- }.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
816
921
  end
817
922
  end
818
923
  end
@@ -821,6 +926,8 @@ describe Mongoid::Contextual::Memory do
821
926
 
822
927
  describe "##{method}" do
823
928
 
929
+ let(:method) { method }
930
+
824
931
  let(:hobrecht) do
825
932
  Address.new(street: "hobrecht")
826
933
  end
@@ -851,6 +958,8 @@ describe Mongoid::Contextual::Memory do
851
958
  expect(context.send(method, 1)).to eq([ hobrecht ])
852
959
  end
853
960
 
961
+ include_examples "returns nil when no documents"
962
+
854
963
  context 'when there is a collation on the criteria' do
855
964
 
856
965
  let(:criteria) do
@@ -868,8 +977,39 @@ describe Mongoid::Contextual::Memory do
868
977
  end
869
978
  end
870
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
+
871
1009
  describe "#take" do
872
1010
 
1011
+ let(:method) { :take }
1012
+
873
1013
  let(:hobrecht) do
874
1014
  Address.new(street: "hobrecht")
875
1015
  end
@@ -900,6 +1040,8 @@ describe Mongoid::Contextual::Memory do
900
1040
  expect(context.take(1)).to eq([ hobrecht ])
901
1041
  end
902
1042
 
1043
+ include_examples "returns nil when no documents"
1044
+
903
1045
  context 'when there is a collation on the criteria' do
904
1046
 
905
1047
  let(:criteria) do
@@ -918,6 +1060,8 @@ describe Mongoid::Contextual::Memory do
918
1060
 
919
1061
  describe "#take!" do
920
1062
 
1063
+ let(:method) { :take! }
1064
+
921
1065
  let(:hobrecht) do
922
1066
  Address.new(street: "hobrecht")
923
1067
  end
@@ -940,19 +1084,7 @@ describe Mongoid::Contextual::Memory do
940
1084
  expect(context.take!).to eq(hobrecht)
941
1085
  end
942
1086
 
943
- context "when the criteria is empty" do
944
- let(:criteria) do
945
- Address.where(street: "bogus").tap do |crit|
946
- crit.documents = []
947
- end
948
- end
949
-
950
- it "raise an error" do
951
- expect do
952
- context.take!
953
- end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Address./)
954
- end
955
- end
1087
+ include_examples "raises an error when no documents"
956
1088
 
957
1089
  context 'when there is a collation on the criteria' do
958
1090
 
@@ -1073,6 +1205,8 @@ describe Mongoid::Contextual::Memory do
1073
1205
 
1074
1206
  describe "#last" do
1075
1207
 
1208
+ let(:method) { :last }
1209
+
1076
1210
  let(:hobrecht) do
1077
1211
  Address.new(street: "hobrecht")
1078
1212
  end
@@ -1103,6 +1237,8 @@ describe Mongoid::Contextual::Memory do
1103
1237
  expect(context.last(1)).to eq([ friedel ])
1104
1238
  end
1105
1239
 
1240
+ include_examples "returns nil when no documents"
1241
+
1106
1242
  context 'when there is a collation on the criteria' do
1107
1243
 
1108
1244
  let(:criteria) do
@@ -1119,6 +1255,102 @@ describe Mongoid::Contextual::Memory do
1119
1255
  end
1120
1256
  end
1121
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
+
1122
1354
  [ :length, :size ].each do |method|
1123
1355
 
1124
1356
  describe "##{method}" do
@@ -1264,11 +1496,11 @@ describe Mongoid::Contextual::Memory do
1264
1496
  config_override :legacy_pluck_distinct, true
1265
1497
 
1266
1498
  let(:hobrecht) do
1267
- Address.new(street: "hobrecht")
1499
+ Address.new(street: "hobrecht", number: 213)
1268
1500
  end
1269
1501
 
1270
1502
  let(:friedel) do
1271
- Address.new(street: "friedel")
1503
+ Address.new(street: "friedel", number: 11)
1272
1504
  end
1273
1505
 
1274
1506
  let(:criteria) do
@@ -1288,6 +1520,17 @@ describe Mongoid::Contextual::Memory do
1288
1520
  end
1289
1521
  end
1290
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
+
1291
1534
  context "when plucking a mix of empty and non-empty values" do
1292
1535
 
1293
1536
  let(:empty_doc) do
@@ -1317,7 +1560,7 @@ describe Mongoid::Contextual::Memory do
1317
1560
  context.pluck(:foo)
1318
1561
  end
1319
1562
 
1320
- it "returns a empty array" do
1563
+ it "returns an empty array" do
1321
1564
  expect(plucked).to eq([nil, nil])
1322
1565
  end
1323
1566
  end
@@ -1328,7 +1571,7 @@ describe Mongoid::Contextual::Memory do
1328
1571
  context.pluck(:foo, :bar)
1329
1572
  end
1330
1573
 
1331
- it "returns a empty array" do
1574
+ it "returns an empty array" do
1332
1575
  expect(plucked).to eq([[nil, nil], [nil, nil]])
1333
1576
  end
1334
1577
  end
@@ -1550,6 +1793,7 @@ describe Mongoid::Contextual::Memory do
1550
1793
  end
1551
1794
 
1552
1795
  context 'when plucking a localized field' do
1796
+ with_default_i18n_configs
1553
1797
 
1554
1798
  before do
1555
1799
  I18n.locale = :en
@@ -1559,10 +1803,6 @@ describe Mongoid::Contextual::Memory do
1559
1803
  d.save!
1560
1804
  end
1561
1805
 
1562
- after do
1563
- I18n.locale = :en
1564
- end
1565
-
1566
1806
  let(:criteria) do
1567
1807
  Dictionary.all.tap do |crit|
1568
1808
  crit.documents = [ Dictionary.first ]
@@ -1619,17 +1859,11 @@ describe Mongoid::Contextual::Memory do
1619
1859
  end
1620
1860
 
1621
1861
  context 'when fallbacks are enabled with a locale list' do
1622
- with_i18n_fallbacks
1862
+ require_fallbacks
1863
+ with_default_i18n_configs
1623
1864
 
1624
- around(:all) do |example|
1625
- prev_fallbacks = I18n.fallbacks.dup
1865
+ before do
1626
1866
  I18n.fallbacks[:he] = [ :en ]
1627
- example.run
1628
- I18n.fallbacks = prev_fallbacks
1629
- end
1630
-
1631
- after do
1632
- I18n.locale = :en
1633
1867
  end
1634
1868
 
1635
1869
  let(:plucked) do
@@ -1645,6 +1879,8 @@ describe Mongoid::Contextual::Memory do
1645
1879
  end
1646
1880
 
1647
1881
  context "when the localized field is embedded" do
1882
+ with_default_i18n_configs
1883
+
1648
1884
  before do
1649
1885
  p = Passport.new
1650
1886
  I18n.locale = :en
@@ -1655,10 +1891,6 @@ describe Mongoid::Contextual::Memory do
1655
1891
  Person.create!(passport: p, employer_id: 12345)
1656
1892
  end
1657
1893
 
1658
- after do
1659
- I18n.locale = :en
1660
- end
1661
-
1662
1894
  let(:plucked) do
1663
1895
  Person.where(employer_id: 12345).pluck("pass.name").first
1664
1896
  end
@@ -1741,7 +1973,7 @@ describe Mongoid::Contextual::Memory do
1741
1973
  end
1742
1974
  end
1743
1975
 
1744
- context "when tallying deeply nested arrays/embedded associations" do
1976
+ context "when plucking deeply nested arrays/embedded associations" do
1745
1977
 
1746
1978
  let(:criteria) do
1747
1979
  Person.all.tap do |crit|
@@ -1764,116 +1996,6 @@ describe Mongoid::Contextual::Memory do
1764
1996
  end
1765
1997
  end
1766
1998
  end
1767
-
1768
- context 'when plucking a dynamic attribute' do
1769
- let(:criteria) do
1770
- Band.all.tap do |crit|
1771
- crit.documents = [ Band.create!(name: 'Depeche Mode', mood: 'dark') ]
1772
- end
1773
- end
1774
-
1775
- it 'returns the value from the attributes' do
1776
- expect(context.pluck(:mood)).to eq([ 'dark' ])
1777
- end
1778
- end
1779
-
1780
- context 'when the field name is a method name' do
1781
- let!(:person) do
1782
- Person.create!(ssn: 'secret-ssn')
1783
- end
1784
-
1785
- let!(:address) do
1786
- person.addresses.create!(street: 'hobrecht')
1787
- end
1788
-
1789
- let(:criteria) do
1790
- Address.all.tap do |crit|
1791
- crit.documents = [ address ]
1792
- end
1793
- end
1794
-
1795
- it 'does not call the method' do
1796
- expect(address).not_to receive(:destroy)
1797
- expect(context.pluck(:destroy)).to eq([ nil ])
1798
- end
1799
-
1800
- it 'does not return the attributes hash' do
1801
- expect(context.pluck(:attributes)).to eq([ nil ])
1802
- end
1803
-
1804
- it 'does not traverse to the parent document' do
1805
- expect(context.pluck('_root.ssn')).to eq([ nil ])
1806
- end
1807
-
1808
- it 'does not destroy the parent document' do
1809
- expect(context.pluck('_root.destroy')).to eq([ nil ])
1810
- expect(Person.where(_id: person.id).count).to eq(1)
1811
- end
1812
- end
1813
-
1814
- context 'when plucking a belongs_to association' do
1815
- let!(:band) do
1816
- Band.create!(name: 'Depeche Mode')
1817
- end
1818
-
1819
- let!(:artist) do
1820
- Artist.create!(band: band)
1821
- end
1822
-
1823
- let(:criteria) do
1824
- Artist.all.tap do |crit|
1825
- crit.documents = [ artist ]
1826
- end
1827
- end
1828
-
1829
- it 'traverses to the associated document' do
1830
- expect(context.pluck('band.name')).to eq([ 'Depeche Mode' ])
1831
- end
1832
-
1833
- it 'returns the document for the association name' do
1834
- expect(context.pluck(:band)).to eq([ band ])
1835
- end
1836
-
1837
- it 'returns the id for the foreign key' do
1838
- expect(context.pluck(:band_id)).to eq([ band.id ])
1839
- end
1840
- end
1841
-
1842
- context 'when plucking the foreign key of a many to many association' do
1843
- let!(:preference) do
1844
- Preference.create!(name: 'nature')
1845
- end
1846
-
1847
- let!(:person) do
1848
- Person.create!(preferences: [ preference ])
1849
- end
1850
-
1851
- let(:criteria) do
1852
- Person.all.tap do |crit|
1853
- crit.documents = [ person ]
1854
- end
1855
- end
1856
-
1857
- it 'returns the stored ids' do
1858
- expect(context.pluck(:preference_ids)).to eq([ [ preference.id ] ])
1859
- end
1860
- end
1861
-
1862
- context 'when the field path has an empty segment' do
1863
- let!(:band) do
1864
- Band.create!(name: 'Depeche Mode', label: Label.new(name: 'Mute'))
1865
- end
1866
-
1867
- let(:criteria) do
1868
- Band.all.tap do |crit|
1869
- crit.documents = [ band ]
1870
- end
1871
- end
1872
-
1873
- it 'returns nil' do
1874
- expect(context.pluck('label..name')).to eq([ nil ])
1875
- end
1876
- end
1877
1999
  end
1878
2000
 
1879
2001
  describe "#pick" do
@@ -1934,27 +2056,6 @@ describe Mongoid::Contextual::Memory do
1934
2056
  expect(picked).to be_nil
1935
2057
  end
1936
2058
  end
1937
-
1938
- context 'when the field name is a method name' do
1939
- let!(:person) do
1940
- Person.create!(ssn: 'secret-ssn')
1941
- end
1942
-
1943
- let!(:address) do
1944
- person.addresses.create!(street: 'hobrecht')
1945
- end
1946
-
1947
- let(:criteria) do
1948
- Address.all.tap do |crit|
1949
- crit.documents = [ address ]
1950
- end
1951
- end
1952
-
1953
- it 'does not call the method' do
1954
- expect(address).not_to receive(:destroy)
1955
- expect(context.pick(:destroy)).to be_nil
1956
- end
1957
- end
1958
2059
  end
1959
2060
 
1960
2061
  describe "#tally" do
@@ -2029,6 +2130,8 @@ describe Mongoid::Contextual::Memory do
2029
2130
  end
2030
2131
 
2031
2132
  context "when tallying a localized field" do
2133
+ with_default_i18n_configs
2134
+
2032
2135
  let(:d1) { Dictionary.new(description: 'en1') }
2033
2136
  let(:d2) { Dictionary.new(description: 'en1') }
2034
2137
  let(:d3) { Dictionary.new(description: 'en1') }
@@ -2090,6 +2193,7 @@ describe Mongoid::Contextual::Memory do
2090
2193
  end
2091
2194
 
2092
2195
  context "when tallying an embedded localized field" do
2196
+ with_default_i18n_configs
2093
2197
 
2094
2198
  let(:person1) { Person.create!(addresses: [ address1a, address1b ]) }
2095
2199
  let(:person2) { Person.create!(addresses: [ address2a, address2b ]) }
@@ -2406,31 +2510,6 @@ describe Mongoid::Contextual::Memory do
2406
2510
  )
2407
2511
  end
2408
2512
  end
2409
-
2410
- context 'when the field name is a method name' do
2411
- let!(:person) do
2412
- Person.create!(ssn: 'secret-ssn')
2413
- end
2414
-
2415
- let!(:address) do
2416
- person.addresses.create!(street: 'hobrecht')
2417
- end
2418
-
2419
- let(:criteria) do
2420
- Address.all.tap do |crit|
2421
- crit.documents = [ address ]
2422
- end
2423
- end
2424
-
2425
- it 'does not call the method' do
2426
- expect(address).not_to receive(:destroy)
2427
- expect(context.tally(:destroy)).to eq(nil => 1)
2428
- end
2429
-
2430
- it 'does not disclose the parent document fields' do
2431
- expect(context.tally('_root.ssn')).to eq(nil => 1)
2432
- end
2433
- end
2434
2513
  end
2435
2514
 
2436
2515
  describe '#inc' do