mongoid 7.2.0 → 7.3.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 (191) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/Rakefile +16 -0
  4. data/lib/config/locales/en.yml +2 -2
  5. data/lib/mongoid/association/accessors.rb +1 -1
  6. data/lib/mongoid/association/constrainable.rb +1 -1
  7. data/lib/mongoid/association/depending.rb +4 -4
  8. data/lib/mongoid/association/embedded/batchable.rb +1 -1
  9. data/lib/mongoid/association/embedded/embedded_in.rb +1 -1
  10. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +10 -3
  11. data/lib/mongoid/association/nested/many.rb +1 -1
  12. data/lib/mongoid/association/nested/one.rb +4 -2
  13. data/lib/mongoid/association/proxy.rb +6 -1
  14. data/lib/mongoid/association/referenced/auto_save.rb +2 -2
  15. data/lib/mongoid/association/referenced/has_many/enumerable.rb +493 -495
  16. data/lib/mongoid/association/referenced/has_many/proxy.rb +2 -2
  17. data/lib/mongoid/association/referenced/has_one/nested_builder.rb +2 -2
  18. data/lib/mongoid/attributes/projector.rb +120 -0
  19. data/lib/mongoid/attributes.rb +32 -14
  20. data/lib/mongoid/cacheable.rb +2 -2
  21. data/lib/mongoid/clients/factory.rb +22 -8
  22. data/lib/mongoid/clients.rb +1 -1
  23. data/lib/mongoid/config.rb +19 -2
  24. data/lib/mongoid/contextual/aggregable/mongo.rb +10 -8
  25. data/lib/mongoid/copyable.rb +1 -1
  26. data/lib/mongoid/criteria/findable.rb +1 -1
  27. data/lib/mongoid/criteria/queryable/expandable.rb +0 -24
  28. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +1 -1
  29. data/lib/mongoid/criteria/queryable/extensions.rb +0 -4
  30. data/lib/mongoid/criteria/queryable/mergeable.rb +46 -20
  31. data/lib/mongoid/criteria/queryable/selectable.rb +8 -8
  32. data/lib/mongoid/criteria/queryable/selector.rb +0 -4
  33. data/lib/mongoid/criteria.rb +4 -5
  34. data/lib/mongoid/document.rb +4 -17
  35. data/lib/mongoid/errors/delete_restriction.rb +8 -9
  36. data/lib/mongoid/evolvable.rb +1 -1
  37. data/lib/mongoid/extensions/boolean.rb +1 -2
  38. data/lib/mongoid/extensions/false_class.rb +1 -1
  39. data/lib/mongoid/extensions/hash.rb +2 -2
  40. data/lib/mongoid/extensions/true_class.rb +1 -1
  41. data/lib/mongoid/fields.rb +43 -5
  42. data/lib/mongoid/inspectable.rb +1 -1
  43. data/lib/mongoid/interceptable.rb +3 -1
  44. data/lib/mongoid/matcher/bits.rb +41 -0
  45. data/lib/mongoid/matcher/bits_all_clear.rb +20 -0
  46. data/lib/mongoid/matcher/bits_all_set.rb +20 -0
  47. data/lib/mongoid/matcher/bits_any_clear.rb +20 -0
  48. data/lib/mongoid/matcher/bits_any_set.rb +20 -0
  49. data/lib/mongoid/matcher/elem_match.rb +2 -1
  50. data/lib/mongoid/matcher/expression.rb +9 -14
  51. data/lib/mongoid/matcher/field_expression.rb +4 -5
  52. data/lib/mongoid/matcher/field_operator.rb +13 -11
  53. data/lib/mongoid/matcher/mod.rb +17 -0
  54. data/lib/mongoid/matcher/type.rb +99 -0
  55. data/lib/mongoid/matcher.rb +26 -43
  56. data/lib/mongoid/persistable/deletable.rb +1 -2
  57. data/lib/mongoid/persistable/destroyable.rb +8 -2
  58. data/lib/mongoid/persistable/updatable.rb +27 -2
  59. data/lib/mongoid/query_cache.rb +35 -29
  60. data/lib/mongoid/reloadable.rb +5 -0
  61. data/lib/mongoid/selectable.rb +5 -7
  62. data/lib/mongoid/shardable.rb +21 -5
  63. data/lib/mongoid/touchable.rb +23 -4
  64. data/lib/mongoid/version.rb +1 -1
  65. data/lib/rails/generators/mongoid/config/config_generator.rb +8 -1
  66. data/spec/integration/app_spec.rb +171 -84
  67. data/spec/integration/associations/embeds_many_spec.rb +44 -0
  68. data/spec/integration/associations/has_one_spec.rb +48 -0
  69. data/spec/integration/callbacks_models.rb +49 -0
  70. data/spec/integration/callbacks_spec.rb +216 -0
  71. data/spec/integration/criteria/date_field_spec.rb +1 -1
  72. data/spec/integration/document_spec.rb +30 -0
  73. data/spec/integration/matcher_operator_data/bits_all_clear.yml +159 -0
  74. data/spec/integration/matcher_operator_data/bits_all_set.yml +159 -0
  75. data/spec/integration/matcher_operator_data/bits_any_clear.yml +159 -0
  76. data/spec/integration/matcher_operator_data/bits_any_set.yml +159 -0
  77. data/spec/integration/matcher_operator_data/comment.yml +22 -0
  78. data/spec/integration/matcher_operator_data/elem_match.yml +46 -0
  79. data/spec/integration/matcher_operator_data/gt_types.yml +63 -0
  80. data/spec/integration/matcher_operator_data/gte_types.yml +15 -0
  81. data/spec/integration/matcher_operator_data/implicit_traversal.yml +96 -0
  82. data/spec/integration/matcher_operator_data/in.yml +16 -0
  83. data/spec/integration/matcher_operator_data/lt_types.yml +15 -0
  84. data/spec/integration/matcher_operator_data/lte_types.yml +15 -0
  85. data/spec/integration/matcher_operator_data/mod.yml +55 -0
  86. data/spec/integration/matcher_operator_data/ne_types.yml +15 -0
  87. data/spec/integration/matcher_operator_data/type.yml +70 -0
  88. data/spec/integration/matcher_operator_data/type_array.yml +16 -0
  89. data/spec/integration/matcher_operator_data/type_binary.yml +18 -0
  90. data/spec/integration/matcher_operator_data/type_boolean.yml +39 -0
  91. data/spec/integration/matcher_operator_data/type_code.yml +26 -0
  92. data/spec/integration/matcher_operator_data/type_code_with_scope.yml +26 -0
  93. data/spec/integration/matcher_operator_data/type_date.yml +39 -0
  94. data/spec/integration/matcher_operator_data/type_db_pointer.yml +19 -0
  95. data/spec/integration/matcher_operator_data/type_decimal.yml +40 -0
  96. data/spec/integration/matcher_operator_data/type_double.yml +15 -0
  97. data/spec/integration/matcher_operator_data/type_int32.yml +33 -0
  98. data/spec/integration/matcher_operator_data/type_int64.yml +33 -0
  99. data/spec/integration/matcher_operator_data/type_max_key.yml +17 -0
  100. data/spec/integration/matcher_operator_data/type_min_key.yml +17 -0
  101. data/spec/integration/matcher_operator_data/type_null.yml +23 -0
  102. data/spec/integration/matcher_operator_data/type_object.yml +23 -0
  103. data/spec/integration/matcher_operator_data/type_object_id.yml +25 -0
  104. data/spec/integration/matcher_operator_data/type_regex.yml +44 -0
  105. data/spec/integration/matcher_operator_data/type_string.yml +15 -0
  106. data/spec/integration/matcher_operator_data/type_symbol.yml +32 -0
  107. data/spec/integration/matcher_operator_data/type_timestamp.yml +25 -0
  108. data/spec/integration/matcher_operator_data/type_undefined.yml +17 -0
  109. data/spec/lite_spec_helper.rb +5 -4
  110. data/spec/mongoid/association/depending_spec.rb +391 -352
  111. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +50 -0
  112. data/spec/mongoid/association/nested/one_spec.rb +18 -14
  113. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +25 -8
  114. data/spec/mongoid/association/referenced/has_and_belongs_to_many/binding_spec.rb +1 -1
  115. data/spec/mongoid/association/referenced/has_many/binding_spec.rb +1 -1
  116. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +1 -1
  117. data/spec/mongoid/association/referenced/has_one_models.rb +8 -0
  118. data/spec/mongoid/atomic/paths_spec.rb +105 -12
  119. data/spec/mongoid/attributes/projector_data/embedded.yml +105 -0
  120. data/spec/mongoid/attributes/projector_data/fields.yml +93 -0
  121. data/spec/mongoid/attributes/projector_spec.rb +41 -0
  122. data/spec/mongoid/attributes_spec.rb +333 -0
  123. data/spec/mongoid/clients/factory_spec.rb +48 -0
  124. data/spec/mongoid/config_spec.rb +32 -0
  125. data/spec/mongoid/contextual/atomic_spec.rb +17 -4
  126. data/spec/mongoid/contextual/mongo_spec.rb +2 -2
  127. data/spec/mongoid/criteria/modifiable_spec.rb +1 -1
  128. data/spec/mongoid/criteria/queryable/expandable_spec.rb +0 -73
  129. data/spec/mongoid/criteria/queryable/extensions/boolean_spec.rb +1 -1
  130. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +105 -7
  131. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +265 -24
  132. data/spec/mongoid/criteria/queryable/selectable_shared_examples.rb +39 -0
  133. data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -565
  134. data/spec/mongoid/criteria/queryable/selectable_where_spec.rb +590 -0
  135. data/spec/mongoid/criteria_projection_spec.rb +411 -0
  136. data/spec/mongoid/criteria_spec.rb +0 -275
  137. data/spec/mongoid/document_fields_spec.rb +26 -0
  138. data/spec/mongoid/document_spec.rb +13 -13
  139. data/spec/mongoid/errors/delete_restriction_spec.rb +1 -1
  140. data/spec/mongoid/extensions/false_class_spec.rb +1 -1
  141. data/spec/mongoid/extensions/string_spec.rb +5 -5
  142. data/spec/mongoid/extensions/true_class_spec.rb +1 -1
  143. data/spec/mongoid/fields/localized_spec.rb +4 -4
  144. data/spec/mongoid/fields_spec.rb +4 -4
  145. data/spec/mongoid/inspectable_spec.rb +12 -4
  146. data/spec/mongoid/matcher/extract_attribute_data/numeric_keys.yml +104 -0
  147. data/spec/mongoid/matcher/extract_attribute_data/traversal.yml +68 -88
  148. data/spec/mongoid/matcher/extract_attribute_spec.rb +3 -13
  149. data/spec/mongoid/persistable/deletable_spec.rb +175 -1
  150. data/spec/mongoid/persistable/destroyable_spec.rb +191 -3
  151. data/spec/mongoid/persistable/savable_spec.rb +3 -5
  152. data/spec/mongoid/persistable/settable_spec.rb +30 -0
  153. data/spec/mongoid/persistable/upsertable_spec.rb +1 -1
  154. data/spec/mongoid/query_cache_middleware_spec.rb +8 -0
  155. data/spec/mongoid/reloadable_spec.rb +18 -1
  156. data/spec/mongoid/shardable_spec.rb +44 -0
  157. data/spec/mongoid/touchable_spec.rb +104 -16
  158. data/spec/mongoid/touchable_spec_models.rb +52 -0
  159. data/spec/mongoid/validatable_spec.rb +1 -1
  160. data/spec/shared/bin/get-mongodb-download-url +17 -0
  161. data/spec/shared/lib/mrss/cluster_config.rb +221 -0
  162. data/spec/shared/lib/mrss/constraints.rb +51 -0
  163. data/spec/shared/lib/mrss/docker_runner.rb +265 -0
  164. data/spec/shared/lib/mrss/lite_constraints.rb +16 -0
  165. data/spec/shared/lib/mrss/server_version_registry.rb +115 -0
  166. data/spec/shared/lib/mrss/spec_organizer.rb +14 -1
  167. data/spec/shared/lib/mrss/utils.rb +15 -0
  168. data/spec/shared/share/Dockerfile.erb +231 -0
  169. data/spec/shared/shlib/distro.sh +73 -0
  170. data/spec/shared/shlib/server.sh +290 -0
  171. data/spec/shared/shlib/set_env.sh +128 -0
  172. data/spec/spec_helper.rb +6 -2
  173. data/spec/support/client_registry.rb +9 -0
  174. data/spec/support/models/bolt.rb +8 -0
  175. data/spec/support/models/customer.rb +11 -0
  176. data/spec/support/models/customer_address.rb +12 -0
  177. data/spec/support/models/dictionary.rb +6 -0
  178. data/spec/support/models/hole.rb +13 -0
  179. data/spec/support/models/mop.rb +9 -0
  180. data/spec/support/models/nut.rb +8 -0
  181. data/spec/support/models/person.rb +6 -0
  182. data/spec/support/models/sealer.rb +8 -0
  183. data/spec/support/models/shirt.rb +12 -0
  184. data/spec/support/models/spacer.rb +8 -0
  185. data/spec/support/models/threadlocker.rb +8 -0
  186. data/spec/support/models/washer.rb +8 -0
  187. data/spec/support/spec_config.rb +8 -0
  188. data.tar.gz.sig +0 -0
  189. metadata +146 -14
  190. metadata.gz.sig +5 -2
  191. data/spec/support/cluster_config.rb +0 -158
@@ -44,7 +44,7 @@ describe Mongoid::Criteria::Queryable::Mergeable do
44
44
 
45
45
  describe '#_mongoid_expand_keys' do
46
46
  it 'expands simple keys' do
47
- query.send(:_mongoid_expand_keys, {a: 1}).should == {a: 1}
47
+ query.send(:_mongoid_expand_keys, {a: 1}).should == {'a' => 1}
48
48
  end
49
49
 
50
50
  let(:gt) do
@@ -68,18 +68,116 @@ describe Mongoid::Criteria::Queryable::Mergeable do
68
68
  'age' => {'$gt' => 42, '$lt' => 50}}
69
69
  end
70
70
 
71
- it 'expands simple and Key instances on the same field' do
72
- query.send(:_mongoid_expand_keys, {'age' => 42, lt => 50}).should == {
73
- 'age' => {'$eq' => 42, '$lt' => 50}}
71
+ context 'given implicit equality and Key instance on the same field' do
72
+ [42, 'infinite', [nil]].each do |value|
73
+ context "for non-regular expression value #{value}" do
74
+ context 'implicit equality then Key instance' do
75
+ it 'expands implicit equality with $eq and combines with Key operator' do
76
+ query.send(:_mongoid_expand_keys, {'age' => value, lt => 50}).should == {
77
+ 'age' => {'$eq' => value, '$lt' => 50}}
78
+ end
79
+ end
80
+
81
+ context 'symbol operator then implicit equality' do
82
+ it 'expands implicit equality with $eq and combines with Key operator' do
83
+ query.send(:_mongoid_expand_keys, {gt => 42, 'age' => value}).should == {
84
+ 'age' => {'$gt' => 42, '$eq' => value}}
85
+ end
86
+ end
87
+ end
88
+ end
74
89
  end
75
90
 
76
- it 'expands Key and simple instances on the same field' do
77
- query.send(:_mongoid_expand_keys, {gt => 42, 'age' => 50}).should == {
78
- 'age' => {'$gt' => 42, '$eq' => 50}}
91
+ context 'given implicit equality with Regexp argument and Key instance on the same field' do
92
+ [/42/, BSON::Regexp::Raw.new('42')].each do |value|
93
+ context "for regular expression value #{value}" do
94
+ context 'implicit equality then Key instance' do
95
+ it 'expands implicit equality with $eq and combines with Key operator' do
96
+ query.send(:_mongoid_expand_keys, {'age' => value, lt => 50}).should == {
97
+ 'age' => {'$regex' => value, '$lt' => 50}}
98
+ end
99
+ end
100
+
101
+ context 'Key instance then implicit equality' do
102
+ it 'expands implicit equality with $eq and combines with Key operator' do
103
+ query.send(:_mongoid_expand_keys, {gt => 50, 'age' => value}).should == {
104
+ 'age' => {'$gt' => 50, '$regex' => value}}
105
+ end
106
+ end
107
+ end
108
+ end
79
109
  end
80
110
 
81
111
  it 'Ruby does not allow same symbol operator with different values' do
82
112
  {gt => 42, gtp => 50}.should == {gtp => 50}
83
113
  end
114
+
115
+ let(:expanded) do
116
+ query.send(:_mongoid_expand_keys, condition)
117
+ end
118
+
119
+ context 'field name => value' do
120
+ shared_examples_for 'expands' do
121
+
122
+ it 'expands' do
123
+ expanded.should == {'foo' => 'bar'}
124
+ end
125
+ end
126
+
127
+ context 'string key' do
128
+ let(:condition) do
129
+ {'foo' => 'bar'}
130
+ end
131
+
132
+ it_behaves_like 'expands'
133
+ end
134
+
135
+ context 'symbol key' do
136
+ let(:condition) do
137
+ {foo: 'bar'}
138
+ end
139
+
140
+ it_behaves_like 'expands'
141
+ end
142
+ end
143
+
144
+ context 'Key instance => value' do
145
+ let(:key) do
146
+ Mongoid::Criteria::Queryable::Key.new(:foo, :__override__, '$gt')
147
+ end
148
+
149
+ let(:condition) do
150
+ {key => 'bar'}
151
+ end
152
+
153
+ it 'expands' do
154
+ expanded.should == {'foo' => {'$gt' => 'bar'}}
155
+ end
156
+ end
157
+
158
+ context 'operator => operator value expression' do
159
+ shared_examples_for 'expands' do
160
+
161
+ it 'expands' do
162
+ expanded.should == {'foo' => {'$in' => ['bar']}}
163
+ end
164
+ end
165
+
166
+ context 'string key' do
167
+ let(:condition) do
168
+ {foo: {'$in' => %w(bar)}}
169
+ end
170
+
171
+ it_behaves_like 'expands'
172
+ end
173
+
174
+ context 'symbol key' do
175
+ let(:condition) do
176
+ {foo: {:$in => %w(bar)}}
177
+ end
178
+
179
+ it_behaves_like 'expands'
180
+ end
181
+ end
84
182
  end
85
183
  end
@@ -417,6 +417,40 @@ describe Mongoid::Criteria::Queryable::Selectable do
417
417
  it_behaves_like 'returns a cloned query'
418
418
  end
419
419
 
420
+ context 'when criteria use operators' do
421
+ shared_examples 'behave correctly' do
422
+ let(:selection) do
423
+ query.and(
424
+ { field: {first_operator => [ 1, 2 ] }},
425
+ { field: {second_operator => [ 3, 4 ] }},
426
+ )
427
+ end
428
+
429
+ it "combines via $and operator and stringifies all keys" do
430
+ expect(selection.selector).to eq({
431
+ "field" => {'$in' => [ 1, 2 ]},
432
+ "$and" => [
433
+ { "field" => {'$in' => [ 3, 4 ] }}
434
+ ]
435
+ })
436
+ end
437
+ end
438
+
439
+ [
440
+ ['$in', '$in'],
441
+ [:$in, '$in'],
442
+ ['$in', :$in],
443
+ [:$in, :$in],
444
+ ].each do |first_operator, second_operator|
445
+ context "when first operator is #{first_operator.inspect} and second operator is #{second_operator.inspect}" do
446
+ let(:first_operator) { first_operator }
447
+ let(:second_operator) { second_operator }
448
+
449
+ include_examples 'behave correctly'
450
+ end
451
+ end
452
+ end
453
+
420
454
  context 'when criteria are handled via Key' do
421
455
  shared_examples_for 'adds the conditions to top level' do
422
456
 
@@ -467,12 +501,44 @@ describe Mongoid::Criteria::Queryable::Selectable do
467
501
  it_behaves_like 'returns a cloned query'
468
502
  end
469
503
 
504
+ shared_examples_for 'combines conditions with $eq' do
505
+
506
+ it "combines conditions with $eq" do
507
+ expect(selection.selector).to eq({
508
+ "field" => {'$eq' => 3, '$lt' => 5},
509
+ })
510
+ end
511
+
512
+ it_behaves_like 'returns a cloned query'
513
+ end
514
+
515
+ shared_examples_for 'combines conditions with $regex' do
516
+
517
+ it "combines conditions with $regex" do
518
+ expect(selection.selector).to eq({
519
+ "field" => {'$regex' => /t/, '$lt' => 5},
520
+ })
521
+ end
522
+
523
+ it_behaves_like 'returns a cloned query'
524
+ end
525
+
470
526
  context 'criteria are provided in the same hash' do
471
- let(:selection) do
472
- query.send(tested_method, :field => 3, :field.lt => 5)
527
+ context 'non-regexp argument' do
528
+ let(:selection) do
529
+ query.send(tested_method, :field => 3, :field.lt => 5)
530
+ end
531
+
532
+ it_behaves_like 'combines conditions with $eq'
473
533
  end
474
534
 
475
- it_behaves_like 'combines conditions with $and'
535
+ context 'regexp argument' do
536
+ let(:selection) do
537
+ query.send(tested_method, :field => /t/, :field.lt => 5)
538
+ end
539
+
540
+ it_behaves_like 'combines conditions with $regex'
541
+ end
476
542
  end
477
543
 
478
544
  context 'criteria are provided in separate hashes' do
@@ -505,12 +571,44 @@ describe Mongoid::Criteria::Queryable::Selectable do
505
571
  it_behaves_like 'returns a cloned query'
506
572
  end
507
573
 
574
+ shared_examples_for 'combines conditions with $eq' do
575
+
576
+ it "combines conditions with $eq" do
577
+ expect(selection.selector).to eq({
578
+ "field" => {'$gt' => 3, '$eq' => 5},
579
+ })
580
+ end
581
+
582
+ it_behaves_like 'returns a cloned query'
583
+ end
584
+
585
+ shared_examples_for 'combines conditions with $regex' do
586
+
587
+ it "combines conditions with $regex" do
588
+ expect(selection.selector).to eq({
589
+ "field" => {'$gt' => 3, '$regex' => /t/},
590
+ })
591
+ end
592
+
593
+ it_behaves_like 'returns a cloned query'
594
+ end
595
+
508
596
  context 'criteria are provided in the same hash' do
509
- let(:selection) do
510
- query.send(tested_method, :field.gt => 3, :field => 5)
597
+ context 'non-regexp argument' do
598
+ let(:selection) do
599
+ query.send(tested_method, :field.gt => 3, :field => 5)
600
+ end
601
+
602
+ it_behaves_like 'combines conditions with $eq'
511
603
  end
512
604
 
513
- it_behaves_like 'combines conditions with $and'
605
+ context 'regexp argument' do
606
+ let(:selection) do
607
+ query.send(tested_method, :field.gt => 3, :field => /t/)
608
+ end
609
+
610
+ it_behaves_like 'combines conditions with $regex'
611
+ end
514
612
  end
515
613
 
516
614
  context 'criteria are provided in separate hashes' do
@@ -1031,6 +1129,38 @@ describe Mongoid::Criteria::Queryable::Selectable do
1031
1129
  end
1032
1130
  end
1033
1131
  end
1132
+
1133
+ context 'when giving multiple conditions in one call on the same key with symbol operator' do
1134
+
1135
+ context 'non-regexp argument' do
1136
+ let(:selection) do
1137
+ query.send(tested_method, field: 1, :field.gt => 0)
1138
+ end
1139
+
1140
+ it 'combines conditions with $eq' do
1141
+ selection.selector.should == {
1142
+ expected_operator => [
1143
+ 'field' => {'$eq' => 1, '$gt' => 0},
1144
+ ]
1145
+ }
1146
+ end
1147
+ end
1148
+
1149
+ context 'regexp argument' do
1150
+ let(:selection) do
1151
+ query.send(tested_method, field: /t/, :field.gt => 0)
1152
+ end
1153
+
1154
+ it 'combines conditions with $regex' do
1155
+ selection.selector.should == {
1156
+ expected_operator => [
1157
+ 'field' => {'$regex' => /t/, '$gt' => 0},
1158
+ ]
1159
+ }
1160
+ end
1161
+ end
1162
+
1163
+ end
1034
1164
  end
1035
1165
 
1036
1166
  describe "#or" do
@@ -1270,14 +1400,28 @@ describe Mongoid::Criteria::Queryable::Selectable do
1270
1400
  it_behaves_like 'returns a cloned query'
1271
1401
  end
1272
1402
 
1273
- shared_examples_for 'adds one condition' do
1403
+ shared_examples_for 'combines conditions with $eq' do
1274
1404
 
1275
- it "adds one condition" do
1405
+ it "combines conditions with $eq" do
1276
1406
  expect(selection.selector).to eq({
1277
- 'field' => 3,
1278
- '$and' => [
1279
- {'field' => {'$lt' => 5}},
1280
- ],
1407
+ 'field' => {
1408
+ '$eq' => 3,
1409
+ '$lt' => 5,
1410
+ },
1411
+ })
1412
+ end
1413
+
1414
+ it_behaves_like 'returns a cloned query'
1415
+ end
1416
+
1417
+ shared_examples_for 'combines conditions with $regex' do
1418
+
1419
+ it "combines conditions with $regex" do
1420
+ expect(selection.selector).to eq({
1421
+ 'field' => {
1422
+ '$regex' => /t/,
1423
+ '$lt' => 5,
1424
+ },
1281
1425
  })
1282
1426
  end
1283
1427
 
@@ -1285,11 +1429,21 @@ describe Mongoid::Criteria::Queryable::Selectable do
1285
1429
  end
1286
1430
 
1287
1431
  context 'criteria are provided in the same hash' do
1288
- let(:selection) do
1289
- query.send(tested_method, :field => 3, :field.lt => 5)
1432
+ context 'non-regexp argument' do
1433
+ let(:selection) do
1434
+ query.send(tested_method, :field => 3, :field.lt => 5)
1435
+ end
1436
+
1437
+ it_behaves_like 'combines conditions with $eq'
1290
1438
  end
1291
1439
 
1292
- it_behaves_like 'adds one condition'
1440
+ context 'regexp argument' do
1441
+ let(:selection) do
1442
+ query.send(tested_method, :field => /t/, :field.lt => 5)
1443
+ end
1444
+
1445
+ it_behaves_like 'combines conditions with $regex'
1446
+ end
1293
1447
  end
1294
1448
 
1295
1449
  context 'criteria are provided in separate hashes' do
@@ -1324,24 +1478,44 @@ describe Mongoid::Criteria::Queryable::Selectable do
1324
1478
  it_behaves_like 'returns a cloned query'
1325
1479
  end
1326
1480
 
1327
- shared_examples_for 'adds one condition' do
1481
+ shared_examples_for 'combines conditions with $eq' do
1328
1482
 
1329
- it "adds one condition" do
1330
- expect(selection.selector).to eq({
1331
- 'field' => {'$gt' => 3},
1332
- '$and' => ['field' => 5],
1333
- })
1483
+ it "combines conditions with $eq" do
1484
+ expect(selection.selector).to eq(
1485
+ 'field' => {'$gt' => 3, '$eq' => 5},
1486
+ )
1487
+ end
1488
+
1489
+ it_behaves_like 'returns a cloned query'
1490
+ end
1491
+
1492
+ shared_examples_for 'combines conditions with $regex' do
1493
+
1494
+ it "combines conditions with $regex" do
1495
+ expect(selection.selector).to eq(
1496
+ 'field' => {'$gt' => 3, '$regex' => /t/},
1497
+ )
1334
1498
  end
1335
1499
 
1336
1500
  it_behaves_like 'returns a cloned query'
1337
1501
  end
1338
1502
 
1339
1503
  context 'criteria are provided in the same hash' do
1340
- let(:selection) do
1341
- query.send(tested_method, :field.gt => 3, :field => 5)
1504
+ context 'non-regexp argument' do
1505
+ let(:selection) do
1506
+ query.send(tested_method, :field.gt => 3, :field => 5)
1507
+ end
1508
+
1509
+ it_behaves_like 'combines conditions with $eq'
1342
1510
  end
1343
1511
 
1344
- it_behaves_like 'adds one condition'
1512
+ context 'regexp argument' do
1513
+ let(:selection) do
1514
+ query.send(tested_method, :field.gt => 3, :field => /t/)
1515
+ end
1516
+
1517
+ it_behaves_like 'combines conditions with $regex'
1518
+ end
1345
1519
  end
1346
1520
 
1347
1521
  context 'criteria are provided in separate hashes' do
@@ -1447,6 +1621,42 @@ describe Mongoid::Criteria::Queryable::Selectable do
1447
1621
  end
1448
1622
  end
1449
1623
  end
1624
+
1625
+ context 'when using multiple criteria and symbol operators' do
1626
+ context 'when using fields that meaningfully evolve values' do
1627
+
1628
+ let(:query) do
1629
+ Dictionary.any_of({a: 1}, :published.gt => Date.new(2020, 2, 3))
1630
+ end
1631
+
1632
+ it 'generates the expected query' do
1633
+ query.selector.should == {'$or' => [
1634
+ {'a' => 1},
1635
+ # Date instance is converted to a Time instance in local time,
1636
+ # because we are querying on a Time field and dates are interpreted
1637
+ # in local time when assigning to Time fields
1638
+ {'published' => {'$gt' => Time.local(2020, 2, 3)}},
1639
+ ]}
1640
+ end
1641
+ end
1642
+
1643
+ context 'when using fields that do not meaningfully evolve values' do
1644
+
1645
+ let(:query) do
1646
+ Dictionary.any_of({a: 1}, :submitted_on.gt => Date.new(2020, 2, 3))
1647
+ end
1648
+
1649
+ it 'generates the expected query' do
1650
+ query.selector.should == {'$or' => [
1651
+ {'a' => 1},
1652
+ # Date instance is converted to a Time instance in UTC,
1653
+ # because we are querying on a Date field and dates are interpreted
1654
+ # in UTC when persisted as dates by Mongoid
1655
+ {'submitted_on' => {'$gt' => Time.utc(2020, 2, 3)}},
1656
+ ]}
1657
+ end
1658
+ end
1659
+ end
1450
1660
  end
1451
1661
 
1452
1662
  describe "#not" do
@@ -1825,5 +2035,36 @@ describe Mongoid::Criteria::Queryable::Selectable do
1825
2035
  )
1826
2036
  end
1827
2037
  end
2038
+
2039
+ context 'when giving multiple conditions in one call on the same key with symbol operator' do
2040
+
2041
+ context 'non-regexp argument' do
2042
+ let(:selection) do
2043
+ query.not(field: 1, :field.gt => 0)
2044
+ end
2045
+
2046
+ it 'combines conditions with $eq' do
2047
+ selection.selector.should == {
2048
+ '$and' => ['$nor' => [
2049
+ 'field' => {'$eq' => 1, '$gt' => 0},
2050
+ ]]
2051
+ }
2052
+ end
2053
+ end
2054
+
2055
+ context 'regexp argument' do
2056
+ let(:selection) do
2057
+ query.not(field: /t/, :field.gt => 0)
2058
+ end
2059
+
2060
+ it 'combines conditions with $regex' do
2061
+ selection.selector.should == {
2062
+ '$and' => ['$nor' => [
2063
+ 'field' => {'$regex' => /t/, '$gt' => 0},
2064
+ ]]
2065
+ }
2066
+ end
2067
+ end
2068
+ end
1828
2069
  end
1829
2070
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ shared_examples_for "returns a cloned query" do
5
+
6
+ it "returns a cloned query" do
7
+ expect(selection).to_not equal(query)
8
+ end
9
+ end
10
+
11
+ shared_examples_for 'requires an argument' do
12
+ context "when provided no argument" do
13
+
14
+ let(:selection) do
15
+ query.send(query_method)
16
+ end
17
+
18
+ it "raises ArgumentError" do
19
+ expect do
20
+ selection.selector
21
+ end.to raise_error(ArgumentError)
22
+ end
23
+ end
24
+ end
25
+
26
+ shared_examples_for 'requires a non-nil argument' do
27
+ context "when provided nil" do
28
+
29
+ let(:selection) do
30
+ query.send(query_method, nil)
31
+ end
32
+
33
+ it "raises CriteriaArgumentRequired" do
34
+ expect do
35
+ selection.selector
36
+ end.to raise_error(Mongoid::Errors::CriteriaArgumentRequired, /#{query_method}/)
37
+ end
38
+ end
39
+ end