mongoid 5.4.1 → 6.1.1

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 (260) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +3 -3
  3. data/lib/config/locales/en.yml +19 -0
  4. data/lib/mongoid.rb +4 -4
  5. data/lib/mongoid/atomic.rb +2 -2
  6. data/lib/mongoid/atomic/modifiers.rb +8 -12
  7. data/lib/mongoid/attributes.rb +22 -21
  8. data/lib/mongoid/attributes/readonly.rb +22 -0
  9. data/lib/mongoid/cacheable.rb +36 -0
  10. data/lib/mongoid/changeable.rb +36 -0
  11. data/lib/mongoid/clients.rb +8 -63
  12. data/lib/mongoid/clients/options.rb +55 -250
  13. data/lib/mongoid/clients/storage_options.rb +1 -69
  14. data/lib/mongoid/composable.rb +29 -3
  15. data/lib/mongoid/config.rb +1 -0
  16. data/lib/mongoid/contextual/atomic.rb +5 -8
  17. data/lib/mongoid/contextual/map_reduce.rb +0 -4
  18. data/lib/mongoid/contextual/memory.rb +2 -2
  19. data/lib/mongoid/contextual/mongo.rb +40 -22
  20. data/lib/mongoid/contextual/none.rb +12 -0
  21. data/lib/mongoid/copyable.rb +13 -6
  22. data/lib/mongoid/criteria.rb +5 -2
  23. data/lib/mongoid/criteria/marshalable.rb +2 -2
  24. data/lib/mongoid/criteria/modifiable.rb +17 -1
  25. data/lib/mongoid/criteria/options.rb +25 -0
  26. data/lib/mongoid/criteria/queryable.rb +87 -0
  27. data/lib/mongoid/criteria/queryable/aggregable.rb +120 -0
  28. data/lib/mongoid/criteria/queryable/extensions.rb +28 -0
  29. data/lib/mongoid/criteria/queryable/extensions/array.rb +185 -0
  30. data/lib/mongoid/criteria/queryable/extensions/big_decimal.rb +37 -0
  31. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +34 -0
  32. data/lib/mongoid/criteria/queryable/extensions/date.rb +63 -0
  33. data/lib/mongoid/criteria/queryable/extensions/date_time.rb +53 -0
  34. data/lib/mongoid/criteria/queryable/extensions/hash.rb +200 -0
  35. data/lib/mongoid/criteria/queryable/extensions/nil_class.rb +86 -0
  36. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +90 -0
  37. data/lib/mongoid/criteria/queryable/extensions/object.rb +206 -0
  38. data/lib/mongoid/criteria/queryable/extensions/range.rb +70 -0
  39. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +79 -0
  40. data/lib/mongoid/criteria/queryable/extensions/set.rb +34 -0
  41. data/lib/mongoid/criteria/queryable/extensions/string.rb +137 -0
  42. data/lib/mongoid/criteria/queryable/extensions/symbol.rb +79 -0
  43. data/lib/mongoid/criteria/queryable/extensions/time.rb +60 -0
  44. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +54 -0
  45. data/lib/mongoid/criteria/queryable/forwardable.rb +65 -0
  46. data/lib/mongoid/criteria/queryable/key.rb +103 -0
  47. data/lib/mongoid/criteria/queryable/macroable.rb +27 -0
  48. data/lib/mongoid/criteria/queryable/mergeable.rb +271 -0
  49. data/lib/mongoid/criteria/queryable/optional.rb +429 -0
  50. data/lib/mongoid/criteria/queryable/options.rb +153 -0
  51. data/lib/mongoid/criteria/queryable/pipeline.rb +111 -0
  52. data/lib/mongoid/criteria/queryable/selectable.rb +662 -0
  53. data/lib/mongoid/criteria/queryable/selector.rb +212 -0
  54. data/lib/mongoid/criteria/queryable/smash.rb +104 -0
  55. data/lib/mongoid/document.rb +30 -37
  56. data/lib/mongoid/errors.rb +2 -0
  57. data/lib/mongoid/errors/ambiguous_relationship.rb +1 -1
  58. data/lib/mongoid/errors/in_memory_collation_not_supported.rb +1 -1
  59. data/lib/mongoid/errors/invalid_field.rb +2 -2
  60. data/lib/mongoid/errors/invalid_persistence_option.rb +29 -0
  61. data/lib/mongoid/errors/invalid_relation.rb +66 -0
  62. data/lib/mongoid/evolvable.rb +1 -1
  63. data/lib/mongoid/extensions.rb +0 -4
  64. data/lib/mongoid/extensions/big_decimal.rb +17 -8
  65. data/lib/mongoid/extensions/date.rb +4 -1
  66. data/lib/mongoid/extensions/decimal128.rb +3 -3
  67. data/lib/mongoid/extensions/hash.rb +1 -0
  68. data/lib/mongoid/extensions/string.rb +4 -3
  69. data/lib/mongoid/extensions/time.rb +4 -1
  70. data/lib/mongoid/fields/validators/macro.rb +18 -0
  71. data/lib/mongoid/findable.rb +2 -2
  72. data/lib/mongoid/indexable.rb +15 -13
  73. data/lib/mongoid/interceptable.rb +5 -22
  74. data/lib/mongoid/matchable.rb +13 -7
  75. data/lib/mongoid/matchable/all.rb +2 -2
  76. data/lib/mongoid/matchable/and.rb +3 -3
  77. data/lib/mongoid/matchable/default.rb +2 -2
  78. data/lib/mongoid/matchable/elem_match.rb +28 -0
  79. data/lib/mongoid/matchable/exists.rb +2 -2
  80. data/lib/mongoid/matchable/gt.rb +4 -2
  81. data/lib/mongoid/matchable/gte.rb +4 -2
  82. data/lib/mongoid/matchable/in.rb +2 -2
  83. data/lib/mongoid/matchable/lt.rb +4 -2
  84. data/lib/mongoid/matchable/lte.rb +4 -2
  85. data/lib/mongoid/matchable/ne.rb +2 -2
  86. data/lib/mongoid/matchable/nin.rb +2 -2
  87. data/lib/mongoid/matchable/or.rb +3 -3
  88. data/lib/mongoid/matchable/regexp.rb +3 -3
  89. data/lib/mongoid/matchable/size.rb +2 -2
  90. data/lib/mongoid/persistable.rb +3 -5
  91. data/lib/mongoid/persistable/creatable.rb +2 -2
  92. data/lib/mongoid/persistable/deletable.rb +1 -1
  93. data/lib/mongoid/persistable/settable.rb +1 -1
  94. data/lib/mongoid/persistable/updatable.rb +5 -12
  95. data/lib/mongoid/persistable/upsertable.rb +1 -1
  96. data/lib/mongoid/persistence_context.rb +215 -0
  97. data/lib/mongoid/query_cache.rb +3 -6
  98. data/lib/mongoid/relations/accessors.rb +3 -0
  99. data/lib/mongoid/relations/auto_save.rb +12 -4
  100. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +4 -4
  101. data/lib/mongoid/relations/counter_cache.rb +15 -5
  102. data/lib/mongoid/relations/eager.rb +6 -11
  103. data/lib/mongoid/relations/eager/base.rb +3 -3
  104. data/lib/mongoid/relations/eager/has_and_belongs_to_many.rb +2 -2
  105. data/lib/mongoid/relations/eager/has_many.rb +1 -1
  106. data/lib/mongoid/relations/embedded/batchable.rb +12 -36
  107. data/lib/mongoid/relations/embedded/in.rb +13 -1
  108. data/lib/mongoid/relations/embedded/many.rb +28 -10
  109. data/lib/mongoid/relations/embedded/one.rb +14 -1
  110. data/lib/mongoid/relations/macros.rb +9 -1
  111. data/lib/mongoid/relations/metadata.rb +3 -3
  112. data/lib/mongoid/relations/options.rb +2 -2
  113. data/lib/mongoid/relations/proxy.rb +1 -31
  114. data/lib/mongoid/relations/referenced/in.rb +19 -10
  115. data/lib/mongoid/relations/referenced/many.rb +23 -17
  116. data/lib/mongoid/relations/referenced/many_to_many.rb +20 -13
  117. data/lib/mongoid/relations/referenced/one.rb +15 -1
  118. data/lib/mongoid/relations/synchronization.rb +11 -11
  119. data/lib/mongoid/relations/touchable.rb +6 -3
  120. data/lib/mongoid/reloadable.rb +1 -1
  121. data/lib/mongoid/serializable.rb +1 -1
  122. data/lib/mongoid/traversable.rb +1 -1
  123. data/lib/mongoid/validatable/uniqueness.rb +1 -2
  124. data/lib/mongoid/version.rb +1 -1
  125. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +14 -3
  126. data/spec/app/models/album.rb +5 -1
  127. data/spec/app/models/artist.rb +21 -0
  128. data/spec/app/models/book.rb +2 -1
  129. data/spec/app/models/dokument.rb +1 -0
  130. data/spec/app/models/ordered_post.rb +5 -0
  131. data/spec/app/models/oscar.rb +1 -2
  132. data/spec/app/models/page.rb +1 -1
  133. data/spec/app/models/person.rb +3 -3
  134. data/spec/app/models/princess.rb +2 -0
  135. data/spec/app/models/record.rb +1 -0
  136. data/spec/app/models/subscription.rb +1 -0
  137. data/spec/app/models/thing.rb +1 -1
  138. data/spec/config/mongoid.yml +15 -0
  139. data/spec/mongoid/atomic/modifiers_spec.rb +17 -17
  140. data/spec/mongoid/atomic_spec.rb +17 -17
  141. data/spec/mongoid/attributes/nested_spec.rb +14 -14
  142. data/spec/mongoid/attributes/readonly_spec.rb +87 -44
  143. data/spec/mongoid/attributes_spec.rb +90 -5
  144. data/spec/mongoid/cacheable_spec.rb +112 -0
  145. data/spec/mongoid/changeable_spec.rb +58 -0
  146. data/spec/mongoid/clients/factory_spec.rb +31 -3
  147. data/spec/mongoid/clients/options_spec.rb +382 -96
  148. data/spec/mongoid/clients_spec.rb +243 -101
  149. data/spec/mongoid/composable_spec.rb +7 -0
  150. data/spec/mongoid/config_spec.rb +67 -11
  151. data/spec/mongoid/contextual/atomic_spec.rb +3 -3
  152. data/spec/mongoid/contextual/mongo_spec.rb +165 -20
  153. data/spec/mongoid/contextual/none_spec.rb +15 -0
  154. data/spec/mongoid/copyable_spec.rb +13 -4
  155. data/spec/mongoid/criteria/modifiable_spec.rb +239 -7
  156. data/spec/mongoid/criteria/options_spec.rb +29 -0
  157. data/spec/mongoid/criteria/queryable/aggregable_spec.rb +370 -0
  158. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +523 -0
  159. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +59 -0
  160. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +58 -0
  161. data/spec/mongoid/criteria/queryable/extensions/boolean_spec.rb +213 -0
  162. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +330 -0
  163. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +405 -0
  164. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +58 -0
  165. data/spec/mongoid/criteria/queryable/extensions/float_spec.rb +65 -0
  166. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +327 -0
  167. data/spec/mongoid/criteria/queryable/extensions/integer_spec.rb +65 -0
  168. data/spec/mongoid/criteria/queryable/extensions/nil_class_spec.rb +77 -0
  169. data/spec/mongoid/criteria/queryable/extensions/object_spec.rb +108 -0
  170. data/spec/mongoid/criteria/queryable/extensions/range_spec.rb +309 -0
  171. data/spec/mongoid/{extensions/origin → criteria/queryable/extensions}/regexp_raw_spec.rb +2 -2
  172. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +90 -0
  173. data/spec/mongoid/criteria/queryable/extensions/set_spec.rb +39 -0
  174. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +302 -0
  175. data/spec/mongoid/criteria/queryable/extensions/symbol_spec.rb +167 -0
  176. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +376 -0
  177. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +347 -0
  178. data/spec/mongoid/criteria/queryable/forwardable_spec.rb +87 -0
  179. data/spec/mongoid/criteria/queryable/key_spec.rb +52 -0
  180. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +49 -0
  181. data/spec/mongoid/criteria/queryable/optional_spec.rb +1799 -0
  182. data/spec/mongoid/criteria/queryable/options_spec.rb +360 -0
  183. data/spec/mongoid/criteria/queryable/pipeline_spec.rb +200 -0
  184. data/spec/mongoid/criteria/queryable/queryable_spec.rb +137 -0
  185. data/spec/mongoid/criteria/queryable/selectable_spec.rb +4174 -0
  186. data/spec/mongoid/criteria/queryable/selector_spec.rb +844 -0
  187. data/spec/mongoid/criteria/queryable/smash_spec.rb +30 -0
  188. data/spec/mongoid/criteria_spec.rb +152 -21
  189. data/spec/mongoid/document_spec.rb +37 -88
  190. data/spec/mongoid/errors/invalid_relation_spec.rb +37 -0
  191. data/spec/mongoid/errors/mongoid_error_spec.rb +6 -3
  192. data/spec/mongoid/extensions/big_decimal_spec.rb +320 -18
  193. data/spec/mongoid/extensions/boolean_spec.rb +14 -0
  194. data/spec/mongoid/extensions/date_spec.rb +2 -6
  195. data/spec/mongoid/extensions/date_time_spec.rb +2 -6
  196. data/spec/mongoid/extensions/decimal128_spec.rb +1 -1
  197. data/spec/mongoid/extensions/float_spec.rb +8 -1
  198. data/spec/mongoid/extensions/hash_spec.rb +15 -0
  199. data/spec/mongoid/extensions/integer_spec.rb +8 -1
  200. data/spec/mongoid/extensions/object_spec.rb +11 -0
  201. data/spec/mongoid/extensions/string_spec.rb +21 -0
  202. data/spec/mongoid/extensions/time_spec.rb +2 -6
  203. data/spec/mongoid/extensions/time_with_zone_spec.rb +2 -6
  204. data/spec/mongoid/findable_spec.rb +46 -1
  205. data/spec/mongoid/indexable_spec.rb +15 -3
  206. data/spec/mongoid/interceptable_spec.rb +68 -10
  207. data/spec/mongoid/matchable/all_spec.rb +4 -4
  208. data/spec/mongoid/matchable/and_spec.rb +10 -10
  209. data/spec/mongoid/matchable/default_spec.rb +12 -12
  210. data/spec/mongoid/matchable/elem_match_spec.rb +86 -0
  211. data/spec/mongoid/matchable/exists_spec.rb +5 -5
  212. data/spec/mongoid/matchable/gt_spec.rb +18 -7
  213. data/spec/mongoid/matchable/gte_spec.rb +17 -7
  214. data/spec/mongoid/matchable/in_spec.rb +5 -5
  215. data/spec/mongoid/matchable/lt_spec.rb +18 -7
  216. data/spec/mongoid/matchable/lte_spec.rb +18 -7
  217. data/spec/mongoid/matchable/ne_spec.rb +5 -5
  218. data/spec/mongoid/matchable/nin_spec.rb +5 -5
  219. data/spec/mongoid/matchable/or_spec.rb +7 -7
  220. data/spec/mongoid/matchable/regexp_spec.rb +5 -5
  221. data/spec/mongoid/matchable/size_spec.rb +3 -3
  222. data/spec/mongoid/matchable_spec.rb +173 -53
  223. data/spec/mongoid/persistable/creatable_spec.rb +7 -2
  224. data/spec/mongoid/persistable/deletable_spec.rb +16 -1
  225. data/spec/mongoid/persistable/destroyable_spec.rb +6 -2
  226. data/spec/mongoid/persistable/savable_spec.rb +35 -30
  227. data/spec/mongoid/persistable/settable_spec.rb +45 -29
  228. data/spec/mongoid/persistable/updatable_spec.rb +184 -5
  229. data/spec/mongoid/persistence_context_spec.rb +680 -0
  230. data/spec/mongoid/positional_spec.rb +10 -10
  231. data/spec/mongoid/query_cache_spec.rb +89 -0
  232. data/spec/mongoid/relations/accessors_spec.rb +1 -1
  233. data/spec/mongoid/relations/auto_save_spec.rb +39 -6
  234. data/spec/mongoid/relations/bindings/referenced/many_to_many_spec.rb +4 -4
  235. data/spec/mongoid/relations/builders_spec.rb +37 -10
  236. data/spec/mongoid/relations/counter_cache_spec.rb +64 -3
  237. data/spec/mongoid/relations/eager/has_and_belongs_to_many_spec.rb +16 -0
  238. data/spec/mongoid/relations/eager_spec.rb +40 -0
  239. data/spec/mongoid/relations/embedded/many_spec.rb +63 -47
  240. data/spec/mongoid/relations/embedded/one_spec.rb +2 -1
  241. data/spec/mongoid/relations/macros_spec.rb +395 -7
  242. data/spec/mongoid/relations/metadata_spec.rb +15 -1
  243. data/spec/mongoid/relations/proxy_spec.rb +27 -1
  244. data/spec/mongoid/relations/referenced/in_spec.rb +41 -1
  245. data/spec/mongoid/relations/referenced/many_spec.rb +13 -25
  246. data/spec/mongoid/relations/referenced/many_to_many_spec.rb +14 -26
  247. data/spec/mongoid/relations/synchronization_spec.rb +48 -2
  248. data/spec/mongoid/relations/touchable_spec.rb +40 -0
  249. data/spec/mongoid/reloadable_spec.rb +51 -0
  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 +18 -9
  253. data/spec/mongoid/validatable_spec.rb +16 -0
  254. data/spec/spec_helper.rb +20 -11
  255. metadata +524 -469
  256. checksums.yaml.gz.sig +0 -0
  257. data.tar.gz.sig +0 -0
  258. data/lib/mongoid/clients/thread_options.rb +0 -19
  259. data/lib/mongoid/extensions/origin/regexp_raw.rb +0 -43
  260. metadata.gz.sig +0 -0
@@ -523,13 +523,18 @@ describe Mongoid::Persistable::Creatable do
523
523
  context "when a unique index exists" do
524
524
 
525
525
  before do
526
+ Person.index({ ssn: 1 }, { unique: true })
526
527
  Person.create_indexes
527
528
  end
528
529
 
530
+ after do
531
+ Person.collection.drop
532
+ end
533
+
529
534
  it "raises an error" do
530
535
  expect {
531
536
  4.times { Person.create!(ssn: "555-55-1029") }
532
- }.to raise_error
537
+ }.to raise_error(Mongo::Error::OperationFailure)
533
538
  end
534
539
  end
535
540
  end
@@ -562,7 +567,7 @@ describe Mongoid::Persistable::Creatable do
562
567
  end
563
568
  end
564
569
 
565
- context "when a callback returns false" do
570
+ context "when a callback aborts the chain" do
566
571
 
567
572
  it "raises a callback error" do
568
573
  expect { Oscar.create! }.to raise_error(Mongoid::Errors::Callback)
@@ -30,7 +30,7 @@ describe Mongoid::Persistable::Deletable do
30
30
  it "deletes the document from the collection" do
31
31
  expect {
32
32
  Person.find(person.id)
33
- }.to raise_error
33
+ }.to raise_error(Mongoid::Errors::DocumentNotFound)
34
34
  end
35
35
 
36
36
  it "returns true" do
@@ -213,6 +213,21 @@ describe Mongoid::Persistable::Deletable do
213
213
  expect(removed).to eq(1)
214
214
  end
215
215
  end
216
+
217
+ context "when the conditions need to be mongoized" do
218
+
219
+ let!(:removed) do
220
+ Person.delete_all(id: person.id)
221
+ end
222
+
223
+ it "removes the matching documents" do
224
+ expect(Person.count).to eq(1)
225
+ end
226
+
227
+ it "returns the number of documents removed" do
228
+ expect(removed).to eq(1)
229
+ end
230
+ end
216
231
  end
217
232
  end
218
233
  end
@@ -30,7 +30,7 @@ describe Mongoid::Persistable::Destroyable do
30
30
  it "destroys the document from the collection" do
31
31
  expect {
32
32
  Person.find(person.id)
33
- }.to raise_error
33
+ }.to raise_error(Mongoid::Errors::DocumentNotFound)
34
34
  end
35
35
 
36
36
  it "returns true" do
@@ -164,7 +164,11 @@ describe Mongoid::Persistable::Destroyable do
164
164
  end
165
165
 
166
166
  before do
167
- expect(album).to receive(:set_parent_name).and_return(false)
167
+ Album.before_destroy(:set_parent_name_fail)
168
+ end
169
+
170
+ after do
171
+ Album.reset_callbacks(:destroy)
168
172
  end
169
173
 
170
174
  it "raises an exception" do
@@ -189,8 +189,8 @@ describe Mongoid::Persistable::Savable do
189
189
  "title" => "King",
190
190
  "name.first_name" => "Ryan"
191
191
  },
192
- "$push"=> {
193
- "addresses" => {'$each' => [ { "_id" => address.id, "street" => "Bond St" } ]}
192
+ "$pushAll"=> {
193
+ "addresses" => [ { "_id" => address.id, "street" => "Bond St" } ]
194
194
  }
195
195
  })
196
196
  end
@@ -295,20 +295,37 @@ describe Mongoid::Persistable::Savable do
295
295
  end
296
296
  end
297
297
 
298
- context "when the document is readonly" do
299
-
300
- let(:person) do
301
- Person.only(:title).first
302
- end
298
+ context "when the changed attribute is not writable" do
303
299
 
304
300
  before do
305
301
  Person.create(title: "sir")
306
302
  end
307
303
 
304
+ let(:person) do
305
+ Person.only(:title).first
306
+ end
307
+
308
308
  it "raises an error" do
309
309
  expect {
310
+ person.username = 'unloaded-attribute'
310
311
  person.save
311
- }.to raise_error(Mongoid::Errors::ReadonlyDocument)
312
+ }.to raise_error(Mongoid::Errors::ReadonlyAttribute)
313
+ end
314
+
315
+ context 'when the changed attribute is aliased' do
316
+
317
+ before do
318
+ Person.create(at: Time.now)
319
+ end
320
+
321
+ let(:person) do
322
+ Person.only(:at).first
323
+ end
324
+
325
+ it "saves the document" do
326
+ person.aliased_timestamp = Time.now
327
+ expect(person.save(validate: false)).to be true
328
+ end
312
329
  end
313
330
  end
314
331
 
@@ -353,12 +370,17 @@ describe Mongoid::Persistable::Savable do
353
370
  end
354
371
 
355
372
  before do
373
+ Person.index({ ssn: 1 }, { unique: true })
356
374
  Person.create_indexes
357
375
  Person.create!(ssn: "555-55-9999")
358
376
  end
359
377
 
360
- it "raises an error" do
361
- expect { person.save! }.to raise_error
378
+ after do
379
+ Person.collection.drop
380
+ end
381
+
382
+ it "raises an OperationFailure" do
383
+ expect { person.save! }.to raise_error(Mongo::Error::OperationFailure)
362
384
  end
363
385
  end
364
386
  end
@@ -374,12 +396,12 @@ describe Mongoid::Persistable::Savable do
374
396
  end
375
397
 
376
398
  it "raises an error with multiple save attempts" do
377
- expect { subject.save! }.to raise_error
378
- expect { subject.save! }.to raise_error
399
+ expect { service.save! }.to raise_error(Mongoid::Errors::Validations)
400
+ expect { service.save! }.to raise_error(Mongoid::Errors::Validations)
379
401
  end
380
402
  end
381
403
 
382
- context "when a callback returns false" do
404
+ context "when a callback aborts the callback chain" do
383
405
 
384
406
  let(:oscar) do
385
407
  Oscar.new
@@ -495,22 +517,5 @@ describe Mongoid::Persistable::Savable do
495
517
  expect(from_db.shapes.first.canvas).to eq(firefox)
496
518
  end
497
519
  end
498
-
499
- context "when the document is readonly" do
500
-
501
- let(:person) do
502
- Person.only(:title).first
503
- end
504
-
505
- before do
506
- Person.create(title: "sir")
507
- end
508
-
509
- it "raises an error" do
510
- expect {
511
- person.save!
512
- }.to raise_error(Mongoid::Errors::ReadonlyDocument)
513
- end
514
- end
515
520
  end
516
521
  end
@@ -230,6 +230,22 @@ describe Mongoid::Persistable::Settable do
230
230
  end
231
231
  end
232
232
 
233
+ context 'when the field is assigned with nil' do
234
+
235
+ before do
236
+ church.location = nil
237
+ church.set('location.neighborhood' => 'Kreuzberg')
238
+ end
239
+
240
+ it 'updates the hash while keeping existing key and values locally' do
241
+ expect(church.location).to eq({'neighborhood' => 'Kreuzberg'})
242
+ end
243
+
244
+ it 'updates the hash in the database' do
245
+ expect(church.reload.location).to eq({'neighborhood' => 'Kreuzberg'})
246
+ end
247
+ end
248
+
233
249
  context 'when the field type is String' do
234
250
 
235
251
  before do
@@ -261,48 +277,48 @@ describe Mongoid::Persistable::Settable do
261
277
  expect(church.reload.location).to eq({ 'city' => 'Berlin', 'street' => 'Yorckstr.'})
262
278
  end
263
279
  end
264
- end
265
280
 
266
- context 'when the field is a nested hash' do
281
+ context 'when the field is a bested hash' do
267
282
 
268
- context 'when a leaf value in the nested hash is updated' do
283
+ context 'when a leaf value in the nested hash is updated' do
269
284
 
270
- let(:church) do
271
- Church.new.tap do |a|
272
- a.location = {'address' => {'city' => 'Berlin', 'street' => 'Yorckstr'}}
273
- a.name = 'Church1'
274
- a.save
285
+ let(:church) do
286
+ Church.new.tap do |a|
287
+ a.location = {'address' => {'city' => 'Berlin', 'street' => 'Yorckstr'}}
288
+ a.name = 'Church1'
289
+ a.save
290
+ end
275
291
  end
276
- end
277
292
 
278
- before do
279
- church.set('location.address.city' => 'Munich')
280
- end
293
+ before do
294
+ church.set('location.address.city' => 'Munich')
295
+ end
281
296
 
282
- it 'does not reset the nested hash' do
283
- expect(church.name).to eq('Church1')
284
- expect(church.location).to eql({'address' => {'city' => 'Munich', 'street' => 'Yorckstr'}})
297
+ it 'does not reset the nested hash' do
298
+ expect(church.name).to eq('Church1')
299
+ expect(church.location).to eql({'address' => {'city' => 'Munich', 'street' => 'Yorckstr'}})
300
+ end
285
301
  end
286
- end
287
302
 
288
303
 
289
- context 'when the nested hash is many levels deep' do
304
+ context 'when the nested hash is many levels deep' do
290
305
 
291
- let(:church) do
292
- Church.new.tap do |a|
293
- a.location = {'address' => {'state' => {'address' => {'city' => 'Berlin', 'street' => 'Yorckstr'}}}}
294
- a.name = 'Church1'
295
- a.save
306
+ let(:church) do
307
+ Church.new.tap do |a|
308
+ a.location = {'address' => {'state' => {'address' => {'city' => 'Berlin', 'street' => 'Yorckstr'}}}}
309
+ a.name = 'Church1'
310
+ a.save
311
+ end
296
312
  end
297
- end
298
313
 
299
- before do
300
- church.set('location.address.state.address.city' => 'Munich')
301
- end
314
+ before do
315
+ church.set('location.address.state.address.city' => 'Munich')
316
+ end
302
317
 
303
- it 'does not reset the nested hash' do
304
- expect(church.name).to eq('Church1')
305
- expect(church.location).to eql({'address' => {'state' => {'address' => {'city' => 'Munich', 'street' => 'Yorckstr'}}}})
318
+ it 'does not reset the nested hash' do
319
+ expect(church.name).to eq('Church1')
320
+ expect(church.location).to eql({'address' => {'state' => {'address' => {'city' => 'Munich', 'street' => 'Yorckstr'}}}})
321
+ end
306
322
  end
307
323
  end
308
324
  end
@@ -112,6 +112,24 @@ describe Mongoid::Persistable::Updatable do
112
112
  end
113
113
  end
114
114
 
115
+ context "when dynamic attributes are not enabled" do
116
+
117
+ it "raises exception for an unknown attribute " do
118
+ account = Account.create
119
+
120
+ expect {
121
+ account.update_attribute(:somethingnew, "somethingnew")
122
+ }.to raise_error(Mongoid::Errors::UnknownAttribute)
123
+ end
124
+
125
+ it "will update value of aliased field" do
126
+ person = Person.create
127
+ person.update_attribute(:t, "test_value")
128
+ expect(person.reload.t).to eq "test_value"
129
+ expect(person.test).to eq "test_value"
130
+ end
131
+ end
132
+
115
133
  context "when provided a symbol attribute name" do
116
134
 
117
135
  let(:post) do
@@ -168,7 +186,7 @@ describe Mongoid::Persistable::Updatable do
168
186
  it "raises an error" do
169
187
  expect {
170
188
  post.update_attribute(:title, "something")
171
- }.to raise_error
189
+ }.to raise_error(RuntimeError)
172
190
  end
173
191
  end
174
192
  end
@@ -269,6 +287,167 @@ describe Mongoid::Persistable::Updatable do
269
287
  expect(from_db.reload.name).to eq("home")
270
288
  end
271
289
  end
290
+
291
+ context 'when the field is read-only' do
292
+
293
+ before do
294
+ Person.attr_readonly :species
295
+ end
296
+
297
+ after do
298
+ Person.readonly_attributes.reject! { |a| a.to_s == 'species' }
299
+ end
300
+
301
+ let(:person) do
302
+ Person.create(species: :human)
303
+ end
304
+
305
+ it 'raises an error when trying to set the attribute' do
306
+ expect {
307
+ person.update_attribute(:species, :reptile)
308
+ }.to raise_exception(Mongoid::Errors::ReadonlyAttribute)
309
+ end
310
+
311
+ context 'when referring to the attribute with a string' do
312
+
313
+ it 'raises an error when trying to set the attribute' do
314
+ expect {
315
+ person.update_attribute('species', :reptile)
316
+ }.to raise_exception(Mongoid::Errors::ReadonlyAttribute)
317
+ end
318
+ end
319
+
320
+ context 'when the field is aliased' do
321
+
322
+ before do
323
+ Person.attr_readonly :at
324
+ end
325
+
326
+ after do
327
+ Person.readonly_attributes.reject! { |a| a.to_s == 'at' }
328
+ end
329
+
330
+ it 'raises an error when trying to set the attribute using the db name' do
331
+ expect {
332
+ person.update_attribute(:at, Time.now)
333
+ }.to raise_exception(Mongoid::Errors::ReadonlyAttribute)
334
+ end
335
+
336
+ it 'raises an error when trying to set the attribute using the aliased name' do
337
+ expect {
338
+ person.update_attribute(:aliased_timestamp, Time.now)
339
+ }.to raise_exception(Mongoid::Errors::ReadonlyAttribute)
340
+ end
341
+ end
342
+ end
343
+
344
+ context 'when the field is loaded explicitly' do
345
+
346
+ before do
347
+ Person.create(title: 'Captain')
348
+ end
349
+
350
+ context 'when the loaded attribute is updated' do
351
+
352
+ let(:person) do
353
+ Person.only(:title).first.tap do |_person|
354
+ _person.update_attribute(:title, 'Esteemed')
355
+ end
356
+ end
357
+
358
+ it 'allows the field to be updated' do
359
+ expect(person.title).to eq('Esteemed')
360
+ end
361
+
362
+ it 'persists the updated field' do
363
+ expect(person.reload.title).to eq('Esteemed')
364
+ end
365
+ end
366
+
367
+ context 'when the an attribute other than the loaded one is updated' do
368
+
369
+ let(:person) do
370
+ Person.only(:title).first
371
+ end
372
+
373
+ it 'does not allow the field to be updated' do
374
+ expect {
375
+ person.update_attribute(:age, 20)
376
+ }.to raise_exception(Mongoid::Errors::ReadonlyAttribute)
377
+ end
378
+
379
+ it 'does not persist the change' do
380
+ expect(person.reload.age).to eq(100)
381
+ end
382
+
383
+ context 'when referring to the attribute with a string' do
384
+
385
+ it 'does not allow the field to be updated' do
386
+ expect {
387
+ person.update_attribute('age', 20)
388
+ }.to raise_exception(Mongoid::Errors::ReadonlyAttribute)
389
+ end
390
+
391
+ it 'does not persist the change' do
392
+ expect(person.reload.age).to eq(100)
393
+ end
394
+ end
395
+ end
396
+ end
397
+
398
+ context 'when fields are explicitly not loaded' do
399
+
400
+ before do
401
+ Person.create(title: 'Captain')
402
+ end
403
+
404
+ context 'when the loaded attribute is updated' do
405
+
406
+ let(:person) do
407
+ Person.without(:age).first.tap do |_person|
408
+ _person.update_attribute(:title, 'Esteemed')
409
+ end
410
+ end
411
+
412
+ it 'allows the field to be updated' do
413
+ expect(person.title).to eq('Esteemed')
414
+ end
415
+
416
+ it 'persists the updated field' do
417
+ expect(person.reload.title).to eq('Esteemed')
418
+ end
419
+ end
420
+
421
+ context 'when the non-loaded attribute is updated' do
422
+
423
+ let(:person) do
424
+ Person.without(:title).first
425
+ end
426
+
427
+ it 'does not allow the field to be updated' do
428
+ expect {
429
+ person.update_attribute(:title, 'Esteemed')
430
+ }.to raise_exception(ActiveModel::MissingAttributeError)
431
+ end
432
+
433
+ it 'does not persist the change' do
434
+ expect(person.reload.title).to eq('Captain')
435
+ end
436
+
437
+ context 'when referring to the attribute with a string' do
438
+
439
+ it 'does not allow the field to be updated' do
440
+ expect {
441
+ person.update_attribute('title', 'Esteemed')
442
+ }.to raise_exception(ActiveModel::MissingAttributeError)
443
+ end
444
+
445
+ it 'does not persist the change' do
446
+ expect(person.reload.title).to eq('Captain')
447
+ end
448
+ end
449
+ end
450
+ end
272
451
  end
273
452
 
274
453
  [:update_attributes, :update].each do |method|
@@ -283,7 +462,7 @@ describe Mongoid::Persistable::Updatable do
283
462
 
284
463
  it "raises an error" do
285
464
  expect {
286
- person.update_attributes(map: { "$bad.key" => "value" })
465
+ person.update_attributes(map: { "bad.key" => "value" })
287
466
  }.to raise_error(Mongo::Error::OperationFailure)
288
467
  end
289
468
  end
@@ -319,7 +498,7 @@ describe Mongoid::Persistable::Updatable do
319
498
 
320
499
  it "raises an error" do
321
500
  expect {
322
- person.send(method, map: { "$bad.key" => "value" })
501
+ person.send(method, map: { "bad.key" => "value" })
323
502
  }.to raise_error(Mongo::Error::OperationFailure)
324
503
  end
325
504
  end
@@ -360,7 +539,7 @@ describe Mongoid::Persistable::Updatable do
360
539
  it "raises an error" do
361
540
  expect {
362
541
  person.send(method, title: "something")
363
- }.to raise_error
542
+ }.to raise_error(RuntimeError)
364
543
  end
365
544
  end
366
545
 
@@ -543,7 +722,7 @@ describe Mongoid::Persistable::Updatable do
543
722
 
544
723
  describe "#update!" do
545
724
 
546
- context "when a callback returns false" do
725
+ context "when a callback aborts the callback chain" do
547
726
 
548
727
  let(:oscar) do
549
728
  Oscar.new