mongoid 8.0.10 → 8.1.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 (212) 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 +18 -67
  6. data/lib/config/locales/en.yml +46 -14
  7. data/lib/mongoid/association/accessors.rb +3 -7
  8. data/lib/mongoid/association/builders.rb +1 -1
  9. data/lib/mongoid/association/eager_loadable.rb +0 -3
  10. data/lib/mongoid/association/embedded/batchable.rb +2 -2
  11. data/lib/mongoid/association/embedded/embedded_in/buildable.rb +2 -2
  12. data/lib/mongoid/association/embedded/embedded_in/proxy.rb +2 -1
  13. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +3 -2
  14. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +6 -6
  15. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +1 -1
  16. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +1 -1
  17. data/lib/mongoid/association/macros.rb +0 -6
  18. data/lib/mongoid/association/nested/one.rb +40 -2
  19. data/lib/mongoid/association/proxy.rb +1 -1
  20. data/lib/mongoid/association/referenced/counter_cache.rb +2 -2
  21. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +1 -1
  22. data/lib/mongoid/association/referenced/has_many/enumerable.rb +6 -23
  23. data/lib/mongoid/association/referenced/has_many/proxy.rb +3 -3
  24. data/lib/mongoid/association/reflections.rb +2 -2
  25. data/lib/mongoid/atomic.rb +7 -16
  26. data/lib/mongoid/attributes/dynamic.rb +1 -1
  27. data/lib/mongoid/attributes/nested.rb +2 -2
  28. data/lib/mongoid/attributes/processing.rb +5 -29
  29. data/lib/mongoid/attributes/projector.rb +1 -1
  30. data/lib/mongoid/attributes/readonly.rb +1 -1
  31. data/lib/mongoid/attributes.rb +8 -2
  32. data/lib/mongoid/changeable.rb +107 -5
  33. data/lib/mongoid/clients/storage_options.rb +2 -5
  34. data/lib/mongoid/clients/validators/storage.rb +1 -13
  35. data/lib/mongoid/collection_configurable.rb +58 -0
  36. data/lib/mongoid/composable.rb +2 -0
  37. data/lib/mongoid/config/defaults.rb +60 -0
  38. data/lib/mongoid/config/options.rb +0 -3
  39. data/lib/mongoid/config/validators/async_query_executor.rb +24 -0
  40. data/lib/mongoid/config/validators.rb +1 -0
  41. data/lib/mongoid/config.rb +88 -27
  42. data/lib/mongoid/contextual/atomic.rb +1 -1
  43. data/lib/mongoid/contextual/memory.rb +233 -33
  44. data/lib/mongoid/contextual/mongo/documents_loader.rb +177 -0
  45. data/lib/mongoid/contextual/mongo.rb +370 -133
  46. data/lib/mongoid/contextual/none.rb +162 -7
  47. data/lib/mongoid/contextual.rb +12 -0
  48. data/lib/mongoid/criteria/findable.rb +2 -2
  49. data/lib/mongoid/criteria/includable.rb +4 -3
  50. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -15
  51. data/lib/mongoid/criteria/queryable/key.rb +1 -1
  52. data/lib/mongoid/criteria/queryable/mergeable.rb +1 -1
  53. data/lib/mongoid/criteria/queryable/optional.rb +8 -8
  54. data/lib/mongoid/criteria/queryable/selectable.rb +43 -12
  55. data/lib/mongoid/criteria/queryable/selector.rb +1 -1
  56. data/lib/mongoid/criteria/queryable/storable.rb +1 -1
  57. data/lib/mongoid/criteria.rb +6 -5
  58. data/lib/mongoid/deprecable.rb +1 -2
  59. data/lib/mongoid/deprecation.rb +3 -3
  60. data/lib/mongoid/document.rb +1 -8
  61. data/lib/mongoid/errors/create_collection_failure.rb +33 -0
  62. data/lib/mongoid/errors/drop_collection_failure.rb +27 -0
  63. data/lib/mongoid/errors/immutable_attribute.rb +26 -0
  64. data/lib/mongoid/errors/invalid_async_query_executor.rb +25 -0
  65. data/lib/mongoid/errors/invalid_global_executor_concurrency.rb +22 -0
  66. data/lib/mongoid/errors/invalid_storage_parent.rb +2 -0
  67. data/lib/mongoid/errors.rb +4 -1
  68. data/lib/mongoid/extensions/hash.rb +2 -24
  69. data/lib/mongoid/extensions/object.rb +2 -2
  70. data/lib/mongoid/extensions/time.rb +2 -0
  71. data/lib/mongoid/fields/localized.rb +10 -0
  72. data/lib/mongoid/fields/standard.rb +10 -0
  73. data/lib/mongoid/fields.rb +59 -35
  74. data/lib/mongoid/findable.rb +27 -3
  75. data/lib/mongoid/interceptable.rb +6 -116
  76. data/lib/mongoid/matcher/eq_impl.rb +1 -1
  77. data/lib/mongoid/matcher/type.rb +1 -1
  78. data/lib/mongoid/persistable/creatable.rb +1 -0
  79. data/lib/mongoid/persistable/deletable.rb +1 -1
  80. data/lib/mongoid/persistable/savable.rb +13 -1
  81. data/lib/mongoid/persistable/unsettable.rb +2 -2
  82. data/lib/mongoid/persistable/updatable.rb +51 -1
  83. data/lib/mongoid/persistable/upsertable.rb +20 -1
  84. data/lib/mongoid/persistable.rb +3 -0
  85. data/lib/mongoid/query_cache.rb +5 -1
  86. data/lib/mongoid/railties/database.rake +7 -2
  87. data/lib/mongoid/reloadable.rb +5 -3
  88. data/lib/mongoid/stateful.rb +22 -1
  89. data/lib/mongoid/tasks/database.rake +12 -0
  90. data/lib/mongoid/tasks/database.rb +20 -0
  91. data/lib/mongoid/timestamps/created.rb +1 -8
  92. data/lib/mongoid/traversable.rb +0 -12
  93. data/lib/mongoid/utils.rb +22 -0
  94. data/lib/mongoid/validatable/associated.rb +17 -98
  95. data/lib/mongoid/validatable/macros.rb +5 -5
  96. data/lib/mongoid/validatable.rb +4 -9
  97. data/lib/mongoid/version.rb +1 -1
  98. data/lib/mongoid/warnings.rb +17 -1
  99. data/lib/mongoid.rb +16 -3
  100. data/spec/integration/app_spec.rb +2 -6
  101. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +0 -40
  102. data/spec/integration/callbacks_spec.rb +99 -12
  103. data/spec/integration/discriminator_key_spec.rb +4 -5
  104. data/spec/integration/i18n_fallbacks_spec.rb +3 -2
  105. data/spec/mongoid/association/eager_spec.rb +2 -24
  106. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +27 -0
  107. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +20 -25
  108. data/spec/mongoid/association/embedded/embeds_many_models.rb +1 -0
  109. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +0 -4
  110. data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +15 -2
  111. data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -18
  112. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +42 -55
  113. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +9 -50
  114. data/spec/mongoid/association/syncable_spec.rb +1 -1
  115. data/spec/mongoid/association_spec.rb +0 -60
  116. data/spec/mongoid/attributes_spec.rb +3 -33
  117. data/spec/mongoid/changeable_spec.rb +299 -24
  118. data/spec/mongoid/clients_spec.rb +122 -13
  119. data/spec/mongoid/collection_configurable_spec.rb +158 -0
  120. data/spec/mongoid/config/defaults_spec.rb +160 -0
  121. data/spec/mongoid/config_spec.rb +154 -27
  122. data/spec/mongoid/contextual/memory_spec.rb +332 -76
  123. data/spec/mongoid/contextual/mongo/documents_loader_spec.rb +187 -0
  124. data/spec/mongoid/contextual/mongo_spec.rb +1009 -125
  125. data/spec/mongoid/contextual/none_spec.rb +49 -2
  126. data/spec/mongoid/copyable_spec.rb +2 -10
  127. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -10
  128. data/spec/mongoid/criteria/queryable/options_spec.rb +1 -1
  129. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +419 -0
  130. data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -1
  131. data/spec/mongoid/criteria/queryable/selector_spec.rb +3 -76
  132. data/spec/mongoid/criteria/queryable/storable_spec.rb +0 -72
  133. data/spec/mongoid/criteria_projection_spec.rb +1 -4
  134. data/spec/mongoid/criteria_spec.rb +5 -9
  135. data/spec/mongoid/document_spec.rb +0 -27
  136. data/spec/mongoid/errors/readonly_document_spec.rb +2 -2
  137. data/spec/mongoid/extensions/hash_spec.rb +3 -3
  138. data/spec/mongoid/extensions/time_spec.rb +8 -43
  139. data/spec/mongoid/extensions/time_with_zone_spec.rb +7 -52
  140. data/spec/mongoid/fields/localized_spec.rb +46 -28
  141. data/spec/mongoid/fields_spec.rb +136 -77
  142. data/spec/mongoid/findable_spec.rb +391 -34
  143. data/spec/mongoid/indexable_spec.rb +16 -10
  144. data/spec/mongoid/interceptable_spec.rb +153 -442
  145. data/spec/mongoid/interceptable_spec_models.rb +111 -51
  146. data/spec/mongoid/persistable/deletable_spec.rb +26 -6
  147. data/spec/mongoid/persistable/destroyable_spec.rb +26 -6
  148. data/spec/mongoid/persistable/incrementable_spec.rb +37 -0
  149. data/spec/mongoid/persistable/logical_spec.rb +37 -0
  150. data/spec/mongoid/persistable/poppable_spec.rb +36 -0
  151. data/spec/mongoid/persistable/pullable_spec.rb +72 -0
  152. data/spec/mongoid/persistable/pushable_spec.rb +72 -0
  153. data/spec/mongoid/persistable/renamable_spec.rb +36 -0
  154. data/spec/mongoid/persistable/savable_spec.rb +96 -0
  155. data/spec/mongoid/persistable/settable_spec.rb +37 -0
  156. data/spec/mongoid/persistable/unsettable_spec.rb +36 -0
  157. data/spec/mongoid/persistable/updatable_spec.rb +20 -28
  158. data/spec/mongoid/persistable/upsertable_spec.rb +80 -6
  159. data/spec/mongoid/persistence_context_spec.rb +7 -57
  160. data/spec/mongoid/query_cache_spec.rb +56 -61
  161. data/spec/mongoid/reloadable_spec.rb +24 -28
  162. data/spec/mongoid/scopable_spec.rb +70 -0
  163. data/spec/mongoid/serializable_spec.rb +23 -44
  164. data/spec/mongoid/stateful_spec.rb +122 -8
  165. data/spec/mongoid/tasks/database_rake_spec.rb +74 -0
  166. data/spec/mongoid/tasks/database_spec.rb +127 -0
  167. data/spec/mongoid/timestamps/created_spec.rb +0 -23
  168. data/spec/mongoid/timestamps_spec.rb +9 -11
  169. data/spec/mongoid/touchable_spec.rb +277 -5
  170. data/spec/mongoid/touchable_spec_models.rb +3 -1
  171. data/spec/mongoid/traversable_spec.rb +9 -24
  172. data/spec/mongoid/validatable/associated_spec.rb +34 -27
  173. data/spec/mongoid/validatable/uniqueness_spec.rb +2 -3
  174. data/spec/mongoid_spec.rb +36 -10
  175. data/spec/shared/LICENSE +20 -0
  176. data/spec/shared/bin/get-mongodb-download-url +17 -0
  177. data/spec/shared/bin/s3-copy +45 -0
  178. data/spec/shared/bin/s3-upload +69 -0
  179. data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
  180. data/spec/shared/lib/mrss/cluster_config.rb +231 -0
  181. data/spec/shared/lib/mrss/constraints.rb +378 -0
  182. data/spec/shared/lib/mrss/docker_runner.rb +298 -0
  183. data/spec/shared/lib/mrss/eg_config_utils.rb +51 -0
  184. data/spec/shared/lib/mrss/event_subscriber.rb +210 -0
  185. data/spec/shared/lib/mrss/lite_constraints.rb +238 -0
  186. data/spec/shared/lib/mrss/server_version_registry.rb +113 -0
  187. data/spec/shared/lib/mrss/session_registry.rb +69 -0
  188. data/spec/shared/lib/mrss/session_registry_legacy.rb +60 -0
  189. data/spec/shared/lib/mrss/spec_organizer.rb +179 -0
  190. data/spec/shared/lib/mrss/utils.rb +37 -0
  191. data/spec/shared/share/Dockerfile.erb +321 -0
  192. data/spec/shared/share/haproxy-1.conf +16 -0
  193. data/spec/shared/share/haproxy-2.conf +17 -0
  194. data/spec/shared/shlib/config.sh +27 -0
  195. data/spec/shared/shlib/distro.sh +74 -0
  196. data/spec/shared/shlib/server.sh +416 -0
  197. data/spec/shared/shlib/set_env.sh +169 -0
  198. data/spec/spec_helper.rb +5 -0
  199. data/spec/support/immutable_ids.rb +118 -0
  200. data/spec/support/macros.rb +47 -15
  201. data/spec/support/models/artist.rb +0 -1
  202. data/spec/support/models/band.rb +1 -0
  203. data/spec/support/models/building.rb +2 -0
  204. data/spec/support/models/name.rb +0 -10
  205. data/spec/support/models/person.rb +0 -1
  206. data/spec/support/models/product.rb +1 -0
  207. data.tar.gz.sig +0 -0
  208. metadata +745 -637
  209. metadata.gz.sig +2 -0
  210. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -60
  211. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -60
  212. 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)
@@ -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