mongoid 7.0.0.beta → 7.0.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 (86) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/config/locales/en.yml +21 -0
  5. data/lib/mongoid.rb +1 -1
  6. data/lib/mongoid/association/embedded/batchable.rb +39 -13
  7. data/lib/mongoid/association/many.rb +4 -0
  8. data/lib/mongoid/association/referenced/has_many.rb +2 -0
  9. data/lib/mongoid/association/referenced/has_many/enumerable.rb +38 -7
  10. data/lib/mongoid/association/referenced/has_many/proxy.rb +3 -2
  11. data/lib/mongoid/association/referenced/syncable.rb +1 -1
  12. data/lib/mongoid/association/touchable.rb +1 -1
  13. data/lib/mongoid/atomic.rb +3 -3
  14. data/lib/mongoid/atomic/modifiers.rb +12 -8
  15. data/lib/mongoid/attributes.rb +20 -12
  16. data/lib/mongoid/attributes/dynamic.rb +2 -2
  17. data/lib/mongoid/changeable.rb +1 -1
  18. data/lib/mongoid/clients.rb +2 -0
  19. data/lib/mongoid/clients/sessions.rb +113 -0
  20. data/lib/mongoid/clients/storage_options.rb +1 -0
  21. data/lib/mongoid/composable.rb +1 -0
  22. data/lib/mongoid/contextual/aggregable/mongo.rb +1 -1
  23. data/lib/mongoid/contextual/atomic.rb +6 -3
  24. data/lib/mongoid/contextual/geo_near.rb +1 -1
  25. data/lib/mongoid/contextual/map_reduce.rb +6 -2
  26. data/lib/mongoid/contextual/memory.rb +25 -2
  27. data/lib/mongoid/contextual/mongo.rb +33 -14
  28. data/lib/mongoid/copyable.rb +2 -2
  29. data/lib/mongoid/criteria.rb +1 -0
  30. data/lib/mongoid/criteria/queryable/extensions/string.rb +1 -1
  31. data/lib/mongoid/criteria/queryable/mergeable.rb +3 -1
  32. data/lib/mongoid/errors.rb +1 -0
  33. data/lib/mongoid/errors/invalid_session_use.rb +24 -0
  34. data/lib/mongoid/extensions.rb +0 -4
  35. data/lib/mongoid/extensions/hash.rb +5 -2
  36. data/lib/mongoid/factory.rb +14 -5
  37. data/lib/mongoid/fields.rb +2 -2
  38. data/lib/mongoid/indexable.rb +4 -4
  39. data/lib/mongoid/matchable/and.rb +1 -1
  40. data/lib/mongoid/matchable/elem_match.rb +9 -3
  41. data/lib/mongoid/persistable.rb +2 -2
  42. data/lib/mongoid/persistable/creatable.rb +4 -2
  43. data/lib/mongoid/persistable/deletable.rb +4 -2
  44. data/lib/mongoid/persistable/destroyable.rb +1 -5
  45. data/lib/mongoid/persistable/updatable.rb +2 -2
  46. data/lib/mongoid/persistable/upsertable.rb +2 -1
  47. data/lib/mongoid/persistence_context.rb +5 -4
  48. data/lib/mongoid/query_cache.rb +5 -3
  49. data/lib/mongoid/reloadable.rb +1 -1
  50. data/lib/mongoid/serializable.rb +1 -1
  51. data/lib/mongoid/shardable.rb +1 -1
  52. data/lib/mongoid/tasks/database.rb +3 -2
  53. data/lib/mongoid/threaded.rb +38 -0
  54. data/lib/mongoid/traversable.rb +1 -1
  55. data/lib/mongoid/version.rb +1 -1
  56. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +4 -0
  57. data/spec/app/models/band.rb +1 -0
  58. data/spec/app/models/shipment_address.rb +1 -0
  59. data/spec/mongoid/association/macros_spec.rb +20 -0
  60. data/spec/mongoid/association/referenced/has_many/eager_spec.rb +15 -0
  61. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +229 -0
  62. data/spec/mongoid/atomic/modifiers_spec.rb +17 -17
  63. data/spec/mongoid/atomic_spec.rb +17 -17
  64. data/spec/mongoid/attributes_spec.rb +38 -2
  65. data/spec/mongoid/clients/sessions_spec.rb +325 -0
  66. data/spec/mongoid/contextual/memory_spec.rb +19 -0
  67. data/spec/mongoid/contextual/mongo_spec.rb +133 -0
  68. data/spec/mongoid/copyable_spec.rb +34 -0
  69. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +43 -0
  70. data/spec/mongoid/criteria/queryable/selectable_spec.rb +32 -3
  71. data/spec/mongoid/extensions/hash_spec.rb +18 -1
  72. data/spec/mongoid/factory_spec.rb +11 -0
  73. data/spec/mongoid/interceptable_spec.rb +3 -1
  74. data/spec/mongoid/matchable/elem_match_spec.rb +20 -0
  75. data/spec/mongoid/persistable/deletable_spec.rb +19 -0
  76. data/spec/mongoid/persistable/destroyable_spec.rb +19 -0
  77. data/spec/mongoid/persistable/savable_spec.rb +2 -2
  78. data/spec/mongoid/persistable/updatable_spec.rb +2 -2
  79. data/spec/mongoid/persistable_spec.rb +31 -16
  80. data/spec/mongoid/persistence_context_spec.rb +14 -0
  81. data/spec/mongoid/positional_spec.rb +10 -10
  82. data/spec/mongoid/query_cache_spec.rb +2 -16
  83. data/spec/mongoid/shardable_spec.rb +32 -12
  84. data/spec/spec_helper.rb +74 -0
  85. metadata +456 -446
  86. metadata.gz.sig +0 -0
@@ -144,6 +144,40 @@ describe Mongoid::Copyable do
144
144
  end
145
145
  end
146
146
 
147
+ context "when cloning a document with polymorphic embedded documents with multiple language field" do
148
+
149
+ let!(:shipment_address) do
150
+ person.addresses.build({ shipping_name: "Title" }, ShipmentAddress)
151
+ end
152
+
153
+ before do
154
+ I18n.enforce_available_locales = false
155
+ I18n.locale = 'pt_BR'
156
+ person.addresses.type(ShipmentAddress).each { |address| address.shipping_name = "Título" }
157
+ person.save
158
+ end
159
+
160
+ after do
161
+ I18n.locale = :en
162
+ end
163
+
164
+ let!(:from_db) do
165
+ Person.find(person.id)
166
+ end
167
+
168
+ let(:copy) do
169
+ from_db.send(method)
170
+ end
171
+
172
+ it 'sets embedded translations' do
173
+ I18n.locale = 'pt_BR'
174
+ copy.addresses.type(ShipmentAddress).each do |address|
175
+ expect(address.shipping_name).to eq("Título")
176
+ end
177
+ end
178
+
179
+ end
180
+
147
181
  context "when cloning a loaded document" do
148
182
 
149
183
  before do
@@ -66,6 +66,49 @@ describe String do
66
66
  expect(evolved).to eq(Time.new(2010, 1, 1, 11, 0, 0, 0).utc)
67
67
  end
68
68
  end
69
+
70
+ context "when the string without timezone" do
71
+
72
+ context "when using active support's time zone" do
73
+
74
+ before do
75
+ Mongoid.use_activesupport_time_zone = true
76
+ ::Time.zone = "Tokyo"
77
+ end
78
+
79
+ let(:date) do
80
+ "2010-01-01 5:00:00"
81
+ end
82
+
83
+ let(:evolved) do
84
+ date.__evolve_time__
85
+ end
86
+
87
+ it "parses string using active support's time zone" do
88
+ expect(evolved).to eq(Time.zone.parse(date).utc)
89
+ end
90
+ end
91
+
92
+ context "when not using active support's time zone" do
93
+
94
+ before do
95
+ Mongoid.use_activesupport_time_zone = false
96
+ ::Time.zone = nil
97
+ end
98
+
99
+ let(:date) do
100
+ "2010-01-01 5:00:00"
101
+ end
102
+
103
+ let(:evolved) do
104
+ date.__evolve_time__
105
+ end
106
+
107
+ it "parses string using system time zone" do
108
+ expect(evolved).to eq(Time.parse(date).utc)
109
+ end
110
+ end
111
+ end
69
112
  end
70
113
 
71
114
  describe "#__sort_option__" do
@@ -1449,7 +1449,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
1449
1449
 
1450
1450
  context "when the criterion are on the same field" do
1451
1451
 
1452
- context "when the stretegy is the default (intersection)" do
1452
+ context "when the strategy is the default (intersection)" do
1453
1453
 
1454
1454
  let(:selection) do
1455
1455
  query.in(first: [ 1, 2 ].freeze).in(first: [ 2, 3 ])
@@ -1466,6 +1466,35 @@ describe Mongoid::Criteria::Queryable::Selectable do
1466
1466
  end
1467
1467
  end
1468
1468
 
1469
+ context 'when the field is aliased' do
1470
+
1471
+ before(:all) do
1472
+ class TestModel
1473
+ include Mongoid::Document
1474
+ end
1475
+ end
1476
+
1477
+ after(:all) do
1478
+ Object.send(:remove_const, :TestModel)
1479
+ end
1480
+
1481
+ let(:bson_object_id) do
1482
+ BSON::ObjectId.new
1483
+ end
1484
+
1485
+ let(:selection) do
1486
+ TestModel.in(id: [bson_object_id.to_s]).in(id: [bson_object_id.to_s])
1487
+ end
1488
+
1489
+ it "intersects the $in selectors" do
1490
+ expect(selection.selector).to eq("_id" => { "$in" => [ bson_object_id ] })
1491
+ end
1492
+
1493
+ it "returns a cloned query" do
1494
+ expect(selection).to_not equal(query)
1495
+ end
1496
+ end
1497
+
1469
1498
  context "when the stretegy is intersect" do
1470
1499
 
1471
1500
  let(:selection) do
@@ -1483,7 +1512,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
1483
1512
  end
1484
1513
  end
1485
1514
 
1486
- context "when the stretegy is override" do
1515
+ context "when the strategy is override" do
1487
1516
 
1488
1517
  let(:selection) do
1489
1518
  query.in(first: [ 1, 2 ]).override.in(first: [ 3, 4 ])
@@ -1500,7 +1529,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
1500
1529
  end
1501
1530
  end
1502
1531
 
1503
- context "when the stretegy is union" do
1532
+ context "when the strategy is union" do
1504
1533
 
1505
1534
  let(:selection) do
1506
1535
  query.in(first: [ 1, 2 ]).union.in(first: [ 3, 4 ])
@@ -230,10 +230,27 @@ describe Mongoid::Extensions::Hash do
230
230
  it "should retrieve a nested value under the provided key" do
231
231
  expect(nested).to eq "hundred"
232
232
  end
233
+
234
+ context 'and the value is falsey' do
235
+ let(:hash) do
236
+ { "100" => { "name" => false } }
237
+ end
238
+ it "should retrieve the falsey nested value under the provided key" do
239
+ expect(nested).to eq false
240
+ end
241
+ end
242
+
243
+ context 'and the value is nil' do
244
+ let(:hash) do
245
+ { "100" => { 0 => "Please don't return this value!" } }
246
+ end
247
+ it "should retrieve the nil nested value under the provided key" do
248
+ expect(nested).to eq nil
249
+ end
250
+ end
233
251
  end
234
252
 
235
253
  context "when the hash key is an integer" do
236
-
237
254
  let(:hash) do
238
255
  { 100 => { "name" => "hundred" } }
239
256
  end
@@ -88,6 +88,17 @@ describe Mongoid::Factory do
88
88
  expect(person.title).to eq("Sir")
89
89
  end
90
90
  end
91
+
92
+ context "when the type is a symbol" do
93
+
94
+ let(:person) do
95
+ described_class.build(Person, { :_type => "Doctor" })
96
+ end
97
+
98
+ it "instantiates the subclass" do
99
+ expect(person.class).to eq(Doctor)
100
+ end
101
+ end
91
102
  end
92
103
 
93
104
  describe ".from_db" do
@@ -526,7 +526,9 @@ describe Mongoid::Interceptable do
526
526
  end
527
527
 
528
528
  after(:all) do
529
- Band.reset_callbacks(:rearrange)
529
+ # ActiveSupport may raise an error when trying to reset callbacks on all of Band's
530
+ # descendants, regardless of whether they have a particular callback defined.
531
+ begin; Band.reset_callbacks(:rearrange); rescue; end
530
532
  end
531
533
 
532
534
  let(:attributes) do
@@ -53,6 +53,19 @@ describe Mongoid::Matchable::ElemMatch do
53
53
  expect(matcher._matches?("$elemMatch" => {"a" => {"$in" => [1]}, "b" => {"$gt" => 1}})).to be true
54
54
  end
55
55
  end
56
+
57
+ context "when using a $not operator that matches" do
58
+
59
+ it "returns true" do
60
+ expect(matcher._matches?("$elemMatch" => {"a" => {"$not" => 4}})).to be true
61
+ end
62
+ end
63
+ end
64
+
65
+ context "when using symbols and a :$not operator that matches" do
66
+ it "returns true" do
67
+ expect(matcher._matches?(:$elemMatch => {"a" => {:$not => 4}})).to be true
68
+ end
56
69
  end
57
70
 
58
71
  context "when there is not a sub document that matches the criteria" do
@@ -74,6 +87,13 @@ describe Mongoid::Matchable::ElemMatch do
74
87
  expect(matcher._matches?("$elemMatch" => {"a" => {"$in" => [1]}, "b" => {"$gt" => 10}})).to be false
75
88
  end
76
89
  end
90
+
91
+ context "when using a $not operator that does not match" do
92
+
93
+ it "returns true" do
94
+ expect(matcher._matches?("$elemMatch" => {"a" => {"$not" => 1}})).to be true
95
+ end
96
+ end
77
97
  end
78
98
 
79
99
  context "when using a criteria that matches partially but not a single sub document" do
@@ -228,6 +228,25 @@ describe Mongoid::Persistable::Deletable do
228
228
  expect(removed).to eq(1)
229
229
  end
230
230
  end
231
+
232
+ context 'when the write concern is unacknowledged' do
233
+
234
+ before do
235
+ Person.create(title: 'miss')
236
+ end
237
+
238
+ let!(:deleted) do
239
+ Person.with(write: { w: 0 }) { |klass| klass.delete_all(title: "sir") }
240
+ end
241
+
242
+ it "removes the matching documents" do
243
+ expect(Person.where(title: 'miss').count).to eq(1)
244
+ end
245
+
246
+ it "returns 0" do
247
+ expect(deleted).to eq(0)
248
+ end
249
+ end
231
250
  end
232
251
  end
233
252
  end
@@ -222,6 +222,25 @@ describe Mongoid::Persistable::Destroyable do
222
222
  end
223
223
  end
224
224
 
225
+ context 'when the write concern is unacknowledged' do
226
+
227
+ before do
228
+ Person.create(title: 'miss')
229
+ end
230
+
231
+ let!(:removed) do
232
+ Person.with(write: { w: 0 }) { |klass| klass.destroy_all(title: "sir") }
233
+ end
234
+
235
+ it "removes the matching documents" do
236
+ expect(Person.where(title: 'miss').count).to eq(1)
237
+ end
238
+
239
+ it "returns 0" do
240
+ expect(removed).to eq(0)
241
+ end
242
+ end
243
+
225
244
  context 'when removing a list of embedded documents' do
226
245
 
227
246
  context 'when the embedded documents list is reversed in memory' do
@@ -189,8 +189,8 @@ describe Mongoid::Persistable::Savable do
189
189
  "title" => "King",
190
190
  "name.first_name" => "Ryan"
191
191
  },
192
- "$pushAll"=> {
193
- "addresses" => [ { "_id" => address.id, "street" => "Bond St" } ]
192
+ "$push"=> {
193
+ "addresses" => { '$each' => [ { "_id" => address.id, "street" => "Bond St" } ] }
194
194
  }
195
195
  })
196
196
  end
@@ -462,7 +462,7 @@ describe Mongoid::Persistable::Updatable do
462
462
 
463
463
  it "raises an error" do
464
464
  expect {
465
- person.update_attributes(map: { "bad.key" => "value" })
465
+ person.update_attributes(map: { "$bad.key" => "value" })
466
466
  }.to raise_error(Mongo::Error::OperationFailure)
467
467
  end
468
468
  end
@@ -498,7 +498,7 @@ describe Mongoid::Persistable::Updatable do
498
498
 
499
499
  it "raises an error" do
500
500
  expect {
501
- person.send(method, map: { "bad.key" => "value" })
501
+ person.send(method, map: { "$bad.key" => "value" })
502
502
  }.to raise_error(Mongo::Error::OperationFailure)
503
503
  end
504
504
  end
@@ -52,16 +52,16 @@ describe Mongoid::Persistable do
52
52
  context "when not chaining the operations" do
53
53
 
54
54
  let(:operations) do
55
- {
55
+ [{
56
56
  "$inc" => { "member_count" => 10 },
57
57
  "$bit" => { "likes" => { :and => 13 }},
58
58
  "$set" => { "name" => "Placebo" },
59
- "$unset" => { "origin" => true }
60
- }
59
+ "$unset" => { "origin" => true }},
60
+ { :session => nil } ]
61
61
  end
62
62
 
63
63
  before do
64
- expect_any_instance_of(Mongo::Collection::View).to receive(:update_one).with(operations).and_call_original
64
+ expect_any_instance_of(Mongo::Collection::View).to receive(:update_one).with(*operations).and_call_original
65
65
  end
66
66
 
67
67
  let!(:update) do
@@ -79,16 +79,16 @@ describe Mongoid::Persistable do
79
79
  context "when chaining the operations" do
80
80
 
81
81
  let(:operations) do
82
- {
82
+ [{
83
83
  "$inc" => { "member_count" => 10 },
84
84
  "$bit" => { "likes" => { :and => 13 }},
85
85
  "$set" => { "name" => "Placebo" },
86
- "$unset" => { "origin" => true }
87
- }
86
+ "$unset" => { "origin" => true } },
87
+ { :session => nil } ]
88
88
  end
89
89
 
90
90
  before do
91
- expect_any_instance_of(Mongo::Collection::View).to receive(:update_one).with(operations).and_call_original
91
+ expect_any_instance_of(Mongo::Collection::View).to receive(:update_one).with(*operations).and_call_original
92
92
  end
93
93
 
94
94
  let!(:update) do
@@ -107,16 +107,16 @@ describe Mongoid::Persistable do
107
107
  context "when given multiple operations of the same type" do
108
108
 
109
109
  let(:operations) do
110
- {
110
+ [{
111
111
  "$inc" => { "member_count" => 10, "other_count" => 10 },
112
112
  "$bit" => { "likes" => { :and => 13 }},
113
113
  "$set" => { "name" => "Placebo" },
114
- "$unset" => { "origin" => true }
115
- }
114
+ "$unset" => { "origin" => true }},
115
+ { :session => nil } ]
116
116
  end
117
117
 
118
118
  before do
119
- expect_any_instance_of(Mongo::Collection::View).to receive(:update_one).with(operations).and_call_original
119
+ expect_any_instance_of(Mongo::Collection::View).to receive(:update_one).with(*operations).and_call_original
120
120
  end
121
121
 
122
122
  let!(:update) do
@@ -144,16 +144,16 @@ describe Mongoid::Persistable do
144
144
  context "when expecting the document to be yielded" do
145
145
 
146
146
  let(:operations) do
147
- {
147
+ [{
148
148
  "$inc" => { "member_count" => 10 },
149
149
  "$bit" => { "likes" => { :and => 13 }},
150
150
  "$set" => { "name" => "Placebo" },
151
- "$unset" => { "origin" => true }
152
- }
151
+ "$unset" => { "origin" => true }},
152
+ { :session => nil } ]
153
153
  end
154
154
 
155
155
  before do
156
- expect_any_instance_of(Mongo::Collection::View).to receive(:update_one).with(operations).and_call_original
156
+ expect_any_instance_of(Mongo::Collection::View).to receive(:update_one).with(*operations).and_call_original
157
157
  end
158
158
 
159
159
  let!(:update) do
@@ -200,6 +200,21 @@ describe Mongoid::Persistable do
200
200
  expect(document.atomically).to be true
201
201
  end
202
202
  end
203
+
204
+ context "when the block has no operations" do
205
+ before do
206
+ expect_any_instance_of(Mongo::Collection::View).to_not receive(:update_one)
207
+ end
208
+
209
+ let!(:update) do
210
+ document.atomically do
211
+ end
212
+ end
213
+
214
+ it "doesn't update the document" do
215
+ expect(document.reload.origin).to eq("London")
216
+ end
217
+ end
203
218
  end
204
219
 
205
220
  describe "#fail_due_to_valiation!" do
@@ -70,6 +70,20 @@ describe Mongoid::PersistenceContext do
70
70
  { collection: :other }
71
71
  end
72
72
 
73
+ context 'when the method throws an error' do
74
+
75
+ let!(:persistence_context) do
76
+ described_class.set(object, options).tap do |cxt|
77
+ allow(cxt).to receive(:client).and_raise(Mongoid::Errors::NoClientConfig.new('default'))
78
+ end
79
+ end
80
+
81
+ it 'clears the context anyway' do
82
+ begin; described_class.clear(object); rescue; end
83
+ expect(described_class.get(object)).to be(nil)
84
+ end
85
+ end
86
+
73
87
  context 'when there has been a persistence context set on the current thread' do
74
88
 
75
89
  let!(:persistence_context) do