mongoid 5.0.0 → 5.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -0
  4. data/CHANGELOG.md +54 -2
  5. data/lib/config/locales/en.yml +1 -1
  6. data/lib/mongoid/attributes.rb +1 -1
  7. data/lib/mongoid/clients.rb +7 -4
  8. data/lib/mongoid/clients/options.rb +2 -2
  9. data/lib/mongoid/contextual/aggregable/mongo.rb +2 -1
  10. data/lib/mongoid/contextual/geo_near.rb +1 -1
  11. data/lib/mongoid/contextual/memory.rb +4 -1
  12. data/lib/mongoid/contextual/mongo.rb +4 -5
  13. data/lib/mongoid/document.rb +1 -0
  14. data/lib/mongoid/indexable/specification.rb +3 -5
  15. data/lib/mongoid/indexable/validators/options.rb +7 -1
  16. data/lib/mongoid/matchable/exists.rb +1 -1
  17. data/lib/mongoid/persistable.rb +2 -1
  18. data/lib/mongoid/persistable/creatable.rb +1 -1
  19. data/lib/mongoid/persistable/deletable.rb +1 -1
  20. data/lib/mongoid/persistable/updatable.rb +2 -2
  21. data/lib/mongoid/positional.rb +75 -0
  22. data/lib/mongoid/relations/counter_cache.rb +19 -0
  23. data/lib/mongoid/relations/eager/base.rb +4 -2
  24. data/lib/mongoid/relations/embedded/batchable.rb +10 -3
  25. data/lib/mongoid/relations/proxy.rb +1 -1
  26. data/lib/mongoid/relations/touchable.rb +1 -1
  27. data/lib/mongoid/scopable.rb +6 -5
  28. data/lib/mongoid/selectable.rb +36 -1
  29. data/lib/mongoid/threaded.rb +34 -2
  30. data/lib/mongoid/timestamps/created.rb +1 -2
  31. data/lib/mongoid/timestamps/timeless.rb +19 -2
  32. data/lib/mongoid/timestamps/updated.rb +1 -1
  33. data/lib/mongoid/traversable.rb +1 -1
  34. data/lib/mongoid/version.rb +1 -1
  35. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +3 -1
  36. data/spec/app/models/account.rb +8 -0
  37. data/spec/app/models/answer.rb +2 -0
  38. data/spec/app/models/article.rb +2 -0
  39. data/spec/app/models/author.rb +2 -0
  40. data/spec/app/models/baby.rb +4 -0
  41. data/spec/app/models/book.rb +2 -0
  42. data/spec/app/models/consumption_period.rb +7 -0
  43. data/spec/app/models/exhibitor.rb +1 -0
  44. data/spec/app/models/kaleidoscope.rb +6 -0
  45. data/spec/app/models/kangaroo.rb +4 -0
  46. data/spec/app/models/note.rb +3 -0
  47. data/spec/app/models/page.rb +11 -0
  48. data/spec/app/models/simple.rb +5 -0
  49. data/spec/config/mongoid.yml +3 -1
  50. data/spec/mongoid/atomic/paths_spec.rb +17 -10
  51. data/spec/mongoid/attributes_spec.rb +2 -2
  52. data/spec/mongoid/clients/options_spec.rb +15 -0
  53. data/spec/mongoid/clients_spec.rb +6 -2
  54. data/spec/mongoid/config_spec.rb +3 -2
  55. data/spec/mongoid/contextual/aggregable/mongo_spec.rb +25 -2
  56. data/spec/mongoid/contextual/atomic_spec.rb +6 -6
  57. data/spec/mongoid/contextual/mongo_spec.rb +28 -75
  58. data/spec/mongoid/criteria_spec.rb +54 -0
  59. data/spec/mongoid/fields/standard_spec.rb +1 -1
  60. data/spec/mongoid/fields_spec.rb +1 -1
  61. data/spec/mongoid/indexable/specification_spec.rb +1 -1
  62. data/spec/mongoid/indexable_spec.rb +7 -7
  63. data/spec/mongoid/interceptable_spec.rb +55 -0
  64. data/spec/mongoid/persistable/creatable_spec.rb +19 -0
  65. data/spec/mongoid/persistable/destroyable_spec.rb +50 -0
  66. data/spec/mongoid/persistable/incrementable_spec.rb +56 -4
  67. data/spec/mongoid/persistable/pushable_spec.rb +11 -0
  68. data/spec/mongoid/persistable/savable_spec.rb +20 -2
  69. data/spec/mongoid/positional_spec.rb +221 -0
  70. data/spec/mongoid/query_cache_spec.rb +19 -0
  71. data/spec/mongoid/relations/auto_save_spec.rb +1 -1
  72. data/spec/mongoid/relations/bindings/referenced/many_to_many_spec.rb +1 -1
  73. data/spec/mongoid/relations/counter_cache_spec.rb +64 -11
  74. data/spec/mongoid/relations/eager/has_many_spec.rb +37 -0
  75. data/spec/mongoid/relations/eager_spec.rb +11 -0
  76. data/spec/mongoid/relations/embedded/many_spec.rb +38 -9
  77. data/spec/mongoid/relations/embedded/one_spec.rb +1 -1
  78. data/spec/mongoid/relations/proxy_spec.rb +22 -0
  79. data/spec/mongoid/relations/reflections_spec.rb +1 -1
  80. data/spec/mongoid/scopable_spec.rb +160 -19
  81. data/spec/mongoid/selectable_spec.rb +16 -6
  82. data/spec/mongoid/timestamps/timeless_spec.rb +17 -0
  83. data/spec/mongoid/validatable/uniqueness_spec.rb +17 -0
  84. metadata +40 -5
  85. metadata.gz.sig +3 -0
@@ -123,6 +123,25 @@ describe Mongoid::QueryCache do
123
123
  end
124
124
  end
125
125
  end
126
+
127
+ context "when query caching is enabled and the batch_size is set" do
128
+
129
+ around(:each) do |example|
130
+ query_cache_enabled = Mongoid::QueryCache.enabled?
131
+ Mongoid::QueryCache.enabled = true
132
+ example.run
133
+ Mongoid::QueryCache.enabled = query_cache_enabled
134
+ end
135
+
136
+ it "does not raise an error when requesting the second batch" do
137
+ expect {
138
+ Band.batch_size(4).where(:views.gte => 0).each do |doc|
139
+ doc.set(likes: Random.rand(100))
140
+ end
141
+ }.not_to raise_error
142
+ end
143
+
144
+ end
126
145
  end
127
146
 
128
147
  context "when querying in different collection" do
@@ -219,7 +219,7 @@ describe Mongoid::Relations::AutoSave do
219
219
  end
220
220
  end
221
221
 
222
- context "when it has two ralations with autosaves" do
222
+ context "when it has two relations with autosaves" do
223
223
 
224
224
  before do
225
225
  Person.autosave(Person.relations["drugs"].merge!(autosave: true))
@@ -112,7 +112,7 @@ describe Mongoid::Relations::Bindings::Referenced::ManyToMany do
112
112
  binding.bind_one(target.first)
113
113
  end
114
114
 
115
- it "never performs a persistance operation" do
115
+ it "never performs a persistence operation" do
116
116
  expect(person).to receive(:delete).never
117
117
  expect(person).to receive(:save).never
118
118
  expect(preference).to receive(:delete).never
@@ -290,27 +290,80 @@ describe Mongoid::Relations::CounterCache do
290
290
 
291
291
  describe "#add_counter_cache_callbacks" do
292
292
 
293
- let(:person) do
294
- Person.create
295
- end
293
+ context "when parent is not frozen" do
296
294
 
297
- let!(:drug) do
298
- person.drugs.create
299
- end
295
+ context 'when #destroy is called on the object' do
300
296
 
301
- context "when parent is not frozen" do
297
+ let(:person) do
298
+ Person.create
299
+ end
302
300
 
303
- before do
304
- drug.destroy
301
+ let!(:drug) do
302
+ person.drugs.create
303
+ end
304
+
305
+ before do
306
+ drug.destroy
307
+ end
308
+
309
+ it "updates the counter cache" do
310
+ expect(person.drugs_count).to eq(0)
311
+ end
305
312
  end
306
313
 
307
- it "before_destroy updates counter cache" do
308
- expect(person.drugs_count).to eq(0)
314
+ context 'when #create is called on the object' do
315
+
316
+ let(:person) do
317
+ Person.create { |p| p.drugs += [Drug.create, Drug.create] }
318
+ end
319
+
320
+ it "updates the counter cache" do
321
+ expect(person.drugs_count).to eq(2)
322
+ end
323
+ end
324
+
325
+ context 'when #update is called on the object' do
326
+
327
+ let(:person1) do
328
+ Person.create { |p| p.drugs += [Drug.create, Drug.create] }
329
+ end
330
+
331
+ let(:drug) do
332
+ person1.drugs.first
333
+ end
334
+
335
+ let(:person2) do
336
+ Person.create
337
+ end
338
+
339
+ before do
340
+ drug.update_attribute(:person, person2)
341
+ end
342
+
343
+ it "updates the current counter cache" do
344
+ expect(drug.person.drugs_count).to eq(1)
345
+ end
346
+
347
+ it "updates the current counter cache" do
348
+ expect(person2.drugs_count).to eq(1)
349
+ end
350
+
351
+ it "updates the original object's counter cache" do
352
+ expect(person1.reload.drugs_count).to eq(1)
353
+ end
309
354
  end
310
355
  end
311
356
 
312
357
  context "when parent is frozen" do
313
358
 
359
+ let(:person) do
360
+ Person.create
361
+ end
362
+
363
+ let!(:drug) do
364
+ person.drugs.create
365
+ end
366
+
314
367
  before do
315
368
  person.destroy
316
369
  drug.destroy
@@ -177,6 +177,43 @@ describe Mongoid::Relations::Eager::HasMany do
177
177
  expect(eager.last.ivar(:posts)).to be_empty
178
178
  end
179
179
  end
180
+
181
+ context "when the child has a default scope" do
182
+
183
+ let(:criteria) do
184
+ Exhibitor.where(:status.ne => "removed")
185
+ end
186
+
187
+ let(:exhibitorPresent) do
188
+ Exhibitor.create!(status: "present")
189
+ end
190
+
191
+ let(:exhibitorRemoved) do
192
+ Exhibitor.create!(status: "removed")
193
+ end
194
+
195
+ let(:exhibitionIncludesExhibitors) do
196
+ Exhibition.includes(:exhibitors).first
197
+ end
198
+
199
+ before do
200
+ Exhibitor.default_scope ->{ criteria }
201
+ exhibition = Exhibition.create!
202
+ exhibition.exhibitors << exhibitorPresent
203
+ exhibition.exhibitors << exhibitorRemoved
204
+ exhibitionIncludesExhibitors
205
+ end
206
+
207
+ after do
208
+ Exhibitor.default_scoping = nil
209
+ end
210
+
211
+ it "does not send another query when the children are accessed" do
212
+ expect_query(0) do
213
+ expect(exhibitionIncludesExhibitors.exhibitors).to eq( [exhibitorPresent] )
214
+ end
215
+ end
216
+ end
180
217
  end
181
218
 
182
219
  context "when the relation is polymorphic" do
@@ -131,6 +131,17 @@ describe Mongoid::Relations::Eager do
131
131
 
132
132
  context.eager_load(docs)
133
133
  end
134
+
135
+ context 'when combined with a #find_by' do
136
+
137
+ let!(:person) do
138
+ Person.create!(title: 'manager')
139
+ end
140
+
141
+ it 'executes the find_by' do
142
+ expect(criteria.find_by(title: 'manager')).to eq(person)
143
+ end
144
+ end
134
145
  end
135
146
 
136
147
  context "when including multiple relations" do
@@ -2748,26 +2748,55 @@ describe Mongoid::Relations::Embedded::Many do
2748
2748
  end
2749
2749
 
2750
2750
  let!(:location) do
2751
- address.locations.create(name: "work")
2751
+ address.locations.create(name: "vacation", number: 0)
2752
+ address.locations.create(name: "work", number: 3)
2752
2753
  end
2753
2754
 
2754
- context "when updating with a hash" do
2755
+ context "when updating with replacement of embedded array" do
2756
+
2757
+ context "when updating with a hash" do
2758
+
2759
+ before do
2760
+ address.update_attributes(locations: [{ name: "home" }])
2761
+ end
2762
+
2763
+ it "updates the attributes" do
2764
+ expect(address.locations.first.name).to eq("home")
2765
+ end
2766
+
2767
+ it "overwrites the existing documents" do
2768
+ expect(address.locations.count).to eq(1)
2769
+ end
2770
+
2771
+ it "persists the changes" do
2772
+ expect(address.reload.locations.count).to eq(1)
2773
+ end
2774
+ end
2775
+ end
2776
+
2777
+ context "when updating a field in a document of the embedded array" do
2755
2778
 
2756
2779
  before do
2757
- address.update_attributes(locations: [{ name: "home" }])
2780
+ location.number = 7
2781
+ location.save
2758
2782
  end
2759
2783
 
2760
- it "updates the attributes" do
2761
- expect(address.locations.first.name).to eq("home")
2784
+ let(:updated_location_number) do
2785
+ person.reload.addresses.first.locations.find(location.id).number
2762
2786
  end
2763
2787
 
2764
- it "overwrites the existing documents" do
2765
- expect(address.locations.count).to eq(1)
2788
+ let(:updated_location_name) do
2789
+ person.reload.addresses.first.locations.find(location.id).name
2766
2790
  end
2767
2791
 
2768
- it "persists the changes" do
2769
- expect(address.reload.locations.count).to eq(1)
2792
+ it "the change is persisted" do
2793
+ expect(updated_location_number).to eq(7)
2794
+ end
2795
+
2796
+ it "the other field remains unaffected" do
2797
+ expect(updated_location_name).to eq("work")
2770
2798
  end
2799
+
2771
2800
  end
2772
2801
  end
2773
2802
 
@@ -963,7 +963,7 @@ describe Mongoid::Relations::Embedded::One do
963
963
  end
964
964
 
965
965
  before do
966
- person.reload.addresses.first.code = code
966
+ address_two.code = code
967
967
  end
968
968
 
969
969
  it "reloads the correct number" do
@@ -2,6 +2,28 @@ require "spec_helper"
2
2
 
3
3
  describe Mongoid::Relations::Proxy do
4
4
 
5
+ describe '#with', if: non_legacy_server? do
6
+
7
+ let(:circus) do
8
+ Circus.new
9
+ end
10
+
11
+ let(:animal) do
12
+ Animal.new
13
+ end
14
+
15
+ before do
16
+ circus.animals << animal
17
+ circus.save
18
+ end
19
+
20
+ it 'uses the new persistence options' do
21
+ expect {
22
+ animal.with(write: { w: 100 }).update_attribute(:name, 'kangaroo')
23
+ }.to raise_exception(Mongo::Error::OperationFailure)
24
+ end
25
+ end
26
+
5
27
  describe "#find" do
6
28
  let(:person) do
7
29
  Person.create
@@ -34,7 +34,7 @@ describe Mongoid::Relations::Reflections do
34
34
  context "when the name does not exist" do
35
35
 
36
36
  let(:relation) do
37
- klass.reflect_on_association(:nonexistant)
37
+ klass.reflect_on_association(:nonexistent)
38
38
  end
39
39
 
40
40
  it "returns nil" do
@@ -51,7 +51,7 @@ describe Mongoid::Scopable do
51
51
  end
52
52
 
53
53
  let(:rand_criteria) do
54
- ->{ Band.gt(likes: rand(100)) }
54
+ ->{ Band.gt(likes: Mongo::Monitoring.next_operation_id) }
55
55
  end
56
56
 
57
57
  before do
@@ -143,16 +143,34 @@ describe Mongoid::Scopable do
143
143
  Band.where(name: "Depeche Mode")
144
144
  end
145
145
 
146
- before do
147
- Mongoid::Threaded.current_scope = criteria
148
- end
146
+ context "when using #current_scope=scope" do
149
147
 
150
- after do
151
- Mongoid::Threaded.current_scope = nil
148
+ before do
149
+ Mongoid::Threaded.current_scope = criteria
150
+ end
151
+
152
+ after do
153
+ Mongoid::Threaded.current_scope = nil
154
+ end
155
+
156
+ it "returns the criteria on the stack" do
157
+ expect(Band.queryable).to eq(criteria)
158
+ end
152
159
  end
153
160
 
154
- it "returns the criteria on the stack" do
155
- expect(Band.queryable).to eq(criteria)
161
+ context "when using #set_current_scope(scope, klass)" do
162
+
163
+ before do
164
+ Mongoid::Threaded.set_current_scope(criteria, Band)
165
+ end
166
+
167
+ after do
168
+ Mongoid::Threaded.set_current_scope(nil, Band)
169
+ end
170
+
171
+ it "returns the criteria on the stack" do
172
+ expect(Band.queryable).to eq(criteria)
173
+ end
156
174
  end
157
175
  end
158
176
  end
@@ -398,6 +416,45 @@ describe Mongoid::Scopable do
398
416
  end
399
417
  end
400
418
 
419
+ context 'when the block is an none scope' do
420
+
421
+ before do
422
+ Simple.create!(name: 'Emily')
423
+ end
424
+
425
+ context 'when there is no default scope' do
426
+
427
+ before do
428
+ Simple.scope(:nothing, ->{ none })
429
+ end
430
+
431
+ it 'returns no results' do
432
+ expect(Simple.nothing).to be_empty
433
+ end
434
+ end
435
+
436
+ context 'when there is a default scope' do
437
+
438
+ let(:criteria) do
439
+ Simple.where(name: "Emily")
440
+ end
441
+
442
+ before do
443
+ Simple.default_scope ->{ criteria }
444
+ Simple.scope(:nothing, ->{ none })
445
+ end
446
+
447
+ after do
448
+ Simple.default_scoping = nil
449
+ end
450
+
451
+ it 'returns no results' do
452
+ expect(Simple.nothing).to be_empty
453
+ end
454
+ end
455
+
456
+ end
457
+
401
458
  context "when no block is provided" do
402
459
 
403
460
  before do
@@ -509,6 +566,58 @@ describe Mongoid::Scopable do
509
566
  expect(criteria.selector).to eq({ "origin" => "England" })
510
567
  end
511
568
  end
569
+
570
+ context "when chaining scopes through more than one model" do
571
+
572
+ before do
573
+ Author.scope(:author, -> { where(author: true) } )
574
+ Article.scope(:is_public, -> { where(public: true) } )
575
+ Article.scope(:authored, -> {
576
+ author_ids = Author.author.pluck(:id)
577
+ where(:author_id.in => author_ids)
578
+ })
579
+
580
+ Author.create(author: true, id: 1)
581
+ Author.create(author: true, id: 2)
582
+ Author.create(author: true, id: 3)
583
+ Article.create(author_id: 1, public: true)
584
+ Article.create(author_id: 2, public: true)
585
+ Article.create(author_id: 3, public: false)
586
+ end
587
+
588
+ after do
589
+ class << Article
590
+ undef_method :is_public
591
+ undef_method :authored
592
+ end
593
+ Article._declared_scopes.clear
594
+ class << Author
595
+ undef_method :author
596
+ end
597
+ Author._declared_scopes.clear
598
+ end
599
+
600
+ context "when calling another model's scope from within a scope" do
601
+
602
+ let(:authored_count) do
603
+ Article.authored.size
604
+ end
605
+
606
+ it "returns the correct documents" do
607
+ expect(authored_count).to eq(3)
608
+ end
609
+ end
610
+
611
+ context "when calling another model's scope from within a chained scope" do
612
+ let(:is_public_authored_count) do
613
+ Article.is_public.authored.size
614
+ end
615
+
616
+ it "returns the correct documents" do
617
+ expect(is_public_authored_count).to eq(2)
618
+ end
619
+ end
620
+ end
512
621
  end
513
622
  end
514
623
 
@@ -784,17 +893,38 @@ describe Mongoid::Scopable do
784
893
 
785
894
  context "when default scope is in a super class" do
786
895
 
787
- before do
788
- Band.scope(:active, ->{ Band.where(active: true) })
789
- end
896
+ context "when scope is already defined in parent class" do
790
897
 
791
- let(:unscoped) do
792
- class U2 < Band; end
793
- U2.unscoped.active
898
+ let(:unscoped) do
899
+ class U1 < Kaleidoscope; end
900
+ U1.unscoped.activated
901
+ end
902
+
903
+ it "clears default scope" do
904
+ expect(unscoped.selector).to eq({ "active" => true })
905
+ end
794
906
  end
795
907
 
796
- it "clears default scope" do
797
- expect(unscoped.selector).to eq({ "active" => true })
908
+ context "when the scope is created dynamically" do
909
+
910
+ before do
911
+ Band.scope(:active, ->{ Band.where(active: true) })
912
+ end
913
+
914
+ after do
915
+ class << Band
916
+ undef_method :active
917
+ end
918
+ end
919
+
920
+ let(:unscoped) do
921
+ class U2 < Band; end
922
+ U2.unscoped.active
923
+ end
924
+
925
+ it "clears default scope" do
926
+ expect(unscoped.selector).to eq({ "active" => true })
927
+ end
798
928
  end
799
929
  end
800
930
  end
@@ -899,9 +1029,20 @@ describe Mongoid::Scopable do
899
1029
  end
900
1030
  end
901
1031
 
902
- it "pops the criteria off the stack" do
903
- Band.with_scope(criteria) {}
904
- expect(Mongoid::Threaded.current_scope).to be_nil
1032
+ context "when using #current_scope" do
1033
+
1034
+ it "pops the criteria off the stack" do
1035
+ Band.with_scope(criteria) do;end
1036
+ expect(Mongoid::Threaded.current_scope).to be_nil
1037
+ end
1038
+ end
1039
+
1040
+ context "when using #current_scope(klass)" do
1041
+
1042
+ it "pops the criteria off the stack" do
1043
+ Band.with_scope(criteria) do;end
1044
+ expect(Mongoid::Threaded.current_scope(Band)).to be_nil
1045
+ end
905
1046
  end
906
1047
  end
907
1048