mongoid 7.2.2 → 7.2.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +1 -1
  4. data/lib/config/locales/en.yml +13 -0
  5. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +1 -1
  6. data/lib/mongoid/association/proxy.rb +1 -1
  7. data/lib/mongoid/association/referenced/has_many/enumerable.rb +1 -1
  8. data/lib/mongoid/association/referenced/has_many/proxy.rb +1 -1
  9. data/lib/mongoid/association/relatable.rb +2 -0
  10. data/lib/mongoid/config/environment.rb +9 -1
  11. data/lib/mongoid/contextual/atomic.rb +7 -2
  12. data/lib/mongoid/contextual/none.rb +3 -0
  13. data/lib/mongoid/criteria/queryable/selectable.rb +2 -2
  14. data/lib/mongoid/criteria/queryable/storable.rb +4 -4
  15. data/lib/mongoid/criteria.rb +1 -1
  16. data/lib/mongoid/document.rb +3 -2
  17. data/lib/mongoid/errors/empty_config_file.rb +26 -0
  18. data/lib/mongoid/errors/invalid_config_file.rb +26 -0
  19. data/lib/mongoid/errors/mongoid_error.rb +1 -1
  20. data/lib/mongoid/errors.rb +2 -0
  21. data/lib/mongoid/interceptable.rb +1 -1
  22. data/lib/mongoid/persistence_context.rb +3 -1
  23. data/lib/mongoid/query_cache.rb +11 -1
  24. data/lib/mongoid/tasks/database.rb +1 -1
  25. data/lib/mongoid/validatable/associated.rb +1 -1
  26. data/lib/mongoid/validatable/presence.rb +3 -3
  27. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  28. data/lib/mongoid/version.rb +1 -1
  29. data/lib/mongoid.rb +1 -0
  30. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +1 -1
  31. data/spec/integration/app_spec.rb +3 -0
  32. data/spec/integration/contextual/empty_spec.rb +142 -0
  33. data/spec/integration/stringified_symbol_field_spec.rb +2 -2
  34. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +17 -4
  35. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +17 -0
  36. data/spec/mongoid/association/referenced/belongs_to_query_spec.rb +20 -0
  37. data/spec/mongoid/association/referenced/has_many_models.rb +17 -0
  38. data/spec/mongoid/clients/factory_spec.rb +9 -3
  39. data/spec/mongoid/clients/options_spec.rb +11 -3
  40. data/spec/mongoid/config/environment_spec.rb +86 -8
  41. data/spec/mongoid/contextual/atomic_spec.rb +64 -25
  42. data/spec/mongoid/contextual/geo_near_spec.rb +1 -1
  43. data/spec/mongoid/criteria_spec.rb +4 -0
  44. data/spec/mongoid/document_fields_spec.rb +26 -0
  45. data/spec/mongoid/document_query_spec.rb +51 -0
  46. data/spec/mongoid/document_spec.rb +21 -1
  47. data/spec/mongoid/errors/invalid_config_file_spec.rb +32 -0
  48. data/spec/mongoid/errors/mongoid_error_spec.rb +20 -8
  49. data/spec/mongoid/persistable/updatable_spec.rb +2 -0
  50. data/spec/mongoid/persistable_spec.rb +2 -2
  51. data/spec/mongoid/query_cache_spec.rb +24 -0
  52. data/spec/shared/bin/s3-copy +45 -0
  53. data/spec/shared/bin/s3-upload +69 -0
  54. data/spec/shared/lib/mrss/cluster_config.rb +8 -3
  55. data/spec/shared/lib/mrss/constraints.rb +49 -10
  56. data/spec/shared/lib/mrss/docker_runner.rb +7 -1
  57. data/spec/shared/lib/mrss/event_subscriber.rb +200 -0
  58. data/spec/shared/lib/mrss/server_version_registry.rb +17 -12
  59. data/spec/shared/lib/mrss/spec_organizer.rb +29 -2
  60. data/spec/shared/share/Dockerfile.erb +127 -35
  61. data/spec/shared/share/haproxy-1.conf +16 -0
  62. data/spec/shared/share/haproxy-2.conf +17 -0
  63. data/spec/shared/shlib/server.sh +100 -23
  64. data/spec/shared/shlib/set_env.sh +4 -1
  65. data/spec/spec_helper.rb +1 -1
  66. data/spec/support/models/address.rb +4 -0
  67. data/spec/support/models/mop.rb +10 -0
  68. data/spec/support/models/person.rb +9 -0
  69. data.tar.gz.sig +0 -0
  70. metadata +549 -519
  71. metadata.gz.sig +0 -0
@@ -297,6 +297,9 @@ describe 'Mongoid application tests' do
297
297
  end
298
298
 
299
299
  def remove_bundler_req
300
+ return unless File.file?('Gemfile.lock')
301
+ # TODO: Remove this method completely when we get rid of .lock files in
302
+ # mongoid-demo apps.
300
303
  lock_lines = IO.readlines('Gemfile.lock')
301
304
  # Get rid of the bundled with line so that whatever bundler is installed
302
305
  # on the system is usable with the application.
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require 'spec_helper'
5
+
6
+ describe 'Contextual classes when dealing with empty result set' do
7
+ shared_examples 'behave as expected' do
8
+ context '#exists?' do
9
+ it 'is false' do
10
+ context.exists?.should be false
11
+ end
12
+ end
13
+
14
+ context '#count' do
15
+ it 'is 0' do
16
+ context.count.should == 0
17
+ end
18
+ end
19
+
20
+ context '#length' do
21
+ it 'is 0' do
22
+ context.length.should == 0
23
+ end
24
+ end
25
+
26
+ # #estimated_count only exists for Mongo
27
+
28
+ context '#distinct' do
29
+ it 'is empty array' do
30
+ context.distinct(:foo).should == []
31
+ end
32
+ end
33
+
34
+ context '#each' do
35
+ context 'with block' do
36
+ it 'does not invoke the block' do
37
+ called = false
38
+ context.each do
39
+ called = true
40
+ end
41
+ called.should be false
42
+ end
43
+ end
44
+
45
+ context 'without block' do
46
+ it 'returns Enumerable' do
47
+ context.each.should be_a(Enumerable)
48
+ end
49
+
50
+ it 'returns empty Enumerable' do
51
+ context.each.to_a.should == []
52
+ end
53
+ end
54
+ end
55
+
56
+ context '#map' do
57
+ context 'with block' do
58
+ it 'does not invoke the block' do
59
+ called = false
60
+ context.map do
61
+ called = true
62
+ end
63
+ called.should be false
64
+ end
65
+ end
66
+
67
+ context 'without block' do
68
+ it 'returns empty array' do
69
+ skip 'MONGOID-5148'
70
+
71
+ context.map(:field).should == []
72
+ end
73
+ end
74
+ end
75
+
76
+ context '#first' do
77
+ it 'is nil' do
78
+ context.first.should be nil
79
+ end
80
+ end
81
+
82
+ context '#find_first' do
83
+ it 'is nil' do
84
+ context.find_first.should be nil
85
+ end
86
+ end
87
+
88
+ context '#one' do
89
+ it 'is nil' do
90
+ context.one.should be nil
91
+ end
92
+ end
93
+
94
+ context '#last' do
95
+ it 'is nil' do
96
+ context.last.should be nil
97
+ end
98
+ end
99
+ end
100
+
101
+ let(:context) do
102
+ context_cls.new(criteria)
103
+ end
104
+
105
+ before do
106
+ # Create an object of the same class used in the Criteria instance
107
+ # to verify we are using the Contextual classes.
108
+ Mop.create!
109
+ end
110
+
111
+ context 'Mongo' do
112
+ let(:context_cls) { Mongoid::Contextual::Mongo }
113
+
114
+ let(:criteria) do
115
+ Mop.and(Mop.where(a: 1), Mop.where(a: 2))
116
+ end
117
+
118
+ include_examples 'behave as expected'
119
+ end
120
+
121
+ context 'Memory' do
122
+ let(:context_cls) { Mongoid::Contextual::Memory }
123
+
124
+ let(:criteria) do
125
+ Mop.all.tap do |criteria|
126
+ criteria.documents = []
127
+ end
128
+ end
129
+
130
+ include_examples 'behave as expected'
131
+ end
132
+
133
+ context 'None' do
134
+ let(:context_cls) { Mongoid::Contextual::None }
135
+
136
+ let(:criteria) do
137
+ Mop.none
138
+ end
139
+
140
+ include_examples 'behave as expected'
141
+ end
142
+ end
@@ -31,8 +31,8 @@ describe "StringifiedSymbol fields" do
31
31
  end
32
32
  end
33
33
 
34
- # Using command monitoring to test that StringifiedSymbol sends a string and returns a symbol
35
- let(:client) { Order.collection.client }
34
+ # Using command monitoring to test that StringifiedSymbol sends a string and returns a symbol
35
+ let(:client) { Order.collection.client }
36
36
 
37
37
  before do
38
38
  client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
@@ -2449,13 +2449,26 @@ describe Mongoid::Association::Embedded::EmbedsMany::Proxy do
2449
2449
  end
2450
2450
 
2451
2451
  context "when providing a criteria class method" do
2452
+ context "without keyword arguments" do
2452
2453
 
2453
- let(:addresses) do
2454
- person.addresses.california
2454
+ let(:addresses) do
2455
+ person.addresses.california
2456
+ end
2457
+
2458
+ it "applies the criteria to the documents" do
2459
+ expect(addresses).to eq([ address_one ])
2460
+ end
2455
2461
  end
2456
2462
 
2457
- it "applies the criteria to the documents" do
2458
- expect(addresses).to eq([ address_one ])
2463
+ context "with keyword arguments" do
2464
+
2465
+ let(:addresses) do
2466
+ person.addresses.city_and_state(city: "Sacramento", state: "CA")
2467
+ end
2468
+
2469
+ it "applies the criteria to the documents" do
2470
+ expect(addresses).to eq([])
2471
+ end
2459
2472
  end
2460
2473
  end
2461
2474
 
@@ -1332,4 +1332,21 @@ describe Mongoid::Association::Referenced::BelongsTo::Proxy do
1332
1332
  end
1333
1333
  end
1334
1334
  end
1335
+
1336
+ describe "#method_missing" do
1337
+ let!(:person) do
1338
+ Person.create
1339
+ end
1340
+
1341
+ let!(:game) do
1342
+ Game.create(person: person)
1343
+ end
1344
+
1345
+ it 'handles keyword args' do
1346
+ expect do
1347
+ game.person.set_personal_data(ssn: '123', age: 25)
1348
+ end.not_to raise_error
1349
+ end
1350
+
1351
+ end
1335
1352
  end
@@ -35,4 +35,24 @@ describe Mongoid::Association::Referenced::BelongsTo do
35
35
  expect(school.team).to eq('Bulldogs')
36
36
  end
37
37
  end
38
+
39
+ context 'when projecting with #only while having similar inverse_of candidates' do
40
+ before do
41
+ alice = HmmOwner.create!(name: 'Alice')
42
+ bob = HmmOwner.create!(name: 'Bob')
43
+
44
+ HmmPet.create!(name: 'Rex', current_owner: bob, previous_owner: alice)
45
+ end
46
+
47
+ let(:pet) { HmmPet.where(name: 'Rex').only(:name, :previous_owner_id, 'previous_owner.name').first }
48
+
49
+ it 'populates specified fields' do
50
+ expect(pet.name).to eq('Rex')
51
+ expect(pet.previous_owner.name).to eq('Alice')
52
+ end
53
+
54
+ it 'does not try to load the inverse for an association that explicitly prevents it' do
55
+ expect { pet.previous_owner.name }.not_to raise_error
56
+ end
57
+ end
38
58
  end
@@ -24,6 +24,23 @@ class HmmAddress
24
24
  belongs_to :company, class_name: 'HmmCompany'
25
25
  end
26
26
 
27
+ class HmmOwner
28
+ include Mongoid::Document
29
+
30
+ has_many :pets, class_name: 'HmmPet', inverse_of: :current_owner
31
+
32
+ field :name, type: String
33
+ end
34
+
35
+ class HmmPet
36
+ include Mongoid::Document
37
+
38
+ belongs_to :current_owner, class_name: 'HmmOwner', inverse_of: :pets, optional: true
39
+ belongs_to :previous_owner, class_name: 'HmmOwner', inverse_of: nil, optional: true
40
+
41
+ field :name, type: String
42
+ end
43
+
27
44
  class HmmSchool
28
45
  include Mongoid::Document
29
46
 
@@ -65,9 +65,15 @@ describe Mongoid::Clients::Factory do
65
65
  expect(client).to be_a(Mongo::Client)
66
66
  end
67
67
 
68
- it 'does not produce driver warnings' do
69
- Mongo::Logger.logger.should_not receive(:warn)
70
- client
68
+ context 'not JRuby' do
69
+ # Run this test on JRuby when driver 2.16.0 is released -
70
+ # see RUBY-2771.
71
+ fails_on_jruby
72
+
73
+ it 'does not produce driver warnings' do
74
+ Mongo::Logger.logger.should_not receive(:warn)
75
+ client
76
+ end
71
77
  end
72
78
 
73
79
  let(:cluster_addresses) do
@@ -83,9 +83,11 @@ describe Mongoid::Clients::Options, retry: 3 do
83
83
 
84
84
  let!(:connections_and_cluster_during) do
85
85
  connections = nil
86
- cluster = Minim.with(options) do |klass|
86
+ cluster = nil
87
+ Minim.with(options) do |klass|
87
88
  klass.where(name: 'emily').to_a
88
89
  connections = Minim.mongo_client.database.command(serverStatus: 1).first['connections']['current']
90
+ cluster = Minim.collection.cluster
89
91
  end
90
92
  [ connections, cluster ]
91
93
  end
@@ -124,7 +126,10 @@ describe Mongoid::Clients::Options, retry: 3 do
124
126
  end
125
127
 
126
128
  it 'disconnects the new cluster when the block exits' do
127
- expect(connections_before).to eq(connections_after)
129
+ expect(cluster_after).not_to be(cluster_during)
130
+
131
+ cluster_during.connected?.should be false
132
+ cluster_before.connected?.should be true
128
133
  end
129
134
  end
130
135
 
@@ -138,11 +143,14 @@ describe Mongoid::Clients::Options, retry: 3 do
138
143
 
139
144
  it 'does not create a new cluster' do
140
145
  expect(connections_during).to eq(connections_before)
146
+
147
+ cluster_during.should be cluster_before
141
148
  end
142
149
 
143
150
  it 'does not disconnect the original cluster' do
144
- expect(connections_after).to eq(connections_before)
145
151
  expect(cluster_before).to be(cluster_after)
152
+
153
+ cluster_before.connected?.should be true
146
154
  end
147
155
  end
148
156
 
@@ -5,9 +5,19 @@ require "spec_helper"
5
5
 
6
6
  describe Mongoid::Config::Environment do
7
7
 
8
- after(:all) do
9
- Rails = RailsTemp
10
- Object.send(:remove_const, :RailsTemp)
8
+ around do |example|
9
+ if defined?(Rails)
10
+ SavedRails = Rails
11
+ example.run
12
+ Object.send(:remove_const, :Rails) if defined?(Rails)
13
+ Rails = SavedRails
14
+ Object.send(:remove_const, :SavedRails)
15
+ else
16
+ example.run
17
+ if defined?(Rails)
18
+ Object.send(:remove_const, :Rails)
19
+ end
20
+ end
11
21
  end
12
22
 
13
23
  describe "#env_name" do
@@ -24,11 +34,6 @@ describe Mongoid::Config::Environment do
24
34
  end
25
35
  end
26
36
 
27
- after do
28
- RailsTemp = Rails
29
- Object.send(:remove_const, :Rails)
30
- end
31
-
32
37
  it "returns the rails environment" do
33
38
  expect(described_class.env_name).to eq("production")
34
39
  end
@@ -86,4 +91,77 @@ describe Mongoid::Config::Environment do
86
91
  end
87
92
  end
88
93
  end
94
+
95
+ describe "#load_yaml" do
96
+ let(:path) { 'mongoid.yml' }
97
+ let(:environment) {}
98
+ before { allow(Rails).to receive('env').and_return('test') }
99
+
100
+ subject { described_class.load_yaml(path, environment) }
101
+
102
+ context 'when file not found' do
103
+ let(:path) { 'not/a/valid/path'}
104
+
105
+ it { expect { subject }.to raise_error(Errno::ENOENT) }
106
+ end
107
+
108
+ context 'when file found' do
109
+ before do
110
+ allow(File).to receive(:new).with('mongoid.yml').and_return(StringIO.new(file_contents))
111
+ end
112
+
113
+ let(:file_contents) do
114
+ <<~FILE
115
+ test:
116
+ clients: ['test']
117
+ development:
118
+ clients: ['dev']
119
+ FILE
120
+ end
121
+
122
+ context 'when file cannot be parsed as YAML' do
123
+ let(:file_contents) { "*\nbad:%123abc" }
124
+
125
+ it { expect { subject }.to raise_error(Psych::SyntaxError) }
126
+ end
127
+
128
+ context 'when file contains ERB errors' do
129
+ let(:file_contents) { '<%= foo %>' }
130
+
131
+ it { expect { subject }.to raise_error(NameError) }
132
+ end
133
+
134
+ context 'when file is empty' do
135
+ let(:file_contents) { '' }
136
+
137
+ it { expect { subject }.to raise_error(Mongoid::Errors::EmptyConfigFile) }
138
+ end
139
+
140
+ context 'when file does not contain a YAML Hash object' do
141
+ let(:file_contents) { '["this", "is", "an", "array"]' }
142
+
143
+ it { expect { subject }.to raise_error(Mongoid::Errors::InvalidConfigFile) }
144
+ end
145
+
146
+ context 'when environment not specified' do
147
+ it 'uses the rails environment' do
148
+ is_expected.to eq("clients"=>["test"])
149
+ end
150
+ end
151
+
152
+ context 'when environment is specified' do
153
+ let(:environment) { 'development' }
154
+
155
+ it 'uses the specified environment' do
156
+ is_expected.to eq("clients"=>["dev"])
157
+ end
158
+ end
159
+
160
+ context 'when environment is missing' do
161
+ let(:environment) { 'staging' }
162
+
163
+ it { is_expected.to be_nil }
164
+ end
165
+ end
166
+ end
89
167
  end
@@ -801,27 +801,28 @@ describe Mongoid::Contextual::Atomic do
801
801
  context.unset(:name)
802
802
  end
803
803
 
804
- it "unsets the first existing field" do
805
- expect(depeche_mode.reload.name).to be_nil
806
- end
807
-
808
- it "unsets the last existing field" do
809
- expect(new_order.reload.name).to be_nil
804
+ it "unsets the fields from all documents" do
805
+ depeche_mode.reload
806
+ new_order.reload
807
+ expect(depeche_mode.name).to be_nil
808
+ expect(depeche_mode.years).to_not be_nil
809
+ expect(new_order.name).to be_nil
810
+ expect(new_order.years).to_not be_nil
810
811
  end
811
812
  end
812
813
 
813
814
  context "when the field is aliased" do
814
-
815
815
  before do
816
816
  context.unset(:years)
817
817
  end
818
818
 
819
- it "unsets the first existing field" do
820
- expect(depeche_mode.reload.years).to be_nil
821
- end
822
-
823
- it "unsets the last existing field" do
824
- expect(new_order.reload.years).to be_nil
819
+ it "unsets the fields from all documents" do
820
+ depeche_mode.reload
821
+ new_order.reload
822
+ expect(depeche_mode.name).to_not be_nil
823
+ expect(depeche_mode.years).to be_nil
824
+ expect(new_order.name).to_not be_nil
825
+ expect(new_order.years).to be_nil
825
826
  end
826
827
  end
827
828
  end
@@ -829,7 +830,8 @@ describe Mongoid::Contextual::Atomic do
829
830
  context "when unsetting multiple fields" do
830
831
 
831
832
  let!(:new_order) do
832
- Band.create(name: "New Order", genres: [ "electro", "dub" ], years: 10)
833
+ Band.create(name: "New Order", genres: %w[electro dub], years: 10,
834
+ likes: 200, rating: 4.3, origin: 'Space')
833
835
  end
834
836
 
835
837
  let(:criteria) do
@@ -846,12 +848,13 @@ describe Mongoid::Contextual::Atomic do
846
848
  context.unset(:name, :genres)
847
849
  end
848
850
 
849
- it "unsets name field" do
850
- expect(new_order.reload.name).to be_nil
851
- end
852
-
853
- it "unsets genres field" do
854
- expect(new_order.reload.genres).to be_nil
851
+ it "unsets the specified fields" do
852
+ new_order.reload
853
+ expect(new_order.name).to be_nil
854
+ expect(new_order.genres).to be_nil
855
+ expect(new_order.years).to_not be_nil
856
+ expect(new_order.likes).to_not be_nil
857
+ expect(new_order.rating).to_not be_nil
855
858
  end
856
859
  end
857
860
 
@@ -861,12 +864,46 @@ describe Mongoid::Contextual::Atomic do
861
864
  context.unset(:name, :years)
862
865
  end
863
866
 
864
- it "unsets the unaliased field" do
865
- expect(new_order.reload.name).to be_nil
867
+ it "unsets the specified fields" do
868
+ new_order.reload
869
+ expect(new_order.name).to be_nil
870
+ expect(new_order.genres).to_not be_nil
871
+ expect(new_order.years).to be_nil
872
+ expect(new_order.likes).to_not be_nil
873
+ expect(new_order.rating).to_not be_nil
874
+ end
875
+ end
876
+
877
+ context "when using Hash arguments" do
878
+
879
+ before do
880
+ context.unset({ years: true, likes: "" }, { rating: false, origin: nil })
881
+ end
882
+
883
+ it "unsets the specified fields" do
884
+ new_order.reload
885
+ expect(new_order.name).to_not be_nil
886
+ expect(new_order.genres).to_not be_nil
887
+ expect(new_order.years).to be_nil
888
+ expect(new_order.likes).to be_nil
889
+ expect(new_order.rating).to be_nil
890
+ expect(new_order.origin).to be_nil
891
+ end
892
+ end
893
+
894
+ context "when mixing argument types" do
895
+
896
+ before do
897
+ context.unset(:name, [:years], { likes: "" }, { rating: false })
866
898
  end
867
899
 
868
- it "unsets the aliased field" do
869
- expect(new_order.reload.years).to be_nil
900
+ it "unsets the specified fields" do
901
+ new_order.reload
902
+ expect(new_order.name).to be_nil
903
+ expect(new_order.genres).to_not be_nil
904
+ expect(new_order.years).to be_nil
905
+ expect(new_order.likes).to be_nil
906
+ expect(new_order.rating).to be_nil
870
907
  end
871
908
  end
872
909
  end
@@ -895,7 +932,9 @@ describe Mongoid::Contextual::Atomic do
895
932
  end
896
933
 
897
934
  it "unsets the unaliased field" do
898
- expect(depeche_mode.reload.name).to be_nil
935
+ depeche_mode.reload
936
+ expect(depeche_mode.name).to be_nil
937
+ expect(depeche_mode.years).to_not be_nil
899
938
  end
900
939
  end
901
940
  end