mongoid 7.0.0.beta → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
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