mongoid 7.0.4 → 7.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/LICENSE +1 -0
  5. data/README.md +3 -2
  6. data/Rakefile +26 -0
  7. data/lib/mongoid.rb +2 -1
  8. data/lib/mongoid/association/embedded/embeds_many.rb +2 -1
  9. data/lib/mongoid/association/embedded/embeds_one.rb +2 -1
  10. data/lib/mongoid/association/proxy.rb +1 -1
  11. data/lib/mongoid/atomic.rb +13 -3
  12. data/lib/mongoid/atomic/paths/embedded.rb +1 -1
  13. data/lib/mongoid/attributes.rb +28 -20
  14. data/lib/mongoid/attributes/dynamic.rb +15 -14
  15. data/lib/mongoid/clients/sessions.rb +20 -4
  16. data/lib/mongoid/config/environment.rb +21 -8
  17. data/lib/mongoid/criteria.rb +7 -1
  18. data/lib/mongoid/criteria/modifiable.rb +13 -2
  19. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
  20. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +3 -3
  21. data/lib/mongoid/criteria/queryable/extensions/time.rb +1 -1
  22. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +12 -0
  23. data/lib/mongoid/criteria/queryable/key.rb +67 -8
  24. data/lib/mongoid/criteria/queryable/mergeable.rb +5 -4
  25. data/lib/mongoid/criteria/queryable/selectable.rb +3 -4
  26. data/lib/mongoid/criteria/queryable/selector.rb +9 -31
  27. data/lib/mongoid/extensions/hash.rb +4 -2
  28. data/lib/mongoid/extensions/regexp.rb +1 -1
  29. data/lib/mongoid/extensions/string.rb +2 -2
  30. data/lib/mongoid/fields.rb +2 -1
  31. data/lib/mongoid/matchable.rb +14 -15
  32. data/lib/mongoid/matchable/all.rb +4 -3
  33. data/lib/mongoid/matchable/default.rb +71 -24
  34. data/lib/mongoid/matchable/regexp.rb +2 -2
  35. data/lib/mongoid/persistable/pushable.rb +11 -2
  36. data/lib/mongoid/persistence_context.rb +6 -6
  37. data/lib/mongoid/positional.rb +1 -1
  38. data/lib/mongoid/query_cache.rb +24 -11
  39. data/lib/mongoid/validatable/macros.rb +1 -1
  40. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  41. data/lib/mongoid/version.rb +2 -1
  42. data/lib/rails/generators/mongoid/model/templates/model.rb.tt +1 -1
  43. data/spec/README.md +18 -0
  44. data/spec/app/models/delegating_patient.rb +16 -0
  45. data/spec/integration/app_spec.rb +192 -0
  46. data/spec/integration/associations/embedded_spec.rb +62 -0
  47. data/spec/integration/criteria/date_field_spec.rb +41 -0
  48. data/spec/integration/criteria/time_with_zone_spec.rb +32 -0
  49. data/spec/integration/document_spec.rb +22 -0
  50. data/spec/integration/matchable_spec.rb +680 -0
  51. data/spec/lite_spec_helper.rb +15 -5
  52. data/spec/mongoid/association/embedded/embeds_many_models.rb +53 -0
  53. data/spec/mongoid/association/embedded/embeds_many_spec.rb +10 -0
  54. data/spec/mongoid/association/embedded/embeds_one_spec.rb +0 -2
  55. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +140 -1
  56. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +105 -0
  57. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +2 -1
  58. data/spec/mongoid/attributes/dynamic_spec.rb +153 -0
  59. data/spec/mongoid/attributes_spec.rb +19 -7
  60. data/spec/mongoid/clients/factory_spec.rb +2 -2
  61. data/spec/mongoid/clients/options_spec.rb +4 -4
  62. data/spec/mongoid/clients/sessions_spec.rb +20 -7
  63. data/spec/mongoid/clients/transactions_spec.rb +36 -15
  64. data/spec/mongoid/clients_spec.rb +2 -2
  65. data/spec/mongoid/contextual/atomic_spec.rb +20 -10
  66. data/spec/mongoid/contextual/geo_near_spec.rb +12 -2
  67. data/spec/mongoid/contextual/map_reduce_spec.rb +20 -5
  68. data/spec/mongoid/contextual/mongo_spec.rb +76 -53
  69. data/spec/mongoid/criteria/modifiable_spec.rb +59 -10
  70. data/spec/mongoid/criteria/queryable/extensions/numeric_spec.rb +54 -0
  71. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +7 -7
  72. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +1 -1
  73. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +19 -7
  74. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +28 -1
  75. data/spec/mongoid/criteria/queryable/key_spec.rb +48 -6
  76. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +762 -0
  77. data/spec/mongoid/criteria/queryable/selectable_spec.rb +5 -224
  78. data/spec/mongoid/criteria/queryable/selector_spec.rb +37 -0
  79. data/spec/mongoid/criteria_spec.rb +7 -2
  80. data/spec/mongoid/document_fields_spec.rb +88 -0
  81. data/spec/mongoid/document_persistence_context_spec.rb +33 -0
  82. data/spec/mongoid/indexable_spec.rb +6 -4
  83. data/spec/mongoid/matchable/default_spec.rb +10 -3
  84. data/spec/mongoid/matchable/regexp_spec.rb +2 -2
  85. data/spec/mongoid/matchable_spec.rb +2 -2
  86. data/spec/mongoid/persistable/pushable_spec.rb +55 -1
  87. data/spec/mongoid/query_cache_spec.rb +62 -8
  88. data/spec/mongoid/relations/proxy_spec.rb +1 -1
  89. data/spec/mongoid/scopable_spec.rb +2 -1
  90. data/spec/mongoid/tasks/database_rake_spec.rb +13 -13
  91. data/spec/mongoid/tasks/database_spec.rb +1 -1
  92. data/spec/mongoid/validatable/uniqueness_spec.rb +33 -6
  93. data/spec/spec_helper.rb +4 -37
  94. data/spec/support/child_process_helper.rb +76 -0
  95. data/spec/support/cluster_config.rb +158 -0
  96. data/spec/support/constraints.rb +201 -30
  97. data/spec/support/expectations.rb +17 -3
  98. data/spec/support/spec_config.rb +12 -4
  99. metadata +490 -454
  100. metadata.gz.sig +0 -0
@@ -315,7 +315,7 @@ describe String do
315
315
  context "when provided a regex" do
316
316
 
317
317
  let(:regex) do
318
- /^[123]/
318
+ /\A[123]/.freeze
319
319
  end
320
320
 
321
321
  let(:evolved) do
@@ -346,23 +346,35 @@ describe Time do
346
346
 
347
347
  describe "#__evolve_date__" do
348
348
 
349
- let(:time) do
350
- Time.new(2010, 1, 1, 12, 0, 0)
351
- end
352
-
353
349
  let(:evolved) do
354
350
  time.__evolve_date__
355
351
  end
356
352
 
357
- it "returns midnight utc" do
358
- expect(evolved).to eq(Time.utc(2010, 1, 1, 0, 0, 0))
353
+ context 'beginning of day' do
354
+ let(:time) do
355
+ Time.new(2010, 1, 1, 0, 0, 1).freeze
356
+ end
357
+
358
+ it "returns midnight utc" do
359
+ expect(evolved).to eq(Time.utc(2010, 1, 1, 0, 0, 0))
360
+ end
361
+ end
362
+
363
+ context 'end of day' do
364
+ let(:time) do
365
+ Time.new(2010, 1, 1, 23, 59, 59).freeze
366
+ end
367
+
368
+ it "returns midnight utc" do
369
+ expect(evolved).to eq(Time.utc(2010, 1, 1, 0, 0, 0))
370
+ end
359
371
  end
360
372
  end
361
373
 
362
374
  describe "#__evolve_time__" do
363
375
 
364
376
  let(:time) do
365
- Time.new(2010, 1, 1, 12, 0, 0)
377
+ Time.new(2010, 1, 1, 12, 0, 0).freeze
366
378
  end
367
379
 
368
380
  let(:evolved) do
@@ -322,10 +322,37 @@ describe ActiveSupport::TimeWithZone do
322
322
  end
323
323
  end
324
324
 
325
+ describe "#__evolve_date__" do
326
+
327
+ let(:evolved) do
328
+ time.__evolve_date__
329
+ end
330
+
331
+ context 'beginning of day' do
332
+ let(:time) do
333
+ time_zone.local(2010, 1, 1, 0, 0, 1).freeze
334
+ end
335
+
336
+ it "returns midnight utc" do
337
+ expect(evolved).to eq(Time.utc(2010, 1, 1, 0, 0, 0))
338
+ end
339
+ end
340
+
341
+ context 'end of day' do
342
+ let(:time) do
343
+ time_zone.local(2010, 1, 1, 23, 59, 59).freeze
344
+ end
345
+
346
+ it "returns midnight utc" do
347
+ expect(evolved).to eq(Time.utc(2010, 1, 1, 0, 0, 0))
348
+ end
349
+ end
350
+ end
351
+
325
352
  describe "#__evolve_time__" do
326
353
 
327
354
  let(:date) do
328
- time_zone.local(2010, 1, 1, 12, 0, 0)
355
+ time_zone.local(2010, 1, 1, 12, 0, 0).freeze
329
356
  end
330
357
 
331
358
  let(:evolved) do
@@ -23,16 +23,58 @@ describe Mongoid::Criteria::Queryable::Key do
23
23
 
24
24
  describe "#__expr_part__" do
25
25
 
26
- let(:key) do
27
- described_class.new("field", :__union__, "$all")
26
+ context 'operator only' do
27
+ let(:key) do
28
+ described_class.new("field", :__union__, "$all")
29
+ end
30
+
31
+ let(:specified) do
32
+ key.__expr_part__([ 1, 2 ])
33
+ end
34
+
35
+ it "returns the name plus operator and value" do
36
+ expect(specified).to eq({ "field" => { "$all" => [ 1, 2 ] }})
37
+ end
28
38
  end
29
39
 
30
- let(:specified) do
31
- key.__expr_part__([ 1, 2 ])
40
+ context 'operator and expanded only' do
41
+ let(:key) do
42
+ described_class.new("field", :__override__, "$geoIntersects", '$geometry')
43
+ end
44
+
45
+ let(:specified) do
46
+ key.__expr_part__([ 1, 10 ])
47
+ end
48
+
49
+ it "returns the query expression" do
50
+ expect(specified).to eq({ "field" => {
51
+ '$geoIntersects' => {
52
+ '$geometry' => [1, 10],
53
+ },
54
+ }})
55
+ end
32
56
  end
33
57
 
34
- it "returns the name plus operator and value" do
35
- expect(specified).to eq({ "field" => { "$all" => [ 1, 2 ] }})
58
+ context 'operator, expanded and block' do
59
+ let(:key) do
60
+ described_class.new("field", :__override__, "$geoIntersects", '$geometry') do |value|
61
+ { "type" => 'Point', "coordinates" => value }
62
+ end
63
+ end
64
+
65
+ let(:specified) do
66
+ key.__expr_part__([ 1, 10 ])
67
+ end
68
+
69
+ it "returns the query expression" do
70
+ expect(specified).to eq({ "field" => {
71
+ '$geoIntersects' => {
72
+ '$geometry' => {
73
+ 'type' => 'Point', 'coordinates' => [1, 10],
74
+ },
75
+ },
76
+ }})
77
+ end
36
78
  end
37
79
  end
38
80
 
@@ -0,0 +1,762 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require "spec_helper"
5
+
6
+ describe Mongoid::Criteria::Queryable::Selectable do
7
+
8
+ let(:query) do
9
+ Mongoid::Query.new("id" => "_id")
10
+ end
11
+
12
+ describe "#and" do
13
+
14
+ context "when provided no criterion" do
15
+
16
+ let(:selection) do
17
+ query.and
18
+ end
19
+
20
+ it "does not add any criterion" do
21
+ expect(selection.selector).to eq({})
22
+ end
23
+
24
+ it "returns the query" do
25
+ expect(selection).to eq(query)
26
+ end
27
+
28
+ it "returns a cloned query" do
29
+ expect(selection).to_not equal(query)
30
+ end
31
+ end
32
+
33
+ context "when provided nil" do
34
+
35
+ let(:selection) do
36
+ query.and(nil)
37
+ end
38
+
39
+ it "does not add any criterion" do
40
+ expect(selection.selector).to eq({})
41
+ end
42
+
43
+ it "returns the query" do
44
+ expect(selection).to eq(query)
45
+ end
46
+
47
+ it "returns a cloned query" do
48
+ expect(selection).to_not equal(query)
49
+ end
50
+ end
51
+
52
+ context "when provided a single criterion" do
53
+
54
+ let(:selection) do
55
+ query.and(field: [ 1, 2 ])
56
+ end
57
+
58
+ it "adds the $and selector" do
59
+ expect(selection.selector).to eq({
60
+ "$and" => [{ "field" => [ 1, 2 ] }]
61
+ })
62
+ end
63
+
64
+ it "returns a cloned query" do
65
+ expect(selection).to_not equal(query)
66
+ end
67
+ end
68
+
69
+ context "when provided a nested criterion" do
70
+
71
+ let(:selection) do
72
+ query.and(:test.elem_match => { :field.in => [ 1, 2 ] })
73
+ end
74
+
75
+ it "adds the $and selector" do
76
+ expect(selection.selector).to eq({
77
+ "$and" => [{ "test" => { "$elemMatch" => { "field" => { "$in" => [ 1, 2 ] }}}}]
78
+ })
79
+ end
80
+
81
+ it "returns a cloned query" do
82
+ expect(selection).to_not equal(query)
83
+ end
84
+ end
85
+
86
+ context "when provided multiple criterion" do
87
+
88
+ context "when the criterion is already included" do
89
+
90
+ let(:selection) do
91
+ query.and({ first: [ 1, 2 ] }).and({ first: [ 1, 2 ] })
92
+ end
93
+
94
+ it "does not duplicate the $and selector" do
95
+ expect(selection.selector).to eq({
96
+ "$and" => [
97
+ { "first" => [ 1, 2 ] }
98
+ ]
99
+ })
100
+ end
101
+
102
+ it "returns a cloned query" do
103
+ expect(selection).to_not equal(query)
104
+ end
105
+ end
106
+
107
+ context "when the criterion are for different fields" do
108
+
109
+ let(:selection) do
110
+ query.and({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
111
+ end
112
+
113
+ it "adds the $and selector" do
114
+ expect(selection.selector).to eq({
115
+ "$and" => [
116
+ { "first" => [ 1, 2 ] },
117
+ { "second" => [ 3, 4 ] }
118
+ ]
119
+ })
120
+ end
121
+
122
+ it "returns a cloned query" do
123
+ expect(selection).to_not equal(query)
124
+ end
125
+ end
126
+
127
+ context "when the criterion are on the same field" do
128
+
129
+ let(:selection) do
130
+ query.and({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
131
+ end
132
+
133
+ it "appends both $and expressions" do
134
+ expect(selection.selector).to eq({
135
+ "$and" => [
136
+ { "first" => [ 1, 2 ] },
137
+ { "first" => [ 3, 4 ] }
138
+ ]
139
+ })
140
+ end
141
+
142
+ it "returns a cloned query" do
143
+ expect(selection).to_not equal(query)
144
+ end
145
+ end
146
+ end
147
+
148
+ context "when chaining the criterion" do
149
+
150
+ context "when the criterion are for different fields" do
151
+
152
+ let(:selection) do
153
+ query.and(first: [ 1, 2 ]).and(second: [ 3, 4 ])
154
+ end
155
+
156
+ it "adds the $and selectors" do
157
+ expect(selection.selector).to eq({
158
+ "$and" => [
159
+ { "first" => [ 1, 2 ] },
160
+ { "second" => [ 3, 4 ] }
161
+ ]
162
+ })
163
+ end
164
+
165
+ it "returns a cloned query" do
166
+ expect(selection).to_not equal(query)
167
+ end
168
+ end
169
+
170
+ context "when the criterion are on the same field" do
171
+
172
+ let(:selection) do
173
+ query.and(first: [ 1, 2 ]).and(first: [ 3, 4 ])
174
+ end
175
+
176
+ it "appends both $and expressions" do
177
+ expect(selection.selector).to eq({
178
+ "$and" => [
179
+ { "first" => [ 1, 2 ] },
180
+ { "first" => [ 3, 4 ] }
181
+ ]
182
+ })
183
+ end
184
+
185
+ it "returns a cloned query" do
186
+ expect(selection).to_not equal(query)
187
+ end
188
+ end
189
+ end
190
+ end
191
+
192
+ describe "#or" do
193
+
194
+ context "when provided no criterion" do
195
+
196
+ let(:selection) do
197
+ query.or
198
+ end
199
+
200
+ it "does not add any criterion" do
201
+ expect(selection.selector).to eq({})
202
+ end
203
+
204
+ it "returns the query" do
205
+ expect(selection).to eq(query)
206
+ end
207
+
208
+ it "returns a cloned query" do
209
+ expect(selection).to_not equal(query)
210
+ end
211
+ end
212
+
213
+ context "when provided nil" do
214
+
215
+ let(:selection) do
216
+ query.or(nil)
217
+ end
218
+
219
+ it "does not add any criterion" do
220
+ expect(selection.selector).to eq({})
221
+ end
222
+
223
+ it "returns the query" do
224
+ expect(selection).to eq(query)
225
+ end
226
+
227
+ it "returns a cloned query" do
228
+ expect(selection).to_not equal(query)
229
+ end
230
+ end
231
+
232
+ context "when provided a single criterion" do
233
+
234
+ let(:selection) do
235
+ query.or(field: [ 1, 2 ])
236
+ end
237
+
238
+ it "adds the $or selector" do
239
+ expect(selection.selector).to eq({
240
+ "$or" => [{ "field" => [ 1, 2 ] }]
241
+ })
242
+ end
243
+
244
+ it "returns a cloned query" do
245
+ expect(selection).to_not equal(query)
246
+ end
247
+ end
248
+
249
+ context "when provided multiple criterion" do
250
+
251
+ context "when the criterion are for different fields" do
252
+
253
+ let(:selection) do
254
+ query.or({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
255
+ end
256
+
257
+ it "adds the $or selector" do
258
+ expect(selection.selector).to eq({
259
+ "$or" => [
260
+ { "first" => [ 1, 2 ] },
261
+ { "second" => [ 3, 4 ] }
262
+ ]
263
+ })
264
+ end
265
+
266
+ it "returns a cloned query" do
267
+ expect(selection).to_not equal(query)
268
+ end
269
+ end
270
+
271
+ context "when a criterion has a selectable key" do
272
+
273
+ let(:selection) do
274
+ query.or({ first: [ 1, 2 ] }, { :second.gt => 3 })
275
+ end
276
+
277
+ it "adds the $or selector" do
278
+ expect(selection.selector).to eq({
279
+ "$or" => [
280
+ { "first" => [ 1, 2 ] },
281
+ { "second" => { "$gt" => 3 }}
282
+ ]
283
+ })
284
+ end
285
+
286
+ it "returns a cloned query" do
287
+ expect(selection).to_not equal(query)
288
+ end
289
+ end
290
+
291
+ context "when the criterion has an aliased field" do
292
+
293
+ let(:selection) do
294
+ query.or({ id: 1 })
295
+ end
296
+
297
+ it "adds the $or selector and aliases the field" do
298
+ expect(selection.selector).to eq({
299
+ "$or" => [ { "_id" => 1 } ]
300
+ })
301
+ end
302
+
303
+ it "returns a cloned query" do
304
+ expect(selection).to_not equal(query)
305
+ end
306
+ end
307
+
308
+ context "when a criterion is wrapped in an array" do
309
+
310
+ let(:selection) do
311
+ query.or([{ first: [ 1, 2 ] }, { :second.gt => 3 }])
312
+ end
313
+
314
+ it "adds the $or selector" do
315
+ expect(selection.selector).to eq({
316
+ "$or" => [
317
+ { "first" => [ 1, 2 ] },
318
+ { "second" => { "$gt" => 3 }}
319
+ ]
320
+ })
321
+ end
322
+
323
+ it "returns a cloned query" do
324
+ expect(selection).to_not equal(query)
325
+ end
326
+ end
327
+
328
+ context "when the criterion are on the same field" do
329
+
330
+ let(:selection) do
331
+ query.or({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
332
+ end
333
+
334
+ it "appends both $or expressions" do
335
+ expect(selection.selector).to eq({
336
+ "$or" => [
337
+ { "first" => [ 1, 2 ] },
338
+ { "first" => [ 3, 4 ] }
339
+ ]
340
+ })
341
+ end
342
+
343
+ it "returns a cloned query" do
344
+ expect(selection).to_not equal(query)
345
+ end
346
+ end
347
+ end
348
+
349
+ context "when chaining the criterion" do
350
+
351
+ context "when the criterion are for different fields" do
352
+
353
+ let(:selection) do
354
+ query.or(first: [ 1, 2 ]).or(second: [ 3, 4 ])
355
+ end
356
+
357
+ it "adds the $or selectors" do
358
+ expect(selection.selector).to eq({
359
+ "$or" => [
360
+ { "first" => [ 1, 2 ] },
361
+ { "second" => [ 3, 4 ] }
362
+ ]
363
+ })
364
+ end
365
+
366
+ it "returns a cloned query" do
367
+ expect(selection).to_not equal(query)
368
+ end
369
+ end
370
+
371
+ context "when the criterion are on the same field" do
372
+
373
+ let(:selection) do
374
+ query.or(first: [ 1, 2 ]).or(first: [ 3, 4 ])
375
+ end
376
+
377
+ it "appends both $or expressions" do
378
+ expect(selection.selector).to eq({
379
+ "$or" => [
380
+ { "first" => [ 1, 2 ] },
381
+ { "first" => [ 3, 4 ] }
382
+ ]
383
+ })
384
+ end
385
+
386
+ it "returns a cloned query" do
387
+ expect(selection).to_not equal(query)
388
+ end
389
+ end
390
+ end
391
+ end
392
+
393
+ describe "#nor" do
394
+
395
+ context "when provided no criterion" do
396
+
397
+ let(:selection) do
398
+ query.nor
399
+ end
400
+
401
+ it "does not add any criterion" do
402
+ expect(selection.selector).to eq({})
403
+ end
404
+
405
+ it "returns the query" do
406
+ expect(selection).to eq(query)
407
+ end
408
+
409
+ it "returns a cloned query" do
410
+ expect(selection).to_not equal(query)
411
+ end
412
+ end
413
+
414
+ context "when provided nil" do
415
+
416
+ let(:selection) do
417
+ query.nor(nil)
418
+ end
419
+
420
+ it "does not add any criterion" do
421
+ expect(selection.selector).to eq({})
422
+ end
423
+
424
+ it "returns the query" do
425
+ expect(selection).to eq(query)
426
+ end
427
+
428
+ it "returns a cloned query" do
429
+ expect(selection).to_not equal(query)
430
+ end
431
+ end
432
+
433
+ context "when provided a single criterion" do
434
+
435
+ let(:selection) do
436
+ query.nor(field: [ 1, 2 ])
437
+ end
438
+
439
+ it "adds the $nor selector" do
440
+ expect(selection.selector).to eq({
441
+ "$nor" => [{"field" => [ 1, 2 ] }]
442
+ })
443
+ end
444
+
445
+ it "returns a cloned query" do
446
+ expect(selection).to_not equal(query)
447
+ end
448
+ end
449
+
450
+ context "when provided multiple criterion" do
451
+
452
+ context "when the criterion are fnor different fields" do
453
+
454
+ let(:selection) do
455
+ query.nor({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
456
+ end
457
+
458
+ it "adds the $nor selector" do
459
+ expect(selection.selector).to eq({
460
+ "$nor" => [
461
+ { "first" => [ 1, 2 ] },
462
+ { "second" => [ 3, 4 ] }
463
+ ]
464
+ })
465
+ end
466
+
467
+ it "returns a cloned query" do
468
+ expect(selection).to_not equal(query)
469
+ end
470
+ end
471
+
472
+ context "when the criterion are on the same field" do
473
+
474
+ let(:selection) do
475
+ query.nor({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
476
+ end
477
+
478
+ it "appends both $nor expressions" do
479
+ expect(selection.selector).to eq({
480
+ "$nor" => [
481
+ { "first" => [ 1, 2 ] },
482
+ { "first" => [ 3, 4 ] }
483
+ ]
484
+ })
485
+ end
486
+
487
+ it "returns a cloned query" do
488
+ expect(selection).to_not equal(query)
489
+ end
490
+ end
491
+ end
492
+
493
+ context "when chaining the criterion" do
494
+
495
+ context "when the criterion are fnor different fields" do
496
+
497
+ let(:selection) do
498
+ query.nor(first: [ 1, 2 ]).nor(second: [ 3, 4 ])
499
+ end
500
+
501
+ it "adds the $nor selectors" do
502
+ expect(selection.selector).to eq({
503
+ "$nor" => [
504
+ { "first" => [ 1, 2 ] },
505
+ { "second" => [ 3, 4 ] }
506
+ ]
507
+ })
508
+ end
509
+
510
+ it "returns a cloned query" do
511
+ expect(selection).to_not equal(query)
512
+ end
513
+ end
514
+
515
+ context "when the criterion are on the same field" do
516
+
517
+ let(:selection) do
518
+ query.nor(first: [ 1, 2 ]).nor(first: [ 3, 4 ])
519
+ end
520
+
521
+ it "appends both $nor expressions" do
522
+ expect(selection.selector).to eq({
523
+ "$nor" => [
524
+ { "first" => [ 1, 2 ] },
525
+ { "first" => [ 3, 4 ] }
526
+ ]
527
+ })
528
+ end
529
+
530
+ it "returns a cloned query" do
531
+ expect(selection).to_not equal(query)
532
+ end
533
+ end
534
+ end
535
+ end
536
+
537
+ describe "#not" do
538
+
539
+ context "when provided no criterion" do
540
+
541
+ let(:selection) do
542
+ query.not
543
+ end
544
+
545
+ it "does not add any criterion" do
546
+ expect(selection.selector).to eq({})
547
+ end
548
+
549
+ it "returns the query" do
550
+ expect(selection).to eq(query)
551
+ end
552
+
553
+ it "returns a cloned query" do
554
+ expect(selection).not_to equal(query)
555
+ end
556
+
557
+ it 'does not mutate receiver' do
558
+ expect(query.negating).to be nil
559
+
560
+ selection
561
+ expect(query.negating).to be nil
562
+ end
563
+
564
+ context "when the following criteria is a query method" do
565
+
566
+ let(:selection) do
567
+ query.not.all(field: [ 1, 2 ])
568
+ end
569
+
570
+ it "negates the all selection" do
571
+ expect(selection.selector).to eq(
572
+ { "field" => { "$not" => { "$all" => [ 1, 2 ] }}}
573
+ )
574
+ end
575
+
576
+ it "returns a cloned query" do
577
+ expect(selection).to_not equal(query)
578
+ end
579
+
580
+ it "removes the negation on the clone" do
581
+ expect(selection).to_not be_negating
582
+ end
583
+ end
584
+
585
+ context "when the following criteria is a gt method" do
586
+
587
+ let(:selection) do
588
+ query.not.gt(age: 50)
589
+ end
590
+
591
+ it "negates the gt selection" do
592
+ expect(selection.selector).to eq(
593
+ { "age" => { "$not" => { "$gt" => 50 }}}
594
+ )
595
+ end
596
+
597
+ it "returns a coned query" do
598
+ expect(selection).to_not eq(query)
599
+ end
600
+
601
+ it "removes the negation on the clone" do
602
+ expect(selection).to_not be_negating
603
+ end
604
+ end
605
+
606
+ context "when the following criteria is a where" do
607
+
608
+ let(:selection) do
609
+ query.not.where(field: 1, :other.in => [ 1, 2 ])
610
+ end
611
+
612
+ it "negates the selection with an operator" do
613
+ expect(selection.selector).to eq(
614
+ { "field" => { "$ne" => 1 }, "other" => { "$not" => { "$in" => [ 1, 2 ] }}}
615
+ )
616
+ end
617
+
618
+ it "returns a cloned query" do
619
+ expect(selection).to_not equal(query)
620
+ end
621
+
622
+ it "removes the negation on the clone" do
623
+ expect(selection).to_not be_negating
624
+ end
625
+ end
626
+
627
+ context "when the following criteria is a where with a regexp" do
628
+
629
+ let(:selection) do
630
+ query.not.where(field: 1, other: /test/)
631
+ end
632
+
633
+ it "negates the selection with an operator" do
634
+ expect(selection.selector).to eq(
635
+ { "field" => { "$ne" => 1 }, "other" => { "$not" => /test/ } }
636
+ )
637
+ end
638
+
639
+ it "returns a cloned query" do
640
+ expect(selection).to_not equal(query)
641
+ end
642
+
643
+ it "removes the negation on the clone" do
644
+ expect(selection).to_not be_negating
645
+ end
646
+
647
+ end
648
+ end
649
+
650
+ context "when provided nil" do
651
+
652
+ let(:selection) do
653
+ query.not(nil)
654
+ end
655
+
656
+ it "does not add any criterion" do
657
+ expect(selection.selector).to eq({})
658
+ end
659
+
660
+ it "returns the query" do
661
+ expect(selection).to eq(query)
662
+ end
663
+
664
+ it "returns a cloned query" do
665
+ expect(selection).to_not equal(query)
666
+ end
667
+ end
668
+
669
+ context "when provided a single criterion" do
670
+
671
+ let(:selection) do
672
+ query.not(field: /test/)
673
+ end
674
+
675
+ it "adds the $not selector" do
676
+ expect(selection.selector).to eq({
677
+ "field" => { "$not" => /test/ }
678
+ })
679
+ end
680
+
681
+ it "returns a cloned query" do
682
+ expect(selection).to_not equal(query)
683
+ end
684
+ end
685
+
686
+ context "when provided multiple criterion" do
687
+
688
+ context "when the criterion are for different fields" do
689
+
690
+ let(:selection) do
691
+ query.not(first: /1/, second: /2/)
692
+ end
693
+
694
+ it "adds the $not selectors" do
695
+ expect(selection.selector).to eq({
696
+ "first" => { "$not" => /1/ },
697
+ "second" => { "$not" => /2/ }
698
+ })
699
+ end
700
+
701
+ it "returns a cloned query" do
702
+ expect(selection).to_not equal(query)
703
+ end
704
+ end
705
+ end
706
+
707
+ context "when chaining the criterion" do
708
+
709
+ context "when the criterion are for different fields" do
710
+
711
+ let(:selection) do
712
+ query.not(first: /1/).not(second: /2/)
713
+ end
714
+
715
+ it "adds the $not selectors" do
716
+ expect(selection.selector).to eq({
717
+ "first" => { "$not" => /1/ },
718
+ "second" => { "$not" => /2/ }
719
+ })
720
+ end
721
+
722
+ it "returns a cloned query" do
723
+ expect(selection).to_not equal(query)
724
+ end
725
+ end
726
+
727
+ context "when the criterion are on the same field" do
728
+
729
+ let(:selection) do
730
+ query.not(first: /1/).not(first: /2/)
731
+ end
732
+
733
+ it "overwrites the first $not selector" do
734
+ expect(selection.selector).to eq({
735
+ "first" => { "$not" => /2/ }
736
+ })
737
+ end
738
+
739
+ it "returns a cloned query" do
740
+ expect(selection).to_not equal(query)
741
+ end
742
+ end
743
+
744
+ context "when the criterion are a double negative" do
745
+
746
+ let(:selection) do
747
+ query.not.where(:first.not => /1/)
748
+ end
749
+
750
+ it "does not double the $not selector" do
751
+ expect(selection.selector).to eq({
752
+ "first" => { "$not" => /1/ }
753
+ })
754
+ end
755
+
756
+ it "returns a cloned query" do
757
+ expect(selection).to_not equal(query)
758
+ end
759
+ end
760
+ end
761
+ end
762
+ end