mongoid 8.0.6 → 8.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (183) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +3 -3
  4. data/README.md +3 -3
  5. data/Rakefile +0 -25
  6. data/lib/config/locales/en.yml +46 -14
  7. data/lib/mongoid/association/accessors.rb +2 -2
  8. data/lib/mongoid/association/builders.rb +1 -1
  9. data/lib/mongoid/association/embedded/batchable.rb +2 -2
  10. data/lib/mongoid/association/embedded/embedded_in/buildable.rb +2 -2
  11. data/lib/mongoid/association/embedded/embedded_in/proxy.rb +2 -1
  12. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +3 -2
  13. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +6 -6
  14. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +1 -1
  15. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +1 -1
  16. data/lib/mongoid/association/macros.rb +0 -6
  17. data/lib/mongoid/association/nested/one.rb +40 -2
  18. data/lib/mongoid/association/proxy.rb +1 -1
  19. data/lib/mongoid/association/referenced/counter_cache.rb +2 -2
  20. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +1 -1
  21. data/lib/mongoid/association/referenced/has_many/enumerable.rb +2 -2
  22. data/lib/mongoid/association/referenced/has_many/proxy.rb +3 -3
  23. data/lib/mongoid/association/reflections.rb +2 -2
  24. data/lib/mongoid/atomic.rb +0 -7
  25. data/lib/mongoid/attributes/dynamic.rb +1 -1
  26. data/lib/mongoid/attributes/nested.rb +2 -2
  27. data/lib/mongoid/attributes/processing.rb +5 -29
  28. data/lib/mongoid/attributes/projector.rb +1 -1
  29. data/lib/mongoid/attributes/readonly.rb +1 -1
  30. data/lib/mongoid/attributes.rb +8 -2
  31. data/lib/mongoid/changeable.rb +107 -5
  32. data/lib/mongoid/clients/storage_options.rb +2 -5
  33. data/lib/mongoid/clients/validators/storage.rb +1 -13
  34. data/lib/mongoid/collection_configurable.rb +58 -0
  35. data/lib/mongoid/composable.rb +2 -0
  36. data/lib/mongoid/config/defaults.rb +60 -0
  37. data/lib/mongoid/config/options.rb +0 -3
  38. data/lib/mongoid/config/validators/async_query_executor.rb +24 -0
  39. data/lib/mongoid/config/validators.rb +1 -0
  40. data/lib/mongoid/config.rb +99 -15
  41. data/lib/mongoid/contextual/atomic.rb +1 -1
  42. data/lib/mongoid/contextual/memory.rb +233 -33
  43. data/lib/mongoid/contextual/mongo/documents_loader.rb +177 -0
  44. data/lib/mongoid/contextual/mongo.rb +373 -113
  45. data/lib/mongoid/contextual/none.rb +162 -7
  46. data/lib/mongoid/contextual.rb +12 -0
  47. data/lib/mongoid/criteria/findable.rb +2 -2
  48. data/lib/mongoid/criteria/includable.rb +4 -3
  49. data/lib/mongoid/criteria/queryable/key.rb +1 -1
  50. data/lib/mongoid/criteria/queryable/mergeable.rb +1 -1
  51. data/lib/mongoid/criteria/queryable/optional.rb +8 -8
  52. data/lib/mongoid/criteria/queryable/selectable.rb +43 -12
  53. data/lib/mongoid/criteria/queryable/selector.rb +1 -1
  54. data/lib/mongoid/criteria/queryable/storable.rb +1 -1
  55. data/lib/mongoid/criteria.rb +6 -5
  56. data/lib/mongoid/deprecable.rb +1 -1
  57. data/lib/mongoid/errors/create_collection_failure.rb +33 -0
  58. data/lib/mongoid/errors/drop_collection_failure.rb +27 -0
  59. data/lib/mongoid/errors/immutable_attribute.rb +26 -0
  60. data/lib/mongoid/errors/invalid_async_query_executor.rb +25 -0
  61. data/lib/mongoid/errors/invalid_global_executor_concurrency.rb +22 -0
  62. data/lib/mongoid/errors/invalid_storage_parent.rb +2 -0
  63. data/lib/mongoid/errors.rb +4 -1
  64. data/lib/mongoid/extensions/hash.rb +2 -6
  65. data/lib/mongoid/extensions/object.rb +2 -2
  66. data/lib/mongoid/extensions/time.rb +2 -0
  67. data/lib/mongoid/fields/localized.rb +10 -0
  68. data/lib/mongoid/fields/standard.rb +10 -0
  69. data/lib/mongoid/fields.rb +53 -24
  70. data/lib/mongoid/findable.rb +27 -3
  71. data/lib/mongoid/interceptable.rb +7 -6
  72. data/lib/mongoid/matcher/eq_impl.rb +1 -1
  73. data/lib/mongoid/matcher/type.rb +1 -1
  74. data/lib/mongoid/persistable/creatable.rb +1 -0
  75. data/lib/mongoid/persistable/deletable.rb +1 -1
  76. data/lib/mongoid/persistable/savable.rb +13 -1
  77. data/lib/mongoid/persistable/unsettable.rb +2 -2
  78. data/lib/mongoid/persistable/updatable.rb +51 -1
  79. data/lib/mongoid/persistable/upsertable.rb +20 -1
  80. data/lib/mongoid/persistable.rb +3 -0
  81. data/lib/mongoid/query_cache.rb +5 -1
  82. data/lib/mongoid/railties/database.rake +7 -2
  83. data/lib/mongoid/reloadable.rb +5 -3
  84. data/lib/mongoid/stateful.rb +22 -1
  85. data/lib/mongoid/tasks/database.rake +12 -0
  86. data/lib/mongoid/tasks/database.rb +20 -0
  87. data/lib/mongoid/utils.rb +22 -0
  88. data/lib/mongoid/validatable/macros.rb +5 -5
  89. data/lib/mongoid/validatable.rb +4 -1
  90. data/lib/mongoid/version.rb +1 -1
  91. data/lib/mongoid/warnings.rb +17 -1
  92. data/lib/mongoid.rb +16 -3
  93. data/spec/integration/app_spec.rb +2 -2
  94. data/spec/integration/callbacks_models.rb +37 -0
  95. data/spec/integration/callbacks_spec.rb +134 -0
  96. data/spec/integration/discriminator_key_spec.rb +4 -5
  97. data/spec/integration/i18n_fallbacks_spec.rb +3 -2
  98. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +27 -0
  99. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +20 -25
  100. data/spec/mongoid/association/embedded/embeds_many_models.rb +1 -0
  101. data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +15 -2
  102. data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -18
  103. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +5 -27
  104. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +9 -50
  105. data/spec/mongoid/association/syncable_spec.rb +1 -1
  106. data/spec/mongoid/attributes_spec.rb +3 -33
  107. data/spec/mongoid/changeable_spec.rb +299 -24
  108. data/spec/mongoid/clients_spec.rb +122 -13
  109. data/spec/mongoid/collection_configurable_spec.rb +158 -0
  110. data/spec/mongoid/config/defaults_spec.rb +160 -0
  111. data/spec/mongoid/config_spec.rb +154 -27
  112. data/spec/mongoid/contextual/memory_spec.rb +332 -76
  113. data/spec/mongoid/contextual/mongo/documents_loader_spec.rb +187 -0
  114. data/spec/mongoid/contextual/mongo_spec.rb +1006 -77
  115. data/spec/mongoid/contextual/none_spec.rb +49 -2
  116. data/spec/mongoid/copyable_spec.rb +3 -11
  117. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -10
  118. data/spec/mongoid/criteria/queryable/options_spec.rb +1 -1
  119. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +419 -0
  120. data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -1
  121. data/spec/mongoid/criteria/queryable/selector_spec.rb +3 -76
  122. data/spec/mongoid/criteria/queryable/storable_spec.rb +0 -72
  123. data/spec/mongoid/criteria_projection_spec.rb +1 -4
  124. data/spec/mongoid/criteria_spec.rb +5 -9
  125. data/spec/mongoid/errors/readonly_document_spec.rb +2 -2
  126. data/spec/mongoid/extensions/hash_spec.rb +3 -3
  127. data/spec/mongoid/extensions/time_spec.rb +8 -43
  128. data/spec/mongoid/extensions/time_with_zone_spec.rb +7 -52
  129. data/spec/mongoid/fields/localized_spec.rb +46 -28
  130. data/spec/mongoid/fields_spec.rb +136 -77
  131. data/spec/mongoid/findable_spec.rb +391 -34
  132. data/spec/mongoid/indexable_spec.rb +16 -10
  133. data/spec/mongoid/interceptable_spec.rb +15 -3
  134. data/spec/mongoid/persistable/deletable_spec.rb +26 -6
  135. data/spec/mongoid/persistable/destroyable_spec.rb +26 -6
  136. data/spec/mongoid/persistable/incrementable_spec.rb +37 -0
  137. data/spec/mongoid/persistable/logical_spec.rb +37 -0
  138. data/spec/mongoid/persistable/poppable_spec.rb +36 -0
  139. data/spec/mongoid/persistable/pullable_spec.rb +72 -0
  140. data/spec/mongoid/persistable/pushable_spec.rb +72 -0
  141. data/spec/mongoid/persistable/renamable_spec.rb +36 -0
  142. data/spec/mongoid/persistable/savable_spec.rb +96 -0
  143. data/spec/mongoid/persistable/settable_spec.rb +37 -0
  144. data/spec/mongoid/persistable/unsettable_spec.rb +36 -0
  145. data/spec/mongoid/persistable/updatable_spec.rb +20 -28
  146. data/spec/mongoid/persistable/upsertable_spec.rb +80 -6
  147. data/spec/mongoid/persistence_context_spec.rb +7 -57
  148. data/spec/mongoid/query_cache_spec.rb +56 -61
  149. data/spec/mongoid/reloadable_spec.rb +24 -28
  150. data/spec/mongoid/scopable_spec.rb +70 -0
  151. data/spec/mongoid/serializable_spec.rb +9 -30
  152. data/spec/mongoid/stateful_spec.rb +122 -8
  153. data/spec/mongoid/tasks/database_rake_spec.rb +74 -0
  154. data/spec/mongoid/tasks/database_spec.rb +127 -0
  155. data/spec/mongoid/timestamps_spec.rb +9 -11
  156. data/spec/mongoid/touchable_spec.rb +277 -5
  157. data/spec/mongoid/touchable_spec_models.rb +3 -1
  158. data/spec/mongoid/traversable_spec.rb +9 -24
  159. data/spec/mongoid/validatable/uniqueness_spec.rb +2 -3
  160. data/spec/mongoid_spec.rb +36 -10
  161. data/spec/shared/lib/mrss/docker_runner.rb +7 -0
  162. data/spec/shared/lib/mrss/lite_constraints.rb +2 -2
  163. data/spec/shared/lib/mrss/server_version_registry.rb +16 -23
  164. data/spec/shared/lib/mrss/utils.rb +28 -6
  165. data/spec/shared/share/Dockerfile.erb +36 -40
  166. data/spec/shared/shlib/server.sh +28 -4
  167. data/spec/shared/shlib/set_env.sh +4 -4
  168. data/spec/spec_helper.rb +5 -0
  169. data/spec/support/immutable_ids.rb +118 -0
  170. data/spec/support/macros.rb +47 -15
  171. data/spec/support/models/artist.rb +0 -1
  172. data/spec/support/models/band.rb +1 -0
  173. data/spec/support/models/book.rb +1 -0
  174. data/spec/support/models/building.rb +2 -0
  175. data/spec/support/models/cover.rb +10 -0
  176. data/spec/support/models/person.rb +0 -1
  177. data/spec/support/models/product.rb +1 -0
  178. data.tar.gz.sig +0 -0
  179. metadata +686 -652
  180. metadata.gz.sig +0 -0
  181. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -60
  182. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -60
  183. data/spec/support/models/purse.rb +0 -9
@@ -54,8 +54,17 @@ describe Mongoid::Contextual::None do
54
54
  end
55
55
 
56
56
  describe "#pluck" do
57
- it "returns an empty array" do
58
- expect(context.pluck(:id)).to eq([])
57
+
58
+ context "when plucking one field" do
59
+ it "returns an empty array" do
60
+ expect(context.pluck(:id)).to eq([])
61
+ end
62
+ end
63
+
64
+ context "when plucking multiple fields" do
65
+ it "returns an empty array" do
66
+ expect(context.pluck(:id, :foo)).to eq([])
67
+ end
59
68
  end
60
69
  end
61
70
 
@@ -81,6 +90,14 @@ describe Mongoid::Contextual::None do
81
90
  end
82
91
  end
83
92
 
93
+ describe "#first!" do
94
+ it "raises an error" do
95
+ expect do
96
+ context.first!
97
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
98
+ end
99
+ end
100
+
84
101
  describe "#last" do
85
102
  it "returns nil" do
86
103
  expect(context.last).to be_nil
@@ -91,6 +108,14 @@ describe Mongoid::Contextual::None do
91
108
  end
92
109
  end
93
110
 
111
+ describe "#last!" do
112
+ it "raises an error" do
113
+ expect do
114
+ context.last!
115
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
116
+ end
117
+ end
118
+
94
119
  describe "#take" do
95
120
  it "returns nil" do
96
121
  expect(context.take).to be_nil
@@ -109,6 +134,28 @@ describe Mongoid::Contextual::None do
109
134
  end
110
135
  end
111
136
 
137
+ [ :second,
138
+ :third,
139
+ :fourth,
140
+ :fifth,
141
+ :second_to_last,
142
+ :third_to_last
143
+ ].each do |meth|
144
+ describe "##{meth}" do
145
+ it "returns nil" do
146
+ expect(context.send(meth)).to be_nil
147
+ end
148
+ end
149
+
150
+ describe "##{meth}!" do
151
+ it "raises an error" do
152
+ expect do
153
+ context.send("#{meth}!")
154
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
155
+ end
156
+ end
157
+ end
158
+
112
159
  describe "#length" do
113
160
  it "returns zero" do
114
161
  expect(context.length).to eq(0)
@@ -90,7 +90,7 @@ describe Mongoid::Copyable do
90
90
  end
91
91
 
92
92
  it 'calls constructor with explicitly declared attributes only' do
93
- expect(Mongoid::Factory).to receive(:build).with(cls, 'name' => 'test').and_call_original
93
+ expect(Mongoid::Factory).to receive(:build).with(cls, { 'name' => 'test' }).and_call_original
94
94
  cloned
95
95
  end
96
96
  end
@@ -164,19 +164,15 @@ describe Mongoid::Copyable do
164
164
  end
165
165
 
166
166
  context "when cloning a document with multiple languages field" do
167
+ with_default_i18n_configs
167
168
 
168
169
  before do
169
- I18n.enforce_available_locales = false
170
170
  I18n.locale = 'pt_BR'
171
171
  person.desc = "descrição"
172
172
  person.addresses.first.name = "descrição"
173
173
  person.save!
174
174
  end
175
175
 
176
- after do
177
- I18n.locale = :en
178
- end
179
-
180
176
  let!(:from_db) do
181
177
  Person.find(person.id)
182
178
  end
@@ -212,22 +208,18 @@ describe Mongoid::Copyable do
212
208
  end
213
209
 
214
210
  context "when cloning a document with polymorphic embedded documents with multiple language field" do
211
+ with_default_i18n_configs
215
212
 
216
213
  let!(:shipment_address) do
217
214
  person.addresses.build({ shipping_name: "Title" }, ShipmentAddress)
218
215
  end
219
216
 
220
217
  before do
221
- I18n.enforce_available_locales = false
222
218
  I18n.locale = 'pt_BR'
223
219
  person.addresses.type(ShipmentAddress).each { |address| address.shipping_name = "Título" }
224
220
  person.save!
225
221
  end
226
222
 
227
- after do
228
- I18n.locale = :en
229
- end
230
-
231
223
  let!(:from_db) do
232
224
  Person.find(person.id)
233
225
  end
@@ -72,11 +72,8 @@ describe String do
72
72
  context "when the string without timezone" do
73
73
 
74
74
  context "when using active support's time zone" do
75
-
76
- before do
77
- Mongoid.use_activesupport_time_zone = true
78
- ::Time.zone = "Tokyo"
79
- end
75
+ config_override :use_activesupport_time_zone, true
76
+ time_zone_override "Tokyo"
80
77
 
81
78
  let(:date) do
82
79
  "2010-01-01 5:00:00"
@@ -92,11 +89,8 @@ describe String do
92
89
  end
93
90
 
94
91
  context "when not using active support's time zone" do
95
-
96
- before do
97
- Mongoid.use_activesupport_time_zone = false
98
- ::Time.zone = nil
99
- end
92
+ config_override :use_activesupport_time_zone, false
93
+ time_zone_override nil
100
94
 
101
95
  let(:date) do
102
96
  "2010-01-01 5:00:00"
@@ -221,6 +221,7 @@ describe Mongoid::Criteria::Queryable::Options do
221
221
  end
222
222
 
223
223
  context "when the serializer is localized" do
224
+ with_default_i18n_configs
224
225
 
225
226
  before(:all) do
226
227
  class Field
@@ -232,7 +233,6 @@ describe Mongoid::Criteria::Queryable::Options do
232
233
 
233
234
  after(:all) do
234
235
  Object.send(:remove_const, :Field)
235
- ::I18n.locale = :en
236
236
  end
237
237
 
238
238
  let(:options) do
@@ -2116,4 +2116,423 @@ describe Mongoid::Criteria::Queryable::Selectable do
2116
2116
  end
2117
2117
  end
2118
2118
  end
2119
+
2120
+ describe "#none_of" do
2121
+ context 'when argument is a mix of Criteria and hashes' do
2122
+ let(:query) { Mongoid::Query.new.where(hello: 'world') }
2123
+ let(:other1) { Mongoid::Query.new.where(foo: 'bar') }
2124
+ let(:other2) { { bar: 42 } }
2125
+ let(:other3) { Mongoid::Query.new.where(a: 2) }
2126
+
2127
+ let(:result) { query.none_of(other1, other2, other3) }
2128
+
2129
+ it 'combines' do
2130
+ expect(result.selector).to eq(
2131
+ 'hello' => 'world',
2132
+ '$nor' => [
2133
+ {'foo' => 'bar'},
2134
+ {'bar' => 42},
2135
+ {'a' => 2},
2136
+ ],
2137
+ )
2138
+ end
2139
+ end
2140
+
2141
+ context "when provided no arguments" do
2142
+ let(:selection) { query.none_of }
2143
+
2144
+ it_behaves_like 'returns a cloned query'
2145
+
2146
+ it "does not add any criteria" do
2147
+ expect(selection.selector).to eq({})
2148
+ end
2149
+
2150
+ it "returns the query" do
2151
+ expect(selection).to eq(query)
2152
+ end
2153
+ end
2154
+
2155
+ context "when provided nil" do
2156
+ let(:selection) { query.none_of(nil) }
2157
+
2158
+ it_behaves_like 'returns a cloned query'
2159
+
2160
+ it "does not add any criteria" do
2161
+ expect(selection.selector).to eq({})
2162
+ end
2163
+
2164
+ it "returns the query" do
2165
+ expect(selection).to eq(query)
2166
+ end
2167
+ end
2168
+
2169
+ context "when provided a single criterion" do
2170
+ let(:selection) { query.none_of(field: [ 1, 2 ]) }
2171
+
2172
+ it_behaves_like 'returns a cloned query'
2173
+
2174
+ it 'adds the $nor selector' do
2175
+ expect(selection.selector).to eq(
2176
+ '$nor' => [ { 'field' => [ 1, 2 ] } ],
2177
+ )
2178
+ end
2179
+
2180
+ context 'when the criterion is wrapped in array' do
2181
+ let(:selection) { query.none_of([{ field: [ 1, 2 ] }]) }
2182
+
2183
+ it_behaves_like 'returns a cloned query'
2184
+
2185
+ it 'adds the condition' do
2186
+ expect(selection.selector).to eq(
2187
+ '$nor' => [ { 'field' => [ 1, 2 ] } ],
2188
+ )
2189
+ end
2190
+
2191
+ context 'when the array has nil as one of the elements' do
2192
+ let(:selection) { query.none_of([{ field: [ 1, 2 ] }, nil]) }
2193
+
2194
+ it_behaves_like 'returns a cloned query'
2195
+
2196
+ it 'adds the $nor selector ignoring the nil element' do
2197
+ expect(selection.selector).to eq(
2198
+ '$nor' => [ { 'field' => [ 1, 2 ] } ],
2199
+ )
2200
+ end
2201
+ end
2202
+ end
2203
+
2204
+ context 'when query already has a condition on another field' do
2205
+ context 'when there is one argument' do
2206
+ let(:selection) { query.where(foo: 'bar').none_of(field: [ 1, 2 ]) }
2207
+
2208
+ it 'adds the new condition' do
2209
+ expect(selection.selector).to eq(
2210
+ 'foo' => 'bar',
2211
+ '$nor' => [ { 'field' => [1, 2] } ],
2212
+ )
2213
+ end
2214
+ end
2215
+
2216
+ context 'when there are multiple arguments' do
2217
+ let(:selection) do
2218
+ query.where(foo: 'bar').none_of({ field: [ 1, 2 ] }, { hello: 'world' })
2219
+ end
2220
+
2221
+ it 'adds the new condition' do
2222
+ expect(selection.selector).to eq(
2223
+ 'foo' => 'bar',
2224
+ '$nor' => [
2225
+ { 'field' => [1, 2] },
2226
+ { 'hello' => 'world' },
2227
+ ],
2228
+ )
2229
+ end
2230
+ end
2231
+ end
2232
+
2233
+ context 'when query already has a $nor condition and another condition' do
2234
+ let(:selection) do
2235
+ query.nor(field: [ 1, 2 ]).where(foo: 'bar').none_of(test: 1)
2236
+ end
2237
+
2238
+ it 'adds the new condition' do
2239
+ expect(selection.selector).to eq(
2240
+ '$nor' => [ { 'field' => [1, 2] } ],
2241
+ 'foo' => 'bar',
2242
+ '$and' => [ { '$nor' => [ { 'test' => 1 } ] } ]
2243
+ )
2244
+ end
2245
+ end
2246
+
2247
+ context 'when none_of has multiple arguments' do
2248
+ let(:selection) do
2249
+ query.nor(field: [ 1, 2 ]).where(foo: 'bar').none_of({a: 1}, {b: 2})
2250
+ end
2251
+
2252
+ it 'adds the new condition to top level' do
2253
+ expect(selection.selector).to eq(
2254
+ 'foo' => 'bar',
2255
+ '$nor' => [ { 'field' => [1, 2] } ],
2256
+ '$and' => [ { '$nor' => [ { 'a' => 1 }, { 'b' => 2 } ] } ]
2257
+ )
2258
+ end
2259
+
2260
+ context 'when query already has a top-level $and' do
2261
+ let(:selection) do
2262
+ query.nor(field: [ 1, 2 ]).where('$and' => [foo: 'bar']).none_of({a: 1}, {b: 2})
2263
+ end
2264
+
2265
+ it 'adds the new condition to top level $and' do
2266
+ expect(selection.selector).to eq(
2267
+ '$nor' => [ { 'field' => [1, 2] } ],
2268
+ '$and' => [
2269
+ { 'foo' => 'bar' },
2270
+ { '$nor' => [ { 'a' => 1 }, { 'b' => 2 } ] }
2271
+ ],
2272
+ )
2273
+ end
2274
+ end
2275
+ end
2276
+ end
2277
+
2278
+ context "when provided multiple criteria" do
2279
+ context "when the criteria are for different fields" do
2280
+ let(:selection) do
2281
+ query.none_of({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
2282
+ end
2283
+
2284
+ it_behaves_like 'returns a cloned query'
2285
+
2286
+ it "adds the $nor selector" do
2287
+ expect(selection.selector).to eq({
2288
+ "$nor" => [
2289
+ { "first" => [ 1, 2 ] },
2290
+ { "second" => [ 3, 4 ] }
2291
+ ]
2292
+ })
2293
+ end
2294
+ end
2295
+
2296
+ context "when the criteria uses a Key instance" do
2297
+ let(:selection) do
2298
+ query.none_of({ first: [ 1, 2 ] }, { :second.gt => 3 })
2299
+ end
2300
+
2301
+ it "adds the $nor selector" do
2302
+ expect(selection.selector).to eq({
2303
+ "$nor" => [
2304
+ { "first" => [ 1, 2 ] },
2305
+ { "second" => { "$gt" => 3 }}
2306
+ ]
2307
+ })
2308
+ end
2309
+
2310
+ it_behaves_like 'returns a cloned query'
2311
+ end
2312
+
2313
+ context 'when criteria are simple and handled via Key' do
2314
+ shared_examples_for 'adds conditions with $nor' do
2315
+ it "adds conditions with $nor" do
2316
+ expect(selection.selector).to eq({
2317
+ '$nor' => [
2318
+ {'field' => 3},
2319
+ {'field' => {'$lt' => 5}},
2320
+ ],
2321
+ })
2322
+ end
2323
+
2324
+ it_behaves_like 'returns a cloned query'
2325
+ end
2326
+
2327
+ shared_examples_for 'combines conditions with $eq' do
2328
+ it "combines conditions with $eq" do
2329
+ expect(selection.selector).to eq({
2330
+ '$nor' => [ { 'field' => { '$eq' => 3, '$lt' => 5 } } ]
2331
+ })
2332
+ end
2333
+
2334
+ it_behaves_like 'returns a cloned query'
2335
+ end
2336
+
2337
+ shared_examples_for 'combines conditions with $regex' do
2338
+ it 'combines conditions with $regex' do
2339
+ expect(selection.selector).to eq({
2340
+ '$nor' => [ { 'field' => { '$regex' => /t/, '$lt' => 5 } } ]
2341
+ })
2342
+ end
2343
+
2344
+ it_behaves_like 'returns a cloned query'
2345
+ end
2346
+
2347
+ context 'criteria are provided in the same hash' do
2348
+ context 'non-regexp argument' do
2349
+ let(:selection) { query.none_of(:field => 3, :field.lt => 5) }
2350
+ it_behaves_like 'combines conditions with $eq'
2351
+ end
2352
+
2353
+ context 'regexp argument' do
2354
+ let(:selection) { query.none_of(:field => /t/, :field.lt => 5) }
2355
+ it_behaves_like 'combines conditions with $regex'
2356
+ end
2357
+ end
2358
+
2359
+ context 'criteria are provided in separate hashes' do
2360
+ let(:selection) { query.none_of({:field => 3}, {:field.lt => 5}) }
2361
+ it_behaves_like 'adds conditions with $nor'
2362
+ end
2363
+
2364
+ context 'when the criterion is wrapped in an array' do
2365
+ let(:selection) { query.none_of([:field => 3], [:field.lt => 5]) }
2366
+ it_behaves_like 'adds conditions with $nor'
2367
+ end
2368
+ end
2369
+
2370
+ context 'when criteria are handled via Key and simple' do
2371
+ shared_examples_for 'adds conditions with $nor' do
2372
+ it 'adds conditions with $nor' do
2373
+ expect(selection.selector).to eq({
2374
+ '$nor' => [
2375
+ { 'field' => { '$gt' => 3 } },
2376
+ { 'field' => 5 },
2377
+ ],
2378
+ })
2379
+ end
2380
+
2381
+ it_behaves_like 'returns a cloned query'
2382
+ end
2383
+
2384
+ shared_examples_for 'combines conditions with $eq' do
2385
+ it 'combines conditions with $eq' do
2386
+ expect(selection.selector).to eq(
2387
+ '$nor' => [ { 'field' => {'$gt' => 3, '$eq' => 5} } ],
2388
+ )
2389
+ end
2390
+
2391
+ it_behaves_like 'returns a cloned query'
2392
+ end
2393
+
2394
+ shared_examples_for 'combines conditions with $regex' do
2395
+ it 'combines conditions with $regex' do
2396
+ expect(selection.selector).to eq(
2397
+ '$nor' => [ { 'field' => {'$gt' => 3, '$regex' => /t/} } ],
2398
+ )
2399
+ end
2400
+
2401
+ it_behaves_like 'returns a cloned query'
2402
+ end
2403
+
2404
+ context 'criteria are provided in the same hash' do
2405
+ context 'non-regexp argument' do
2406
+ let(:selection) { query.none_of(:field.gt => 3, :field => 5) }
2407
+ it_behaves_like 'combines conditions with $eq'
2408
+ end
2409
+
2410
+ context 'regexp argument' do
2411
+ let(:selection) { query.none_of(:field.gt => 3, :field => /t/) }
2412
+ it_behaves_like 'combines conditions with $regex'
2413
+ end
2414
+ end
2415
+
2416
+ context 'criteria are provided in separate hashes' do
2417
+ let(:selection) { query.none_of({:field.gt => 3}, {:field => 5}) }
2418
+ it_behaves_like 'adds conditions with $nor'
2419
+ end
2420
+
2421
+ context 'when the criterion is wrapped in an array' do
2422
+ let(:selection) { query.none_of([:field.gt => 3], [:field => 5]) }
2423
+ it_behaves_like 'adds conditions with $nor'
2424
+ end
2425
+ end
2426
+
2427
+ context 'when a criterion has an aliased field' do
2428
+ let(:selection) { query.none_of({ id: 1 }) }
2429
+
2430
+ it 'adds the $nor selector and aliases the field' do
2431
+ expect(selection.selector).to eq('$nor' => [{ '_id' => 1 }])
2432
+ end
2433
+
2434
+ it_behaves_like 'returns a cloned query'
2435
+ end
2436
+
2437
+ context 'when a criterion is wrapped in an array' do
2438
+ let(:selection) do
2439
+ query.none_of([{ first: [ 1, 2 ] }, { :second.gt => 3 }])
2440
+ end
2441
+
2442
+ it_behaves_like 'returns a cloned query'
2443
+
2444
+ it 'adds the $ nor selector' do
2445
+ expect(selection.selector).to eq({
2446
+ '$nor' => [
2447
+ { 'first' => [ 1, 2 ] },
2448
+ { 'second' => { '$gt' => 3 }}
2449
+ ]
2450
+ })
2451
+ end
2452
+ end
2453
+
2454
+ context "when the criteria are on the same field" do
2455
+ let(:selection) do
2456
+ query.none_of({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
2457
+ end
2458
+
2459
+ it_behaves_like 'returns a cloned query'
2460
+
2461
+ it 'appends both $nor expressions' do
2462
+ expect(selection.selector).to eq({
2463
+ "$nor" => [
2464
+ { "first" => [ 1, 2 ] },
2465
+ { "first" => [ 3, 4 ] }
2466
+ ]
2467
+ })
2468
+ end
2469
+ end
2470
+ end
2471
+
2472
+ context 'when chaining the criteria' do
2473
+ context 'when the criteria are for different fields' do
2474
+ let(:selection) do
2475
+ query.none_of(first: [ 1, 2 ]).none_of(second: [ 3, 4 ])
2476
+ end
2477
+
2478
+ it_behaves_like 'returns a cloned query'
2479
+
2480
+ it 'adds the conditions separately' do
2481
+ expect(selection.selector).to eq(
2482
+ '$nor' => [ { 'first' => [ 1, 2 ] } ],
2483
+ '$and' => [ { '$nor' => [ { 'second' => [ 3, 4 ] } ] } ],
2484
+ )
2485
+ end
2486
+ end
2487
+
2488
+ context "when the criteria are on the same field" do
2489
+ let(:selection) do
2490
+ query.none_of(first: [ 1, 2 ]).none_of(first: [ 3, 4 ])
2491
+ end
2492
+
2493
+ it_behaves_like 'returns a cloned query'
2494
+
2495
+ it 'adds the conditions separately' do
2496
+ expect(selection.selector).to eq(
2497
+ '$nor' => [ { 'first' => [ 1, 2 ] } ],
2498
+ '$and' => [ { '$nor' => [ { 'first' => [ 3, 4 ] } ] } ]
2499
+ )
2500
+ end
2501
+ end
2502
+ end
2503
+
2504
+ context 'when using multiple criteria and symbol operators' do
2505
+ context 'when using fields that meaningfully evolve values' do
2506
+ let(:query) do
2507
+ Dictionary.none_of({a: 1}, :published.gt => Date.new(2020, 2, 3))
2508
+ end
2509
+
2510
+ it 'generates the expected query' do
2511
+ query.selector.should == {'$nor' => [
2512
+ {'a' => 1},
2513
+ # Date instance is converted to a Time instance in local time,
2514
+ # because we are querying on a Time field and dates are interpreted
2515
+ # in local time when assigning to Time fields
2516
+ {'published' => {'$gt' => Time.local(2020, 2, 3) } },
2517
+ ] }
2518
+ end
2519
+ end
2520
+
2521
+ context 'when using fields that do not meaningfully evolve values' do
2522
+ let(:query) do
2523
+ Dictionary.none_of({a: 1}, :submitted_on.gt => Date.new(2020, 2, 3))
2524
+ end
2525
+
2526
+ it 'generates the expected query' do
2527
+ query.selector.should == {'$nor' => [
2528
+ {'a' => 1},
2529
+ # Date instance is converted to a Time instance in UTC,
2530
+ # because we are querying on a Date field and dates are interpreted
2531
+ # in UTC when persisted as dates by Mongoid
2532
+ {'submitted_on' => {'$gt' => Time.utc(2020, 2, 3)}},
2533
+ ]}
2534
+ end
2535
+ end
2536
+ end
2537
+ end
2119
2538
  end
@@ -2045,7 +2045,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
2045
2045
  end
2046
2046
 
2047
2047
  # MongoDB server can only handle one text expression at a time,
2048
- # per https://docs.mongodb.com/manual/reference/operator/query/text/.
2048
+ # per https://www.mongodb.com/docs/manual/reference/operator/query/text/.
2049
2049
  # Nonetheless we test that the query is built correctly when
2050
2050
  # a user supplies more than one text condition.
2051
2051
  it 'merges conditions' do