mongoid 5.4.1 → 6.0.0.beta

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 (264) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/config/locales/en.yml +23 -16
  5. data/lib/mongoid.rb +4 -9
  6. data/lib/mongoid/atomic.rb +1 -1
  7. data/lib/mongoid/atomic/modifiers.rb +8 -12
  8. data/lib/mongoid/attributes.rb +9 -11
  9. data/lib/mongoid/attributes/dynamic.rb +5 -6
  10. data/lib/mongoid/attributes/nested.rb +1 -1
  11. data/lib/mongoid/attributes/processing.rb +4 -0
  12. data/lib/mongoid/attributes/readonly.rb +22 -0
  13. data/lib/mongoid/cacheable.rb +36 -0
  14. data/lib/mongoid/changeable.rb +37 -1
  15. data/lib/mongoid/clients.rb +0 -63
  16. data/lib/mongoid/clients/factory.rb +0 -2
  17. data/lib/mongoid/clients/options.rb +54 -249
  18. data/lib/mongoid/clients/storage_options.rb +1 -69
  19. data/lib/mongoid/composable.rb +26 -2
  20. data/lib/mongoid/config.rb +1 -1
  21. data/lib/mongoid/config/options.rb +1 -1
  22. data/lib/mongoid/contextual/aggregable/mongo.rb +1 -0
  23. data/lib/mongoid/contextual/atomic.rb +6 -9
  24. data/lib/mongoid/contextual/geo_near.rb +2 -3
  25. data/lib/mongoid/contextual/map_reduce.rb +97 -24
  26. data/lib/mongoid/contextual/memory.rb +7 -4
  27. data/lib/mongoid/contextual/mongo.rb +63 -54
  28. data/lib/mongoid/contextual/none.rb +2 -2
  29. data/lib/mongoid/copyable.rb +19 -19
  30. data/lib/mongoid/criteria.rb +5 -4
  31. data/lib/mongoid/criteria/findable.rb +2 -3
  32. data/lib/mongoid/criteria/includable.rb +63 -16
  33. data/lib/mongoid/criteria/marshalable.rb +2 -2
  34. data/lib/mongoid/criteria/modifiable.rb +17 -1
  35. data/lib/mongoid/criteria/options.rb +25 -0
  36. data/lib/mongoid/criteria/queryable.rb +86 -0
  37. data/lib/mongoid/criteria/queryable/aggregable.rb +120 -0
  38. data/lib/mongoid/criteria/queryable/extensions.rb +28 -0
  39. data/lib/mongoid/criteria/queryable/extensions/array.rb +185 -0
  40. data/lib/mongoid/criteria/queryable/extensions/big_decimal.rb +37 -0
  41. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +34 -0
  42. data/lib/mongoid/criteria/queryable/extensions/date.rb +63 -0
  43. data/lib/mongoid/criteria/queryable/extensions/date_time.rb +53 -0
  44. data/lib/mongoid/criteria/queryable/extensions/hash.rb +200 -0
  45. data/lib/mongoid/criteria/queryable/extensions/nil_class.rb +86 -0
  46. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +90 -0
  47. data/lib/mongoid/criteria/queryable/extensions/object.rb +206 -0
  48. data/lib/mongoid/criteria/queryable/extensions/range.rb +70 -0
  49. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +45 -0
  50. data/lib/mongoid/criteria/queryable/extensions/set.rb +34 -0
  51. data/lib/mongoid/criteria/queryable/extensions/string.rb +137 -0
  52. data/lib/mongoid/criteria/queryable/extensions/symbol.rb +79 -0
  53. data/lib/mongoid/criteria/queryable/extensions/time.rb +60 -0
  54. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +54 -0
  55. data/lib/mongoid/criteria/queryable/forwardable.rb +65 -0
  56. data/lib/mongoid/criteria/queryable/key.rb +103 -0
  57. data/lib/mongoid/criteria/queryable/macroable.rb +27 -0
  58. data/lib/mongoid/criteria/queryable/mergeable.rb +271 -0
  59. data/lib/mongoid/criteria/queryable/optional.rb +411 -0
  60. data/lib/mongoid/criteria/queryable/options.rb +136 -0
  61. data/lib/mongoid/criteria/queryable/pipeline.rb +111 -0
  62. data/lib/mongoid/criteria/queryable/selectable.rb +662 -0
  63. data/lib/mongoid/criteria/queryable/selector.rb +196 -0
  64. data/lib/mongoid/criteria/queryable/smash.rb +103 -0
  65. data/lib/mongoid/document.rb +9 -23
  66. data/lib/mongoid/errors.rb +2 -1
  67. data/lib/mongoid/errors/ambiguous_relationship.rb +1 -1
  68. data/lib/mongoid/errors/delete_restriction.rb +2 -2
  69. data/lib/mongoid/errors/invalid_field.rb +2 -2
  70. data/lib/mongoid/errors/invalid_persistence_option.rb +29 -0
  71. data/lib/mongoid/errors/invalid_relation.rb +66 -0
  72. data/lib/mongoid/errors/inverse_not_found.rb +1 -1
  73. data/lib/mongoid/errors/mongoid_error.rb +1 -1
  74. data/lib/mongoid/evolvable.rb +1 -1
  75. data/lib/mongoid/extensions.rb +0 -5
  76. data/lib/mongoid/extensions/big_decimal.rb +17 -8
  77. data/lib/mongoid/extensions/date.rb +4 -1
  78. data/lib/mongoid/extensions/hash.rb +2 -3
  79. data/lib/mongoid/extensions/object.rb +2 -2
  80. data/lib/mongoid/extensions/string.rb +4 -3
  81. data/lib/mongoid/extensions/time.rb +5 -2
  82. data/lib/mongoid/factory.rb +1 -0
  83. data/lib/mongoid/fields/foreign_key.rb +2 -2
  84. data/lib/mongoid/fields/localized.rb +3 -8
  85. data/lib/mongoid/fields/validators/macro.rb +18 -0
  86. data/lib/mongoid/findable.rb +3 -3
  87. data/lib/mongoid/indexable.rb +17 -16
  88. data/lib/mongoid/indexable/specification.rb +1 -1
  89. data/lib/mongoid/indexable/validators/options.rb +1 -2
  90. data/lib/mongoid/interceptable.rb +6 -17
  91. data/lib/mongoid/loggable.rb +1 -1
  92. data/lib/mongoid/matchable.rb +3 -10
  93. data/lib/mongoid/matchable/gt.rb +2 -0
  94. data/lib/mongoid/matchable/gte.rb +2 -0
  95. data/lib/mongoid/matchable/lt.rb +2 -0
  96. data/lib/mongoid/matchable/lte.rb +2 -0
  97. data/lib/mongoid/persistable.rb +6 -5
  98. data/lib/mongoid/persistable/creatable.rb +2 -0
  99. data/lib/mongoid/persistable/deletable.rb +7 -3
  100. data/lib/mongoid/persistable/settable.rb +3 -16
  101. data/lib/mongoid/persistable/updatable.rb +10 -12
  102. data/lib/mongoid/persistence_context.rb +216 -0
  103. data/lib/mongoid/query_cache.rb +5 -30
  104. data/lib/mongoid/relations/accessors.rb +6 -2
  105. data/lib/mongoid/relations/auto_save.rb +12 -4
  106. data/lib/mongoid/relations/bindings/embedded/in.rb +4 -0
  107. data/lib/mongoid/relations/bindings/embedded/many.rb +8 -1
  108. data/lib/mongoid/relations/bindings/embedded/one.rb +10 -0
  109. data/lib/mongoid/relations/bindings/referenced/many.rb +4 -0
  110. data/lib/mongoid/relations/builders.rb +2 -2
  111. data/lib/mongoid/relations/builders/embedded/one.rb +1 -1
  112. data/lib/mongoid/relations/builders/nested_attributes/many.rb +1 -1
  113. data/lib/mongoid/relations/conversions.rb +1 -1
  114. data/lib/mongoid/relations/counter_cache.rb +28 -18
  115. data/lib/mongoid/relations/eager.rb +19 -7
  116. data/lib/mongoid/relations/eager/base.rb +5 -5
  117. data/lib/mongoid/relations/embedded/batchable.rb +9 -33
  118. data/lib/mongoid/relations/embedded/in.rb +16 -2
  119. data/lib/mongoid/relations/embedded/many.rb +23 -8
  120. data/lib/mongoid/relations/embedded/one.rb +17 -2
  121. data/lib/mongoid/relations/macros.rb +9 -2
  122. data/lib/mongoid/relations/metadata.rb +3 -3
  123. data/lib/mongoid/relations/nested_builder.rb +1 -1
  124. data/lib/mongoid/relations/options.rb +2 -2
  125. data/lib/mongoid/relations/proxy.rb +2 -33
  126. data/lib/mongoid/relations/referenced/in.rb +23 -11
  127. data/lib/mongoid/relations/referenced/many.rb +24 -16
  128. data/lib/mongoid/relations/referenced/many_to_many.rb +20 -13
  129. data/lib/mongoid/relations/referenced/one.rb +17 -1
  130. data/lib/mongoid/relations/reflections.rb +3 -5
  131. data/lib/mongoid/relations/touchable.rb +1 -1
  132. data/lib/mongoid/reloadable.rb +1 -1
  133. data/lib/mongoid/scopable.rb +3 -3
  134. data/lib/mongoid/serializable.rb +2 -3
  135. data/lib/mongoid/tasks/database.rb +1 -2
  136. data/lib/mongoid/threaded.rb +4 -4
  137. data/lib/mongoid/traversable.rb +1 -1
  138. data/lib/mongoid/validatable.rb +1 -1
  139. data/lib/mongoid/validatable/macros.rb +2 -4
  140. data/lib/mongoid/validatable/uniqueness.rb +1 -2
  141. data/lib/mongoid/version.rb +1 -1
  142. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +4 -7
  143. data/spec/app/models/album.rb +5 -1
  144. data/spec/app/models/artist.rb +21 -0
  145. data/spec/app/models/band.rb +0 -1
  146. data/spec/app/models/church.rb +0 -2
  147. data/spec/app/models/ordered_post.rb +5 -0
  148. data/spec/app/models/oscar.rb +1 -2
  149. data/spec/app/models/person.rb +3 -1
  150. data/spec/app/models/post.rb +0 -1
  151. data/spec/app/models/princess.rb +2 -0
  152. data/spec/app/models/record.rb +1 -0
  153. data/spec/app/models/thing.rb +1 -1
  154. data/spec/config/mongoid.yml +1 -5
  155. data/spec/mongoid/atomic/modifiers_spec.rb +17 -17
  156. data/spec/mongoid/atomic_spec.rb +17 -17
  157. data/spec/mongoid/attributes/nested_spec.rb +14 -14
  158. data/spec/mongoid/attributes/readonly_spec.rb +87 -44
  159. data/spec/mongoid/attributes_spec.rb +63 -0
  160. data/spec/mongoid/cacheable_spec.rb +112 -0
  161. data/spec/mongoid/changeable_spec.rb +58 -0
  162. data/spec/mongoid/clients/factory_spec.rb +3 -11
  163. data/spec/mongoid/clients/options_spec.rb +378 -96
  164. data/spec/mongoid/clients_spec.rb +207 -170
  165. data/spec/mongoid/composable_spec.rb +7 -0
  166. data/spec/mongoid/config_spec.rb +41 -21
  167. data/spec/mongoid/contextual/atomic_spec.rb +77 -343
  168. data/spec/mongoid/contextual/map_reduce_spec.rb +119 -111
  169. data/spec/mongoid/contextual/memory_spec.rb +56 -316
  170. data/spec/mongoid/contextual/mongo_spec.rb +104 -378
  171. data/spec/mongoid/copyable_spec.rb +8 -15
  172. data/spec/mongoid/criteria/modifiable_spec.rb +239 -7
  173. data/spec/mongoid/criteria/options_spec.rb +29 -0
  174. data/spec/mongoid/criteria/queryable/aggregable_spec.rb +370 -0
  175. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +523 -0
  176. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +59 -0
  177. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +58 -0
  178. data/spec/mongoid/criteria/queryable/extensions/boolean_spec.rb +213 -0
  179. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +330 -0
  180. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +405 -0
  181. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +58 -0
  182. data/spec/mongoid/criteria/queryable/extensions/float_spec.rb +65 -0
  183. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +327 -0
  184. data/spec/mongoid/criteria/queryable/extensions/integer_spec.rb +65 -0
  185. data/spec/mongoid/criteria/queryable/extensions/nil_class_spec.rb +77 -0
  186. data/spec/mongoid/criteria/queryable/extensions/object_spec.rb +108 -0
  187. data/spec/mongoid/criteria/queryable/extensions/range_spec.rb +309 -0
  188. data/spec/mongoid/{extensions/origin/regexp_raw_spec.rb → criteria/queryable/extensions/regexp_spec.rb} +21 -20
  189. data/spec/mongoid/criteria/queryable/extensions/set_spec.rb +39 -0
  190. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +302 -0
  191. data/spec/mongoid/criteria/queryable/extensions/symbol_spec.rb +167 -0
  192. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +376 -0
  193. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +347 -0
  194. data/spec/mongoid/criteria/queryable/forwardable_spec.rb +87 -0
  195. data/spec/mongoid/criteria/queryable/key_spec.rb +52 -0
  196. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +49 -0
  197. data/spec/mongoid/criteria/queryable/optional_spec.rb +1786 -0
  198. data/spec/mongoid/criteria/queryable/options_spec.rb +360 -0
  199. data/spec/mongoid/criteria/queryable/pipeline_spec.rb +200 -0
  200. data/spec/mongoid/criteria/queryable/queryable_spec.rb +137 -0
  201. data/spec/mongoid/criteria/queryable/selectable_spec.rb +4159 -0
  202. data/spec/mongoid/criteria/queryable/selector_spec.rb +778 -0
  203. data/spec/mongoid/criteria/queryable/smash_spec.rb +30 -0
  204. data/spec/mongoid/criteria_spec.rb +45 -63
  205. data/spec/mongoid/document_spec.rb +21 -88
  206. data/spec/mongoid/errors/invalid_relation_spec.rb +37 -0
  207. data/spec/mongoid/errors/mongoid_error_spec.rb +6 -3
  208. data/spec/mongoid/extensions/big_decimal_spec.rb +320 -18
  209. data/spec/mongoid/extensions/date_spec.rb +2 -6
  210. data/spec/mongoid/extensions/date_time_spec.rb +2 -6
  211. data/spec/mongoid/extensions/float_spec.rb +8 -1
  212. data/spec/mongoid/extensions/integer_spec.rb +8 -1
  213. data/spec/mongoid/extensions/object_spec.rb +11 -0
  214. data/spec/mongoid/extensions/string_spec.rb +21 -0
  215. data/spec/mongoid/extensions/time_spec.rb +4 -8
  216. data/spec/mongoid/extensions/time_with_zone_spec.rb +2 -6
  217. data/spec/mongoid/fields/localized_spec.rb +0 -91
  218. data/spec/mongoid/findable_spec.rb +46 -1
  219. data/spec/mongoid/indexable_spec.rb +6 -46
  220. data/spec/mongoid/interceptable_spec.rb +49 -10
  221. data/spec/mongoid/matchable/gt_spec.rb +11 -0
  222. data/spec/mongoid/matchable/gte_spec.rb +10 -0
  223. data/spec/mongoid/matchable/lt_spec.rb +11 -0
  224. data/spec/mongoid/matchable/lte_spec.rb +11 -0
  225. data/spec/mongoid/matchable_spec.rb +1 -51
  226. data/spec/mongoid/persistable/creatable_spec.rb +2 -2
  227. data/spec/mongoid/persistable/deletable_spec.rb +1 -1
  228. data/spec/mongoid/persistable/destroyable_spec.rb +6 -2
  229. data/spec/mongoid/persistable/savable_spec.rb +30 -30
  230. data/spec/mongoid/persistable/settable_spec.rb +0 -185
  231. data/spec/mongoid/persistable/updatable_spec.rb +166 -5
  232. data/spec/mongoid/persistence_context_spec.rb +654 -0
  233. data/spec/mongoid/positional_spec.rb +10 -10
  234. data/spec/mongoid/query_cache_spec.rb +89 -65
  235. data/spec/mongoid/relations/accessors_spec.rb +1 -1
  236. data/spec/mongoid/relations/auto_save_spec.rb +39 -6
  237. data/spec/mongoid/relations/builders_spec.rb +37 -10
  238. data/spec/mongoid/relations/counter_cache_spec.rb +64 -15
  239. data/spec/mongoid/relations/cyclic_spec.rb +0 -22
  240. data/spec/mongoid/relations/embedded/many_spec.rb +9 -41
  241. data/spec/mongoid/relations/embedded/one_spec.rb +2 -1
  242. data/spec/mongoid/relations/macros_spec.rb +395 -7
  243. data/spec/mongoid/relations/proxy_spec.rb +3 -1
  244. data/spec/mongoid/relations/referenced/in_spec.rb +41 -1
  245. data/spec/mongoid/relations/referenced/many_spec.rb +6 -29
  246. data/spec/mongoid/relations/referenced/many_to_many_spec.rb +6 -29
  247. data/spec/mongoid/relations/reflections_spec.rb +9 -9
  248. data/spec/mongoid/reloadable_spec.rb +51 -0
  249. data/spec/mongoid/scopable_spec.rb +0 -12
  250. data/spec/mongoid/serializable_spec.rb +0 -50
  251. data/spec/mongoid/validatable/presence_spec.rb +1 -1
  252. data/spec/mongoid/validatable/uniqueness_spec.rb +16 -9
  253. data/spec/mongoid/validatable_spec.rb +16 -0
  254. data/spec/spec_helper.rb +10 -10
  255. metadata +536 -479
  256. metadata.gz.sig +0 -0
  257. data/lib/mongoid/clients/thread_options.rb +0 -19
  258. data/lib/mongoid/errors/in_memory_collation_not_supported.rb +0 -20
  259. data/lib/mongoid/extensions/decimal128.rb +0 -39
  260. data/lib/mongoid/extensions/origin/regexp_raw.rb +0 -43
  261. data/lib/mongoid/matchable/regexp.rb +0 -27
  262. data/spec/app/models/post_genre.rb +0 -6
  263. data/spec/mongoid/extensions/decimal128_spec.rb +0 -44
  264. data/spec/mongoid/matchable/regexp_spec.rb +0 -59
@@ -168,7 +168,7 @@ describe Mongoid::Persistable::Updatable do
168
168
  it "raises an error" do
169
169
  expect {
170
170
  post.update_attribute(:title, "something")
171
- }.to raise_error
171
+ }.to raise_error(RuntimeError)
172
172
  end
173
173
  end
174
174
  end
@@ -269,6 +269,167 @@ describe Mongoid::Persistable::Updatable do
269
269
  expect(from_db.reload.name).to eq("home")
270
270
  end
271
271
  end
272
+
273
+ context 'when the field is read-only' do
274
+
275
+ before do
276
+ Person.attr_readonly :species
277
+ end
278
+
279
+ after do
280
+ Person.readonly_attributes.reject! { |a| a.to_s == 'species' }
281
+ end
282
+
283
+ let(:person) do
284
+ Person.create(species: :human)
285
+ end
286
+
287
+ it 'raises an error when trying to set the attribute' do
288
+ expect {
289
+ person.update_attribute(:species, :reptile)
290
+ }.to raise_exception(Mongoid::Errors::ReadonlyAttribute)
291
+ end
292
+
293
+ context 'when referring to the attribute with a string' do
294
+
295
+ it 'raises an error when trying to set the attribute' do
296
+ expect {
297
+ person.update_attribute('species', :reptile)
298
+ }.to raise_exception(Mongoid::Errors::ReadonlyAttribute)
299
+ end
300
+ end
301
+
302
+ context 'when the field is aliased' do
303
+
304
+ before do
305
+ Person.attr_readonly :at
306
+ end
307
+
308
+ after do
309
+ Person.readonly_attributes.reject! { |a| a.to_s == 'at' }
310
+ end
311
+
312
+ it 'raises an error when trying to set the attribute using the db name' do
313
+ expect {
314
+ person.update_attribute(:at, Time.now)
315
+ }.to raise_exception(Mongoid::Errors::ReadonlyAttribute)
316
+ end
317
+
318
+ it 'raises an error when trying to set the attribute using the aliased name' do
319
+ expect {
320
+ person.update_attribute(:aliased_timestamp, Time.now)
321
+ }.to raise_exception(Mongoid::Errors::ReadonlyAttribute)
322
+ end
323
+ end
324
+ end
325
+
326
+ context 'when the field is loaded explicitly' do
327
+
328
+ before do
329
+ Person.create(title: 'Captain')
330
+ end
331
+
332
+ context 'when the loaded attribute is updated' do
333
+
334
+ let(:person) do
335
+ Person.only(:title).first.tap do |_person|
336
+ _person.update_attribute(:title, 'Esteemed')
337
+ end
338
+ end
339
+
340
+ it 'allows the field to be updated' do
341
+ expect(person.title).to eq('Esteemed')
342
+ end
343
+
344
+ it 'persists the updated field' do
345
+ expect(person.reload.title).to eq('Esteemed')
346
+ end
347
+ end
348
+
349
+ context 'when the an attribute other than the loaded one is updated' do
350
+
351
+ let(:person) do
352
+ Person.only(:title).first
353
+ end
354
+
355
+ it 'does not allow the field to be updated' do
356
+ expect {
357
+ person.update_attribute(:age, 20)
358
+ }.to raise_exception(Mongoid::Errors::ReadonlyAttribute)
359
+ end
360
+
361
+ it 'does not persist the change' do
362
+ expect(person.reload.age).to eq(100)
363
+ end
364
+
365
+ context 'when referring to the attribute with a string' do
366
+
367
+ it 'does not allow the field to be updated' do
368
+ expect {
369
+ person.update_attribute('age', 20)
370
+ }.to raise_exception(Mongoid::Errors::ReadonlyAttribute)
371
+ end
372
+
373
+ it 'does not persist the change' do
374
+ expect(person.reload.age).to eq(100)
375
+ end
376
+ end
377
+ end
378
+ end
379
+
380
+ context 'when fields are explicitly not loaded' do
381
+
382
+ before do
383
+ Person.create(title: 'Captain')
384
+ end
385
+
386
+ context 'when the loaded attribute is updated' do
387
+
388
+ let(:person) do
389
+ Person.without(:age).first.tap do |_person|
390
+ _person.update_attribute(:title, 'Esteemed')
391
+ end
392
+ end
393
+
394
+ it 'allows the field to be updated' do
395
+ expect(person.title).to eq('Esteemed')
396
+ end
397
+
398
+ it 'persists the updated field' do
399
+ expect(person.reload.title).to eq('Esteemed')
400
+ end
401
+ end
402
+
403
+ context 'when the non-loaded attribute is updated' do
404
+
405
+ let(:person) do
406
+ Person.without(:title).first
407
+ end
408
+
409
+ it 'does not allow the field to be updated' do
410
+ expect {
411
+ person.update_attribute(:title, 'Esteemed')
412
+ }.to raise_exception(ActiveModel::MissingAttributeError)
413
+ end
414
+
415
+ it 'does not persist the change' do
416
+ expect(person.reload.title).to eq('Captain')
417
+ end
418
+
419
+ context 'when referring to the attribute with a string' do
420
+
421
+ it 'does not allow the field to be updated' do
422
+ expect {
423
+ person.update_attribute('title', 'Esteemed')
424
+ }.to raise_exception(ActiveModel::MissingAttributeError)
425
+ end
426
+
427
+ it 'does not persist the change' do
428
+ expect(person.reload.title).to eq('Captain')
429
+ end
430
+ end
431
+ end
432
+ end
272
433
  end
273
434
 
274
435
  [:update_attributes, :update].each do |method|
@@ -283,7 +444,7 @@ describe Mongoid::Persistable::Updatable do
283
444
 
284
445
  it "raises an error" do
285
446
  expect {
286
- person.update_attributes(map: { "$bad.key" => "value" })
447
+ person.update_attributes(map: { "bad.key" => "value" })
287
448
  }.to raise_error(Mongo::Error::OperationFailure)
288
449
  end
289
450
  end
@@ -319,7 +480,7 @@ describe Mongoid::Persistable::Updatable do
319
480
 
320
481
  it "raises an error" do
321
482
  expect {
322
- person.send(method, map: { "$bad.key" => "value" })
483
+ person.send(method, map: { "bad.key" => "value" })
323
484
  }.to raise_error(Mongo::Error::OperationFailure)
324
485
  end
325
486
  end
@@ -360,7 +521,7 @@ describe Mongoid::Persistable::Updatable do
360
521
  it "raises an error" do
361
522
  expect {
362
523
  person.send(method, title: "something")
363
- }.to raise_error
524
+ }.to raise_error(RuntimeError)
364
525
  end
365
526
  end
366
527
 
@@ -543,7 +704,7 @@ describe Mongoid::Persistable::Updatable do
543
704
 
544
705
  describe "#update!" do
545
706
 
546
- context "when a callback returns false" do
707
+ context "when a callback aborts the callback chain" do
547
708
 
548
709
  let(:oscar) do
549
710
  Oscar.new
@@ -0,0 +1,654 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::PersistenceContext do
4
+
5
+ let(:persistence_context) do
6
+ described_class.new(object, options)
7
+ end
8
+
9
+ let(:object) do
10
+ Band
11
+ end
12
+
13
+ describe '.set' do
14
+
15
+ let(:options) do
16
+ { collection: :other }
17
+ end
18
+
19
+ context 'when the persistence context is set on the thread' do
20
+
21
+ let!(:persistence_context) do
22
+ described_class.set(object, options)
23
+ end
24
+
25
+ it 'sets the persistence context for the object on the current thread' do
26
+ expect(described_class.get(object)).to be(persistence_context)
27
+ expect(described_class.get(object)).not_to be(nil)
28
+ expect(described_class.get(object).collection.name).to eq('other')
29
+ end
30
+
31
+ it 'only sets persistence context for the object on the current thread' do
32
+ Thread.new do
33
+ expect(described_class.get(object)).not_to be(persistence_context)
34
+ expect(described_class.get(object)).to be(nil)
35
+ end.value
36
+ end
37
+ end
38
+ end
39
+
40
+
41
+ describe '.get' do
42
+
43
+ let(:options) do
44
+ { collection: :other }
45
+ end
46
+
47
+ context 'when there has been a persistence context set on the current thread' do
48
+
49
+ let!(:persistence_context) do
50
+ described_class.set(object, options)
51
+ end
52
+
53
+ it 'gets the persistence context for the object on the current thread' do
54
+ expect(described_class.get(object)).to be(persistence_context)
55
+ expect(described_class.get(object).collection.name).to eq('other')
56
+ end
57
+
58
+ it 'does not get persistence context for the object from another thread' do
59
+ Thread.new do
60
+ expect(described_class.get(object)).not_to be(persistence_context)
61
+ expect(described_class.get(object)).to be(nil)
62
+ end.value
63
+ end
64
+ end
65
+ end
66
+
67
+ describe '.clear' do
68
+
69
+ let(:options) do
70
+ { collection: :other }
71
+ end
72
+
73
+ context 'when there has been a persistence context set on the current thread' do
74
+
75
+ let!(:persistence_context) do
76
+ described_class.set(object, options)
77
+ end
78
+
79
+ context 'when no cluster is passed to the method' do
80
+
81
+ before do
82
+ described_class.clear(object)
83
+ end
84
+
85
+ it 'clears the persistence context for the object on the current thread' do
86
+ expect(described_class.get(object)).to be(nil)
87
+ end
88
+ end
89
+
90
+ context 'when a cluster is passed to the method' do
91
+
92
+ context 'when the cluster is the same as that of the persistence context on the current thread' do
93
+
94
+ let(:client) do
95
+ persistence_context.client
96
+ end
97
+
98
+ before do
99
+ described_class.clear(object, client.cluster)
100
+ end
101
+
102
+ it 'does not close the cluster' do
103
+ expect(client).not_to receive(:close)
104
+ described_class.clear(object, client.cluster.dup)
105
+ end
106
+ end
107
+
108
+ context 'when the cluster is not the same as that of the persistence context on the current thread' do
109
+
110
+ let!(:client) do
111
+ persistence_context.client
112
+ end
113
+
114
+ it 'closes the client' do
115
+ expect(client).to receive(:close).and_call_original
116
+ described_class.clear(object, client.cluster.dup)
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ describe '#initialize' do
124
+
125
+ let(:options) do
126
+ { collection: 'other' }
127
+ end
128
+
129
+ context 'when an object is passed' do
130
+
131
+ context 'when the object is a klass' do
132
+
133
+ it 'sets the object on the persistence context' do
134
+ expect(persistence_context.instance_variable_get(:@object)).to eq(object)
135
+ end
136
+ end
137
+
138
+ context 'when the object is a model instance' do
139
+
140
+ let(:object) do
141
+ Band.new
142
+ end
143
+
144
+ it 'sets the object on the persistence context' do
145
+ expect(persistence_context.instance_variable_get(:@object)).to eq(object)
146
+ end
147
+ end
148
+ end
149
+
150
+ context 'when options are passed' do
151
+
152
+ let(:options) do
153
+ { connect_timeout: 3 }
154
+ end
155
+
156
+ context 'when the options are valid client options' do
157
+
158
+ it 'sets the options on the persistence context object' do
159
+ expect(persistence_context.options).to eq(options)
160
+ end
161
+ end
162
+
163
+ context 'when the options are not valid client options' do
164
+
165
+ context 'when the options are valid extra options' do
166
+
167
+ let(:options) do
168
+ { collection: 'other' }
169
+ end
170
+
171
+ it 'sets the options on the persistence context object' do
172
+ expect(persistence_context.collection_name).to eq(options[:collection].to_sym)
173
+ end
174
+ end
175
+
176
+ context 'when the options are not valid extra options' do
177
+
178
+ let(:options) do
179
+ { invalid: 'option' }
180
+ end
181
+
182
+ it 'raises an InvalidPersistenceOption error' do
183
+ expect {
184
+ persistence_context
185
+ }.to raise_error(Mongoid::Errors::InvalidPersistenceOption)
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
191
+
192
+ describe '#collection' do
193
+
194
+ let(:persistence_context) do
195
+ described_class.new(object, options)
196
+ end
197
+
198
+ let(:options) do
199
+ { read: { 'mode' => :secondary } }
200
+ end
201
+
202
+ context 'when a parent object is passed' do
203
+
204
+ it 'uses the collection of the parent object' do
205
+ expect(persistence_context.collection(Person.new).name).to eq('people')
206
+ end
207
+
208
+ it 'does not memoize the collection' do
209
+ persistence_context.collection
210
+ expect(persistence_context.collection(Person.new).name).to eq('people')
211
+ end
212
+
213
+ it 'keeps the other options of the persistence context' do
214
+ expect(persistence_context.collection(Person.new).client.options[:read]).to eq(options[:read])
215
+ end
216
+ end
217
+
218
+ context 'when a parent object is not passed' do
219
+
220
+ it 'uses the collection of the object' do
221
+ expect(persistence_context.collection.name).to eq('bands')
222
+ end
223
+
224
+ it 'does not memoize the collection' do
225
+ persistence_context.collection(Person.new)
226
+ expect(persistence_context.collection.name).to eq('bands')
227
+ end
228
+
229
+ it 'keeps the other options of the persistence context' do
230
+ expect(persistence_context.collection.client.options[:read]).to eq(options[:read])
231
+ end
232
+ end
233
+ end
234
+
235
+ describe '#collection_name' do
236
+
237
+ let(:persistence_context) do
238
+ described_class.new(object, options)
239
+ end
240
+
241
+ let(:options) do
242
+ { collection: 'other' }
243
+ end
244
+
245
+ context 'when storage options are set on the object' do
246
+
247
+ context 'when there are no options passed to the Persistence Context' do
248
+
249
+ let(:options) do
250
+ { }
251
+ end
252
+
253
+ after do
254
+ object.reset_storage_options!
255
+ end
256
+
257
+ context 'when the storage options is static' do
258
+
259
+ before do
260
+ object.store_in collection: :schmands
261
+ end
262
+
263
+ it 'uses the storage options' do
264
+ expect(persistence_context.collection_name).to eq(:schmands)
265
+ end
266
+ end
267
+
268
+ context 'when the storage options is a block' do
269
+
270
+ before do
271
+ object.store_in collection: ->{ :schmands }
272
+ end
273
+
274
+ it 'uses the storage options' do
275
+ expect(persistence_context.collection_name).to eq(:schmands)
276
+ end
277
+ end
278
+ end
279
+
280
+ context 'when there are options passed to the Persistence Context' do
281
+
282
+ let(:options) do
283
+ { collection: 'other' }
284
+ end
285
+
286
+ after do
287
+ object.reset_storage_options!
288
+ end
289
+
290
+ context 'when the storage options is static' do
291
+
292
+ before do
293
+ object.store_in collection: :schmands
294
+ end
295
+
296
+ it 'uses the persistence context options' do
297
+ expect(persistence_context.collection_name).to eq(:other)
298
+ end
299
+ end
300
+
301
+ context 'when the storage options is a block' do
302
+
303
+ before do
304
+ object.store_in collection: ->{ :schmands }
305
+ end
306
+
307
+ it 'uses the persistence context options' do
308
+ expect(persistence_context.collection_name).to eq(:other)
309
+ end
310
+ end
311
+ end
312
+ end
313
+
314
+ context 'when storage options are not set on the object' do
315
+
316
+ context 'when there are options passed to the Persistence Context' do
317
+
318
+ let(:options) do
319
+ { collection: 'other' }
320
+ end
321
+
322
+ it 'uses the persistence context options' do
323
+ expect(persistence_context.collection_name).to eq(:other)
324
+ end
325
+ end
326
+ end
327
+ end
328
+
329
+ describe '#database_name' do
330
+
331
+ let(:persistence_context) do
332
+ described_class.new(object, options)
333
+ end
334
+
335
+ let(:options) do
336
+ { database: 'other' }
337
+ end
338
+
339
+ context 'when storage options are set on the object' do
340
+
341
+ context 'when there are no options passed to the Persistence Context' do
342
+
343
+ let(:options) do
344
+ { }
345
+ end
346
+
347
+ after do
348
+ object.reset_storage_options!
349
+ end
350
+
351
+ context 'when there is a database override' do
352
+
353
+ before do
354
+ object.store_in database: :musique
355
+ end
356
+
357
+ before do
358
+ Mongoid::Threaded.database_override = :other
359
+ end
360
+
361
+ after do
362
+ Mongoid::Threaded.database_override = nil
363
+ end
364
+
365
+ it 'uses the override' do
366
+ expect(persistence_context.database_name).to eq(:other)
367
+ end
368
+ end
369
+
370
+ context 'when the storage options is static' do
371
+
372
+ before do
373
+ object.store_in database: :musique
374
+ end
375
+
376
+ it 'uses the storage options' do
377
+ expect(persistence_context.database_name).to eq(:musique)
378
+ end
379
+ end
380
+
381
+ context 'when the storage options is a block' do
382
+
383
+ before do
384
+ object.store_in database: ->{ :musique }
385
+ end
386
+ it 'uses the storage options' do
387
+ expect(persistence_context.database_name).to eq(:musique)
388
+ end
389
+ end
390
+ end
391
+
392
+ context 'when there are options passed to the Persistence Context' do
393
+
394
+ let(:options) do
395
+ { database: 'musique' }
396
+ end
397
+
398
+ context 'when there is a database override' do
399
+
400
+ before do
401
+ Mongoid::Threaded.database_override = :other
402
+ end
403
+
404
+ after do
405
+ Mongoid::Threaded.database_override = nil
406
+ end
407
+
408
+ it 'uses the persistence context options' do
409
+ expect(persistence_context.database_name).to eq(:musique)
410
+ end
411
+ end
412
+
413
+ context 'when the storage options is static' do
414
+
415
+ before do
416
+ object.store_in database: :sounds
417
+ end
418
+
419
+ after do
420
+ object.reset_storage_options!
421
+ end
422
+
423
+ it 'uses the persistence context options' do
424
+ expect(persistence_context.database_name).to eq(:musique)
425
+ end
426
+ end
427
+
428
+ context 'when the storage options is a block' do
429
+
430
+ before do
431
+ object.store_in database: ->{ :sounds }
432
+ end
433
+
434
+ after do
435
+ object.reset_storage_options!
436
+ end
437
+
438
+ it 'uses the persistence context options' do
439
+ expect(persistence_context.database_name).to eq(:musique)
440
+ end
441
+ end
442
+ end
443
+ end
444
+
445
+ context 'when storage options are not set on the object' do
446
+
447
+ context 'when there are options passed to the Persistence Context' do
448
+
449
+ let(:options) do
450
+ { database: 'musique' }
451
+ end
452
+
453
+ it 'uses the persistence context options' do
454
+ expect(persistence_context.database_name).to eq(:musique)
455
+ end
456
+
457
+ context 'when there is a database override' do
458
+
459
+ before do
460
+ Mongoid::Threaded.database_override = :other
461
+ end
462
+
463
+ after do
464
+ Mongoid::Threaded.database_override = nil
465
+ end
466
+
467
+ it 'uses the persistence context options' do
468
+ expect(persistence_context.database_name).to eq(:musique)
469
+ end
470
+ end
471
+ end
472
+
473
+ context 'when there are no options passed to the Persistence Context' do
474
+
475
+ context 'when there is a database override' do
476
+
477
+ before do
478
+ Mongoid::Threaded.database_override = :other
479
+ end
480
+
481
+ after do
482
+ Mongoid::Threaded.database_override = nil
483
+ end
484
+
485
+ it 'uses the database override options' do
486
+ expect(persistence_context.database_name).to eq(Mongoid::Threaded.database_override)
487
+ end
488
+ end
489
+ end
490
+ end
491
+ end
492
+
493
+ describe '#client' do
494
+
495
+ let(:persistence_context) do
496
+ described_class.new(object, options)
497
+ end
498
+
499
+ let(:options) do
500
+ { }
501
+ end
502
+
503
+ before do
504
+ Mongoid.clients[:alternative] = { database: :mongoid_test, hosts: [ "#{HOST}:#{PORT}" ] }
505
+ end
506
+
507
+ after do
508
+ Mongoid.clients.delete(:alternative)
509
+ end
510
+
511
+ context 'when the client is set in the options' do
512
+
513
+ let(:options) do
514
+ { client: :alternative }
515
+ end
516
+
517
+ it 'uses the client option' do
518
+ expect(persistence_context.client).to eq(Mongoid::Clients.with_name(:alternative))
519
+ end
520
+
521
+ context 'when there is a client override' do
522
+
523
+ before do
524
+ Mongoid::Threaded.client_override = :other
525
+ end
526
+
527
+ after do
528
+ Mongoid::Threaded.client_override = nil
529
+ end
530
+
531
+ it 'uses the client option' do
532
+ expect(persistence_context.client).to eq(Mongoid::Clients.with_name(:alternative))
533
+ end
534
+ end
535
+
536
+ context 'when there are storage options set' do
537
+
538
+ after do
539
+ object.reset_storage_options!
540
+ end
541
+
542
+ context 'when the storage options is static' do
543
+
544
+ before do
545
+ object.store_in client: :other
546
+ end
547
+
548
+ it 'uses the persistence context options' do
549
+ expect(persistence_context.client).to eq(Mongoid::Clients.with_name(:alternative))
550
+ end
551
+ end
552
+
553
+ context 'when the storage options is a block' do
554
+
555
+ before do
556
+ object.store_in client: ->{ :other }
557
+ end
558
+
559
+ it 'uses the persistence context options' do
560
+ expect(persistence_context.client).to eq(Mongoid::Clients.with_name(:alternative))
561
+ end
562
+ end
563
+ end
564
+ end
565
+
566
+ context 'when there is no client option set' do
567
+
568
+ let(:options) do
569
+ { }
570
+ end
571
+
572
+ context 'when there is a client override' do
573
+
574
+ before do
575
+ Mongoid::Threaded.client_override = :alternative
576
+ end
577
+
578
+ after do
579
+ Mongoid::Threaded.client_override = nil
580
+ end
581
+
582
+ it 'uses the client override' do
583
+ expect(persistence_context.client).to eq(Mongoid::Clients.with_name(:alternative))
584
+ end
585
+ end
586
+
587
+ context 'when there are storage options set' do
588
+
589
+ after do
590
+ object.reset_storage_options!
591
+ end
592
+
593
+ context 'when the storage options is static' do
594
+
595
+ before do
596
+ object.store_in client: :alternative
597
+ end
598
+
599
+ it 'uses the client storage option' do
600
+ expect(persistence_context.client).to eq(Mongoid::Clients.with_name(:alternative))
601
+ end
602
+ end
603
+
604
+ context 'when the storage options is a block' do
605
+
606
+ before do
607
+ object.store_in client: ->{ :alternative }
608
+ end
609
+
610
+ it 'uses the client storage option' do
611
+ expect(persistence_context.client).to eq(Mongoid::Clients.with_name(:alternative))
612
+ end
613
+ end
614
+
615
+ context 'when there is a client override' do
616
+
617
+ before do
618
+ Mongoid::Threaded.client_override = :alternative
619
+ end
620
+
621
+ after do
622
+ Mongoid::Threaded.client_override = nil
623
+ end
624
+
625
+ it 'uses the client override' do
626
+ expect(persistence_context.client).to eq(Mongoid::Clients.with_name(:alternative))
627
+ end
628
+ end
629
+ end
630
+ end
631
+
632
+ context 'when there are client options set' do
633
+
634
+ let(:options) do
635
+ { connect_timeout: 3 }
636
+ end
637
+
638
+ it 'applies the options to the client' do
639
+ expect(persistence_context.client.options[:connect_timeout]).to eq(options[:connect_timeout])
640
+ end
641
+ end
642
+
643
+ context 'when there is a database name set in the options' do
644
+
645
+ let(:options) do
646
+ { database: 'other' }
647
+ end
648
+
649
+ it 'uses the database from the options' do
650
+ expect(persistence_context.client.database.name).to eq(options[:database])
651
+ end
652
+ end
653
+ end
654
+ end