mongoid 5.0.0 → 5.0.1

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 (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