mongoid 8.0.13 → 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 (225) 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 +17 -66
  6. data/lib/config/locales/en.yml +46 -40
  7. data/lib/mongoid/association/accessors.rb +3 -7
  8. data/lib/mongoid/association/builders.rb +1 -1
  9. data/lib/mongoid/association/depending.rb +4 -8
  10. data/lib/mongoid/association/eager_loadable.rb +0 -3
  11. data/lib/mongoid/association/embedded/batchable.rb +2 -2
  12. data/lib/mongoid/association/embedded/embedded_in/buildable.rb +2 -2
  13. data/lib/mongoid/association/embedded/embedded_in/proxy.rb +2 -1
  14. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +3 -2
  15. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +6 -6
  16. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +1 -1
  17. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +2 -2
  18. data/lib/mongoid/association/macros.rb +0 -6
  19. data/lib/mongoid/association/nested/many.rb +3 -38
  20. data/lib/mongoid/association/nested/nested_buildable.rb +0 -14
  21. data/lib/mongoid/association/nested/one.rb +40 -2
  22. data/lib/mongoid/association/proxy.rb +1 -1
  23. data/lib/mongoid/association/referenced/counter_cache.rb +2 -2
  24. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +1 -1
  25. data/lib/mongoid/association/referenced/has_many/enumerable.rb +6 -23
  26. data/lib/mongoid/association/referenced/has_many/proxy.rb +4 -103
  27. data/lib/mongoid/association/reflections.rb +2 -2
  28. data/lib/mongoid/atomic.rb +7 -16
  29. data/lib/mongoid/attributes/dynamic.rb +1 -1
  30. data/lib/mongoid/attributes/nested.rb +2 -2
  31. data/lib/mongoid/attributes/processing.rb +5 -29
  32. data/lib/mongoid/attributes/projector.rb +1 -1
  33. data/lib/mongoid/attributes/readonly.rb +1 -1
  34. data/lib/mongoid/attributes.rb +8 -2
  35. data/lib/mongoid/changeable.rb +107 -5
  36. data/lib/mongoid/clients/factory.rb +0 -4
  37. data/lib/mongoid/clients/storage_options.rb +2 -5
  38. data/lib/mongoid/clients/validators/storage.rb +1 -13
  39. data/lib/mongoid/collection_configurable.rb +58 -0
  40. data/lib/mongoid/composable.rb +2 -0
  41. data/lib/mongoid/config/defaults.rb +60 -0
  42. data/lib/mongoid/config/options.rb +0 -3
  43. data/lib/mongoid/config/validators/async_query_executor.rb +24 -0
  44. data/lib/mongoid/config/validators.rb +1 -0
  45. data/lib/mongoid/config.rb +88 -89
  46. data/lib/mongoid/contextual/aggregable/memory.rb +2 -5
  47. data/lib/mongoid/contextual/atomic.rb +1 -1
  48. data/lib/mongoid/contextual/memory.rb +240 -51
  49. data/lib/mongoid/contextual/mongo/documents_loader.rb +177 -0
  50. data/lib/mongoid/contextual/mongo.rb +370 -133
  51. data/lib/mongoid/contextual/none.rb +162 -7
  52. data/lib/mongoid/contextual.rb +12 -0
  53. data/lib/mongoid/criteria/findable.rb +2 -2
  54. data/lib/mongoid/criteria/includable.rb +4 -3
  55. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -15
  56. data/lib/mongoid/criteria/queryable/key.rb +1 -1
  57. data/lib/mongoid/criteria/queryable/mergeable.rb +1 -13
  58. data/lib/mongoid/criteria/queryable/optional.rb +8 -8
  59. data/lib/mongoid/criteria/queryable/selectable.rb +43 -121
  60. data/lib/mongoid/criteria/queryable/selector.rb +1 -1
  61. data/lib/mongoid/criteria/queryable/storable.rb +1 -1
  62. data/lib/mongoid/criteria.rb +6 -5
  63. data/lib/mongoid/deprecable.rb +1 -2
  64. data/lib/mongoid/deprecation.rb +3 -3
  65. data/lib/mongoid/document.rb +1 -8
  66. data/lib/mongoid/errors/create_collection_failure.rb +33 -0
  67. data/lib/mongoid/errors/drop_collection_failure.rb +27 -0
  68. data/lib/mongoid/errors/immutable_attribute.rb +26 -0
  69. data/lib/mongoid/errors/invalid_async_query_executor.rb +25 -0
  70. data/lib/mongoid/errors/invalid_global_executor_concurrency.rb +22 -0
  71. data/lib/mongoid/errors/invalid_storage_parent.rb +2 -0
  72. data/lib/mongoid/errors.rb +4 -2
  73. data/lib/mongoid/extensions/hash.rb +3 -51
  74. data/lib/mongoid/extensions/object.rb +2 -2
  75. data/lib/mongoid/extensions/time.rb +2 -0
  76. data/lib/mongoid/fields/localized.rb +10 -0
  77. data/lib/mongoid/fields/standard.rb +10 -0
  78. data/lib/mongoid/fields.rb +59 -35
  79. data/lib/mongoid/findable.rb +27 -3
  80. data/lib/mongoid/interceptable.rb +6 -116
  81. data/lib/mongoid/matchable.rb +1 -6
  82. data/lib/mongoid/matcher/eq_impl.rb +1 -1
  83. data/lib/mongoid/matcher/eq_impl_with_regexp.rb +5 -3
  84. data/lib/mongoid/matcher/regex.rb +9 -8
  85. data/lib/mongoid/matcher/type.rb +1 -1
  86. data/lib/mongoid/matcher.rb +0 -1
  87. data/lib/mongoid/persistable/creatable.rb +1 -0
  88. data/lib/mongoid/persistable/deletable.rb +1 -1
  89. data/lib/mongoid/persistable/savable.rb +13 -1
  90. data/lib/mongoid/persistable/unsettable.rb +2 -2
  91. data/lib/mongoid/persistable/updatable.rb +51 -1
  92. data/lib/mongoid/persistable/upsertable.rb +20 -1
  93. data/lib/mongoid/persistable.rb +3 -0
  94. data/lib/mongoid/query_cache.rb +5 -1
  95. data/lib/mongoid/railties/database.rake +7 -2
  96. data/lib/mongoid/reloadable.rb +5 -3
  97. data/lib/mongoid/stateful.rb +22 -1
  98. data/lib/mongoid/tasks/database.rake +12 -0
  99. data/lib/mongoid/tasks/database.rb +20 -0
  100. data/lib/mongoid/threaded.rb +0 -3
  101. data/lib/mongoid/timestamps/created.rb +1 -8
  102. data/lib/mongoid/traversable.rb +0 -12
  103. data/lib/mongoid/utils.rb +22 -0
  104. data/lib/mongoid/validatable/associated.rb +17 -98
  105. data/lib/mongoid/validatable/macros.rb +5 -20
  106. data/lib/mongoid/validatable.rb +4 -10
  107. data/lib/mongoid/version.rb +1 -5
  108. data/lib/mongoid/warnings.rb +17 -2
  109. data/lib/mongoid.rb +16 -3
  110. data/spec/integration/app_spec.rb +2 -20
  111. data/spec/integration/associations/embeds_one_spec.rb +5 -25
  112. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +0 -55
  113. data/spec/integration/callbacks_spec.rb +99 -12
  114. data/spec/integration/discriminator_key_spec.rb +4 -5
  115. data/spec/integration/dots_and_dollars_spec.rb +2 -12
  116. data/spec/integration/i18n_fallbacks_spec.rb +3 -2
  117. data/spec/integration/matcher_operator_data/regex.yml +0 -21
  118. data/spec/mongoid/association/eager_spec.rb +2 -24
  119. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +27 -0
  120. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +20 -25
  121. data/spec/mongoid/association/embedded/embeds_many_models.rb +1 -0
  122. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +0 -4
  123. data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +15 -2
  124. data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -18
  125. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +42 -103
  126. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +9 -195
  127. data/spec/mongoid/association/syncable_spec.rb +1 -1
  128. data/spec/mongoid/attributes/nested_spec.rb +10 -204
  129. data/spec/mongoid/attributes_spec.rb +3 -33
  130. data/spec/mongoid/changeable_spec.rb +299 -24
  131. data/spec/mongoid/clients/factory_spec.rb +0 -30
  132. data/spec/mongoid/clients_spec.rb +122 -13
  133. data/spec/mongoid/collection_configurable_spec.rb +158 -0
  134. data/spec/mongoid/config/defaults_spec.rb +160 -0
  135. data/spec/mongoid/config_spec.rb +154 -27
  136. data/spec/mongoid/contextual/aggregable/memory_spec.rb +0 -91
  137. data/spec/mongoid/contextual/memory_spec.rb +332 -253
  138. data/spec/mongoid/contextual/mongo/documents_loader_spec.rb +187 -0
  139. data/spec/mongoid/contextual/mongo_spec.rb +1009 -125
  140. data/spec/mongoid/contextual/none_spec.rb +49 -2
  141. data/spec/mongoid/copyable_spec.rb +2 -10
  142. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -10
  143. data/spec/mongoid/criteria/queryable/options_spec.rb +1 -1
  144. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +419 -2
  145. data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -1
  146. data/spec/mongoid/criteria/queryable/selectable_where_spec.rb +0 -235
  147. data/spec/mongoid/criteria/queryable/selector_spec.rb +3 -76
  148. data/spec/mongoid/criteria/queryable/storable_spec.rb +0 -72
  149. data/spec/mongoid/criteria_projection_spec.rb +1 -4
  150. data/spec/mongoid/criteria_spec.rb +5 -11
  151. data/spec/mongoid/document_spec.rb +0 -27
  152. data/spec/mongoid/errors/readonly_document_spec.rb +2 -2
  153. data/spec/mongoid/extensions/hash_spec.rb +3 -239
  154. data/spec/mongoid/extensions/time_spec.rb +8 -43
  155. data/spec/mongoid/extensions/time_with_zone_spec.rb +7 -52
  156. data/spec/mongoid/fields/localized_spec.rb +46 -28
  157. data/spec/mongoid/fields_spec.rb +136 -77
  158. data/spec/mongoid/findable_spec.rb +391 -34
  159. data/spec/mongoid/indexable_spec.rb +16 -10
  160. data/spec/mongoid/interceptable_spec.rb +153 -442
  161. data/spec/mongoid/interceptable_spec_models.rb +111 -51
  162. data/spec/mongoid/persistable/deletable_spec.rb +26 -6
  163. data/spec/mongoid/persistable/destroyable_spec.rb +26 -6
  164. data/spec/mongoid/persistable/incrementable_spec.rb +37 -0
  165. data/spec/mongoid/persistable/logical_spec.rb +37 -0
  166. data/spec/mongoid/persistable/poppable_spec.rb +36 -0
  167. data/spec/mongoid/persistable/pullable_spec.rb +72 -0
  168. data/spec/mongoid/persistable/pushable_spec.rb +72 -0
  169. data/spec/mongoid/persistable/renamable_spec.rb +36 -0
  170. data/spec/mongoid/persistable/savable_spec.rb +96 -0
  171. data/spec/mongoid/persistable/settable_spec.rb +37 -0
  172. data/spec/mongoid/persistable/unsettable_spec.rb +36 -0
  173. data/spec/mongoid/persistable/updatable_spec.rb +20 -28
  174. data/spec/mongoid/persistable/upsertable_spec.rb +80 -6
  175. data/spec/mongoid/persistence_context_spec.rb +7 -57
  176. data/spec/mongoid/query_cache_spec.rb +56 -61
  177. data/spec/mongoid/reloadable_spec.rb +24 -28
  178. data/spec/mongoid/scopable_spec.rb +70 -0
  179. data/spec/mongoid/serializable_spec.rb +23 -44
  180. data/spec/mongoid/stateful_spec.rb +122 -8
  181. data/spec/mongoid/tasks/database_rake_spec.rb +74 -0
  182. data/spec/mongoid/tasks/database_spec.rb +127 -0
  183. data/spec/mongoid/timestamps/created_spec.rb +0 -23
  184. data/spec/mongoid/timestamps_spec.rb +9 -11
  185. data/spec/mongoid/touchable_spec.rb +277 -5
  186. data/spec/mongoid/touchable_spec_models.rb +3 -1
  187. data/spec/mongoid/traversable_spec.rb +9 -24
  188. data/spec/mongoid/validatable/associated_spec.rb +34 -27
  189. data/spec/mongoid/validatable/numericality_spec.rb +0 -16
  190. data/spec/mongoid/validatable/uniqueness_spec.rb +2 -3
  191. data/spec/mongoid_spec.rb +36 -10
  192. data/spec/shared/lib/mrss/docker_runner.rb +1 -1
  193. data/spec/shared/lib/mrss/server_version_registry.rb +1 -1
  194. data/spec/shared/lib/mrss/spec_organizer.rb +3 -32
  195. data/spec/shared/share/Dockerfile.erb +85 -15
  196. data/spec/shared/shlib/distro.sh +0 -10
  197. data/spec/shared/shlib/server.sh +27 -34
  198. data/spec/shared/shlib/set_env.sh +68 -9
  199. data/spec/spec_helper.rb +5 -0
  200. data/spec/support/expectations.rb +17 -20
  201. data/spec/support/immutable_ids.rb +118 -0
  202. data/spec/support/macros.rb +47 -15
  203. data/spec/support/models/artist.rb +0 -1
  204. data/spec/support/models/band.rb +1 -0
  205. data/spec/support/models/building.rb +2 -0
  206. data/spec/support/models/name.rb +0 -10
  207. data/spec/support/models/person.rb +0 -1
  208. data/spec/support/models/product.rb +1 -0
  209. data.tar.gz.sig +0 -0
  210. metadata +715 -671
  211. metadata.gz.sig +2 -0
  212. data/lib/mongoid/errors/in_memory_regexp_timeout.rb +0 -26
  213. data/lib/mongoid/field_readable.rb +0 -70
  214. data/lib/mongoid/matcher/regexp_budget.rb +0 -389
  215. data/lib/mongoid/validatable/numericality.rb +0 -19
  216. data/spec/integration/matcher_regexp_timeout_spec.rb +0 -215
  217. data/spec/integration/query_operator_guard_spec.rb +0 -89
  218. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -61
  219. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -61
  220. data/spec/mongoid/matcher/regexp_budget_spec.rb +0 -570
  221. data/spec/shared/CANDIDATE.md +0 -28
  222. data/spec/shared/lib/mrss/release/candidate.rb +0 -281
  223. data/spec/shared/lib/mrss/release/product_data.rb +0 -144
  224. data/spec/shared/lib/tasks/candidate.rake +0 -64
  225. data/spec/support/models/purse.rb +0 -9
@@ -7,33 +7,6 @@ require_relative './nested_spec_models'
7
7
 
8
8
  describe Mongoid::Attributes::Nested do
9
9
 
10
- # Expects the enclosing context to define `build_with_id`, taking an id
11
- # value and returning the document built with it in nested attributes.
12
- shared_examples 'rejects non-scalar ids' do |klass_name|
13
- not_found = /not found for class #{klass_name}/
14
-
15
- context 'when the id is an operator hash' do
16
- it 'raises a document not found error' do
17
- expect { build_with_id({ '$ne' => nil }) }
18
- .to raise_error(Mongoid::Errors::DocumentNotFound, not_found)
19
- end
20
- end
21
-
22
- context 'when the id is an array' do
23
- it 'raises a document not found error' do
24
- expect { build_with_id([BSON::ObjectId.new]) }
25
- .to raise_error(Mongoid::Errors::DocumentNotFound, not_found)
26
- end
27
- end
28
-
29
- context 'when the id is a malformed object id hash' do
30
- it 'raises a document not found error' do
31
- expect { build_with_id({ '$oid' => 'junk' }) }
32
- .to raise_error(Mongoid::Errors::DocumentNotFound, not_found)
33
- end
34
- end
35
- end
36
-
37
10
  describe ".accepts_nested_attributes_for" do
38
11
 
39
12
  context "when the autosave option is not defined" do
@@ -205,30 +178,6 @@ describe Mongoid::Attributes::Nested do
205
178
  end
206
179
  end
207
180
 
208
- context 'when the id in the attributes is not a scalar' do
209
- let(:person) { Person.create! }
210
-
211
- def build_with_id(id)
212
- person.update!(posts_attributes: { '0' => { id: id, title: 'Crafted' } })
213
- end
214
-
215
- context 'when the association is empty' do
216
- include_examples 'rejects non-scalar ids', 'Post'
217
- end
218
-
219
- context 'when the association is not empty' do
220
- before { person.posts.create!(title: 'Existing') }
221
-
222
- include_examples 'rejects non-scalar ids', 'Post'
223
- end
224
-
225
- context 'when allow_reparenting_via_nested_attributes is true' do
226
- config_override :allow_reparenting_via_nested_attributes, true
227
-
228
- include_examples 'rejects non-scalar ids', 'Post'
229
- end
230
- end
231
-
232
181
  context "when the relation is a references and referenced in many" do
233
182
 
234
183
  before do
@@ -252,100 +201,16 @@ describe Mongoid::Attributes::Nested do
252
201
  )
253
202
  end
254
203
 
255
- context 'when allow_reparenting_via_nested_attributes is false' do
256
- config_override :allow_reparenting_via_nested_attributes, false
257
-
258
- it 'raises a document not found error' do
259
- expect { person }.to raise_error(Mongoid::Errors::DocumentNotFound,
260
- /Document\(s\) not found for class Preference/)
261
- end
262
- end
263
-
264
- context 'when allow_reparenting_via_nested_attributes is true' do
265
- config_override :allow_reparenting_via_nested_attributes, true
266
-
267
- it 'sets the nested attributes' do
268
- expect(person.preferences.map(&:name)).to eq([preference.name])
269
- end
270
-
271
- it 'updates attributes of existing document which is added to relation' do
272
- preference_name = 'updated preference'
273
- person = Person.new(
274
- preferences_attributes: { 0 => { id: preference.id, name: preference_name } }
275
- )
276
- expect(person.preferences.map(&:name)).to eq([preference_name])
277
- end
278
-
279
- context 'when the id does not correspond to an existing document' do
280
- let(:person) do
281
- Person.new(
282
- preferences_attributes: { 0 => { id: BSON::ObjectId.new, name: 'Ghost' } }
283
- )
284
- end
285
-
286
- context 'when raise_not_found_error is true' do
287
- config_override :raise_not_found_error, true
288
-
289
- it 'raises a document not found error' do
290
- expect { person }.to raise_error(Mongoid::Errors::DocumentNotFound,
291
- /Document\(s\) not found for class Preference/)
292
- end
293
- end
294
-
295
- context 'when raise_not_found_error is false' do
296
- config_override :raise_not_found_error, false
297
-
298
- it 'raises a document not found error' do
299
- expect { person }.to raise_error(Mongoid::Errors::DocumentNotFound,
300
- /Document\(s\) not found for class Preference/)
301
- end
302
- end
303
- end
304
- end
305
-
306
- context 'when _destroy is true for a document not in the relation' do
307
- before do
308
- Person.send(:undef_method, :preferences_attributes=)
309
- Person.accepts_nested_attributes_for :preferences, allow_destroy: true
310
- end
311
-
312
- let(:person) do
313
- Person.new(
314
- preferences_attributes: { 0 => { id: preference.id, _destroy: '1' } }
315
- )
316
- end
317
-
318
- it 'does not raise UnknownAttribute' do
319
- expect { person }.not_to raise_error
320
- end
321
-
322
- it 'does not add the document to the relation' do
323
- expect(person.preferences).to be_empty
324
- end
325
- end
326
- end
327
-
328
- context 'when the id in the attributes is not a scalar' do
329
- let(:target) { Person.create! }
330
-
331
- def build_with_id(id)
332
- target.update!(preferences_attributes: { 0 => { id: id, name: 'Crafted' } })
333
- end
334
-
335
- context 'when the association is empty' do
336
- include_examples 'rejects non-scalar ids', 'Preference'
337
- end
338
-
339
- context 'when the association is not empty' do
340
- before { target.preferences.create!(name: 'Existing') }
341
-
342
- include_examples 'rejects non-scalar ids', 'Preference'
204
+ it "sets the nested attributes" do
205
+ expect(person.preferences.map(&:name)).to eq([preference.name])
343
206
  end
344
207
 
345
- context 'when allow_reparenting_via_nested_attributes is true' do
346
- config_override :allow_reparenting_via_nested_attributes, true
347
-
348
- include_examples 'rejects non-scalar ids', 'Preference'
208
+ it "updates attributes of existing document which is added to relation" do
209
+ preference_name = 'updated preference'
210
+ person = Person.new(
211
+ preferences_attributes: { 0 => { id: preference.id, name: preference_name } }
212
+ )
213
+ expect(person.preferences.map(&:name)).to eq([preference_name])
349
214
  end
350
215
  end
351
216
  end
@@ -3150,7 +3015,7 @@ describe Mongoid::Attributes::Nested do
3150
3015
  { "0" =>
3151
3016
  { "id" => BSON::ObjectId.new.to_s, "title" => "Rogue" }
3152
3017
  }
3153
- }.to raise_error(Mongoid::Errors::DocumentNotFound, /Document not found for class Post/)
3018
+ }.to raise_error(Mongoid::Errors::DocumentNotFound, /Document\(s\) not found for class Post with id\(s\)/)
3154
3019
  end
3155
3020
  end
3156
3021
  end
@@ -3184,7 +3049,7 @@ describe Mongoid::Attributes::Nested do
3184
3049
  expect {
3185
3050
  person.posts_attributes =
3186
3051
  { "foo" => { "id" => "test", "title" => "Test" } }
3187
- }.to raise_error(Mongoid::Errors::DocumentNotFound, /Document not found for class Post/)
3052
+ }.to raise_error(Mongoid::Errors::DocumentNotFound, /Document\(s\) not found for class Post with id\(s\)/)
3188
3053
  end
3189
3054
  end
3190
3055
  end
@@ -5132,63 +4997,4 @@ describe Mongoid::Attributes::Nested do
5132
4997
  end
5133
4998
  end
5134
4999
  end
5135
-
5136
- context 'when an id in nested attributes belongs to another parent' do
5137
- let(:victim) { Person.create! }
5138
- let(:attacker) { Person.create! }
5139
-
5140
- context 'when the association is a has-many' do
5141
- before do
5142
- Person.send(:undef_method, :posts_attributes=)
5143
- Person.accepts_nested_attributes_for :posts
5144
- end
5145
-
5146
- let!(:victim_post) { victim.posts.create!(title: 'Private') }
5147
-
5148
- it 'does not update the other parent\'s document' do
5149
- expect { attacker.update!(posts_attributes: { '0' => { id: victim_post.id, title: 'Overwritten' } }) }
5150
- .to raise_error(Mongoid::Errors::DocumentNotFound)
5151
- expect(victim_post.reload.title).to eq('Private')
5152
- end
5153
-
5154
- it 'does not add the other parent\'s document to the association' do
5155
- expect { attacker.update!(posts_attributes: { '0' => { id: victim_post.id, title: 'Overwritten' } }) }
5156
- .to raise_error(Mongoid::Errors::DocumentNotFound)
5157
- expect(attacker.reload.posts).to be_empty
5158
- end
5159
-
5160
- it 'does not update via a direct association assignment' do
5161
- expect { attacker.update!(posts: { '0' => { id: victim_post.id, title: 'Overwritten' } }) }
5162
- .to raise_error(Mongoid::Errors::DocumentNotFound)
5163
- expect(victim_post.reload.title).to eq('Private')
5164
- end
5165
- end
5166
-
5167
- context 'when the association is a has-and-belongs-to-many' do
5168
- before do
5169
- Person.send(:undef_method, :preferences_attributes=)
5170
- Person.accepts_nested_attributes_for :preferences
5171
- end
5172
-
5173
- let!(:victim_preference) { victim.preferences.create!(name: 'Private') }
5174
-
5175
- it 'does not update the other parent\'s document' do
5176
- expect { attacker.update!(preferences_attributes: { '0' => { id: victim_preference.id, name: 'Overwritten' } }) }
5177
- .to raise_error(Mongoid::Errors::DocumentNotFound)
5178
- expect(victim_preference.reload.name).to eq('Private')
5179
- end
5180
-
5181
- it 'does not add the other parent\'s document to the association' do
5182
- expect { attacker.update!(preferences_attributes: { '0' => { id: victim_preference.id, name: 'Overwritten' } }) }
5183
- .to raise_error(Mongoid::Errors::DocumentNotFound)
5184
- expect(attacker.reload.preferences).to be_empty
5185
- end
5186
-
5187
- it 'does not update via a direct association assignment' do
5188
- expect { attacker.update!(preferences: { '0' => { id: victim_preference.id, name: 'Overwritten' } }) }
5189
- .to raise_error(Mongoid::Errors::DocumentNotFound)
5190
- expect(victim_preference.reload.name).to eq('Private')
5191
- end
5192
- end
5193
- end
5194
5000
  end
@@ -393,11 +393,10 @@ describe Mongoid::Attributes do
393
393
  end
394
394
 
395
395
  context "when reloaded" do
396
+ config_override :raise_not_found_error, false
396
397
 
397
398
  before do
398
- Mongoid.raise_not_found_error = false
399
399
  person.reload
400
- Mongoid.raise_not_found_error = true
401
400
  end
402
401
 
403
402
  it "returns the default value" do
@@ -1030,14 +1029,13 @@ describe Mongoid::Attributes do
1030
1029
  end
1031
1030
 
1032
1031
  context "when the attribute does not exist" do
1032
+ config_override :raise_not_found_error, false
1033
1033
 
1034
1034
  before do
1035
1035
  person.collection
1036
1036
  .find({ _id: person.id })
1037
1037
  .update_one({ "$unset" => { age: 1 }})
1038
- Mongoid.raise_not_found_error = false
1039
1038
  person.reload
1040
- Mongoid.raise_not_found_error = true
1041
1039
  end
1042
1040
 
1043
1041
  it "returns the default value" do
@@ -1161,14 +1159,13 @@ describe Mongoid::Attributes do
1161
1159
  end
1162
1160
 
1163
1161
  context "when the attribute does not exist" do
1162
+ config_override :raise_not_found_error, false
1164
1163
 
1165
1164
  before do
1166
1165
  person.collection
1167
1166
  .find({ _id: person.id })
1168
1167
  .update_one({ "$unset" => { age: 1 }})
1169
- Mongoid.raise_not_found_error = false
1170
1168
  person.reload
1171
- Mongoid.raise_not_found_error = true
1172
1169
  end
1173
1170
 
1174
1171
  it "returns true" do
@@ -2711,31 +2708,4 @@ describe Mongoid::Attributes do
2711
2708
  catalog.set_field.should == Set.new([ 1, 2 ])
2712
2709
  end
2713
2710
  end
2714
-
2715
- context 'when an embedded field has a capitalized store_as name' do
2716
- let(:person) { Person.new(Purse: { brand: 'Gucci' }) }
2717
-
2718
- it 'sets the value' do
2719
- expect(person.purse.brand).to eq('Gucci')
2720
- end
2721
-
2722
- it 'saves successfully' do
2723
- expect(person.save!).to eq(true)
2724
- end
2725
-
2726
- context 'when persisted' do
2727
- before do
2728
- person.save!
2729
- person.reload
2730
- end
2731
-
2732
- it 'persists the value' do
2733
- expect(person.reload.purse.brand).to eq('Gucci')
2734
- end
2735
-
2736
- it 'uses the correct key in the database' do
2737
- expect(person.collection.find(_id: person.id).first['Purse']['_id']).to eq(person.purse.id)
2738
- end
2739
- end
2740
- end
2741
2711
  end
@@ -476,6 +476,188 @@ describe Mongoid::Changeable do
476
476
  end
477
477
  end
478
478
  end
479
+
480
+ context "when including key word args" do
481
+
482
+ let(:person) { Person.new }
483
+
484
+ context "when only including from" do
485
+
486
+ context "when the object has not changed" do
487
+
488
+ it "returns false" do
489
+ expect(person.send(:attribute_changed?, :score, from: nil)).to be false
490
+ end
491
+
492
+ it "returns false using (attribute)_changed?" do
493
+ expect(person.score_changed?(from: nil)).to be false
494
+ end
495
+ end
496
+
497
+ context "when the object has changed from the wrong item" do
498
+
499
+ before do
500
+ person.score = 2
501
+ end
502
+
503
+ it "returns false" do
504
+ expect(person.send(:attribute_changed?, :score, from: 1)).to be false
505
+ end
506
+
507
+ it "returns false using (attribute)_changed?" do
508
+ expect(person.score_changed?(from: 1)).to be false
509
+ end
510
+ end
511
+
512
+ context "when the object has changed from the correct item" do
513
+
514
+ before do
515
+ person.score = 2
516
+ end
517
+
518
+ it "returns true" do
519
+ expect(person.send(:attribute_changed?, :score, from: nil)).to be true
520
+ end
521
+
522
+ it "returns true using (attribute)_changed?" do
523
+ expect(person.score_changed?(from: nil)).to be true
524
+ end
525
+ end
526
+ end
527
+
528
+ context "when only including to" do
529
+
530
+ context "when the object has not changed" do
531
+
532
+ it "returns false" do
533
+ expect(person.send(:attribute_changed?, :score, to: nil)).to be false
534
+ end
535
+
536
+ it "returns false using (attribute)_changed?" do
537
+ expect(person.score_changed?(to: nil)).to be false
538
+ end
539
+ end
540
+
541
+ context "when the object has changed to the wrong item" do
542
+
543
+ before do
544
+ person.score = 2
545
+ end
546
+
547
+ it "returns false" do
548
+ expect(person.send(:attribute_changed?, :score, to: 1)).to be false
549
+ end
550
+
551
+ it "returns false using (attribute)_changed?" do
552
+ expect(person.score_changed?(to: 1)).to be false
553
+ end
554
+ end
555
+
556
+ context "when the object has changed to the correct item" do
557
+
558
+ before do
559
+ person.score = 2
560
+ end
561
+
562
+ it "returns true" do
563
+ expect(person.send(:attribute_changed?, :score, to: 2)).to be true
564
+ end
565
+
566
+ it "returns true using (attribute)_changed?" do
567
+ expect(person.score_changed?(to: 2)).to be true
568
+ end
569
+ end
570
+ end
571
+
572
+ context "when including from and to" do
573
+
574
+ context "when the object has not changed" do
575
+
576
+ it "returns false" do
577
+ expect(person.send(:attribute_changed?, :score, from: nil, to: nil)).to be false
578
+ end
579
+
580
+ it "returns false using (attribute)_changed?" do
581
+ expect(person.score_changed?(from: nil, to: nil)).to be false
582
+ end
583
+ end
584
+
585
+ context "when only the from is correct" do
586
+
587
+ before do
588
+ person.score = 2
589
+ end
590
+
591
+ it "returns false" do
592
+ expect(person.send(:attribute_changed?, :score, from: nil, to: 3)).to be false
593
+ end
594
+
595
+ it "returns false using (attribute)_changed?" do
596
+ expect(person.score_changed?(from: nil, to: 3)).to be false
597
+ end
598
+ end
599
+
600
+ context "when only the to is correct" do
601
+
602
+ before do
603
+ person.score = 2
604
+ end
605
+
606
+ it "returns false" do
607
+ expect(person.send(:attribute_changed?, :score, from: 1, to: 2)).to be false
608
+ end
609
+
610
+ it "returns false using (attribute)_changed?" do
611
+ expect(person.score_changed?(from: 1, to: 2)).to be false
612
+ end
613
+ end
614
+
615
+ context "when the from and to are correct" do
616
+
617
+ before do
618
+ person.score = 2
619
+ end
620
+
621
+ it "returns true" do
622
+ expect(person.send(:attribute_changed?, :score, from: nil, to: 2)).to be true
623
+ end
624
+
625
+ it "returns true using (attribute)_changed?" do
626
+ expect(person.score_changed?(from: nil, to: 2)).to be true
627
+ end
628
+ end
629
+
630
+ context "when value is mongoized" do
631
+
632
+ before do
633
+ person.score = "2"
634
+ end
635
+
636
+ it "returns true with mongoized value" do
637
+ expect(person.send(:attribute_changed?, :score, from: nil, to: 2)).to be true
638
+ end
639
+
640
+ it "returns true with mongoized value using (attribute)_changed?" do
641
+ expect(person.score_changed?(from: nil, to: 2)).to be true
642
+ end
643
+ end
644
+
645
+ context "when value is mongoized" do
646
+
647
+ before do
648
+ person.score = "2"
649
+ end
650
+
651
+ it "returns false with unmongoized value" do
652
+ expect(person.send(:attribute_changed?, :score, from: nil, to: "2")).to be false
653
+ end
654
+
655
+ it "returns false with unmongoized value using (attribute)_changed?" do
656
+ expect(person.score_changed?(from: nil, to: "2")).to be false
657
+ end
658
+ end
659
+ end
660
+ end
479
661
  end
480
662
 
481
663
  describe "#attribute_changed_from_default?" do
@@ -1167,20 +1349,10 @@ describe Mongoid::Changeable do
1167
1349
 
1168
1350
  context "when the document is embedded" do
1169
1351
 
1170
- let(:person) do
1171
- Person.instantiate(title: "Grand Poobah")
1172
- end
1352
+ let(:person) { Person.create(title: "Grand Poobah") }
1353
+ let(:address) { person.addresses.create(street: "Oxford St") }
1173
1354
 
1174
- let(:address) do
1175
- Address.instantiate(street: "Oxford St")
1176
- end
1177
-
1178
- before do
1179
- person.addresses << address
1180
- person.instance_variable_set(:@new_record, false)
1181
- address.instance_variable_set(:@new_record, false)
1182
- address.street = "Bond St"
1183
- end
1355
+ before { address.street = "Bond St" }
1184
1356
 
1185
1357
  it "returns a hash of field names and new values" do
1186
1358
  expect(address.setters).to eq(
@@ -1189,17 +1361,9 @@ describe Mongoid::Changeable do
1189
1361
  end
1190
1362
 
1191
1363
  context "when the document is embedded multiple levels" do
1192
-
1193
- let(:location) do
1194
- Location.new(name: "Home")
1195
- end
1196
-
1197
- before do
1198
- location.instance_variable_set(:@new_record, false)
1199
- address.locations << location
1200
- location.name = "Work"
1201
- end
1202
-
1364
+ let(:location) { address.locations.create(name: "Home") }
1365
+ before { location.name = "Work" }
1366
+
1203
1367
  it "returns the proper hash with locations" do
1204
1368
  expect(location.setters).to eq(
1205
1369
  { "addresses.0.locations.0.name" => "Work" }
@@ -1479,6 +1643,117 @@ describe Mongoid::Changeable do
1479
1643
  end
1480
1644
  end
1481
1645
 
1646
+ describe '#attribute_before_last_save' do
1647
+ let(:person) do
1648
+ Person.create!(title: "Grand Poobah")
1649
+ end
1650
+
1651
+ before do
1652
+ person.title = "Captain Obvious"
1653
+ end
1654
+
1655
+ context "when the document has been saved" do
1656
+ before do
1657
+ person.save!
1658
+ end
1659
+
1660
+ it "returns the changes" do
1661
+ expect(person.attribute_before_last_save(:title)).to eq("Grand Poobah")
1662
+ expect(person.title_before_last_save).to eq("Grand Poobah")
1663
+ end
1664
+ end
1665
+
1666
+ context "when the document has not been saved" do
1667
+ it "returns no changes" do
1668
+ expect(person.attribute_before_last_save(:title)).to be_nil
1669
+ expect(person.title_before_last_save).to be_nil
1670
+ end
1671
+ end
1672
+ end
1673
+
1674
+ describe '#saved_change_to_attribute' do
1675
+ let(:person) do
1676
+ Person.create!(title: "Grand Poobah")
1677
+ end
1678
+
1679
+ before do
1680
+ person.title = "Captain Obvious"
1681
+ end
1682
+
1683
+ context "when the document has been saved" do
1684
+ before do
1685
+ person.save!
1686
+ end
1687
+
1688
+ it "returns the changes" do
1689
+ expect(person.saved_change_to_attribute(:title)).to eq(["Grand Poobah", "Captain Obvious"])
1690
+ expect(person.saved_change_to_title).to eq(["Grand Poobah", "Captain Obvious"])
1691
+ end
1692
+ end
1693
+
1694
+ context "when the document has not been saved" do
1695
+ it "returns changes for the previous save" do
1696
+ expect(person.saved_change_to_attribute(:title)).to eq([nil, "Grand Poobah"])
1697
+ expect(person.saved_change_to_title).to eq([nil, "Grand Poobah"])
1698
+ end
1699
+ end
1700
+ end
1701
+
1702
+ describe '#saved_change_to_attribute?' do
1703
+ context "when the document has been saved" do
1704
+ let(:person) do
1705
+ Person.create!(title: "Grand Poobah")
1706
+ end
1707
+
1708
+ before do
1709
+ person.title = "Captain Obvious"
1710
+ end
1711
+
1712
+ before do
1713
+ person.save!
1714
+ end
1715
+
1716
+ it "detects the changes" do
1717
+ expect(person.saved_change_to_attribute?(:title)).to be_truthy
1718
+ expect(person.saved_change_to_attribute?(:title, from: "Grand Poobah")).to be_truthy
1719
+ expect(person.saved_change_to_attribute?(:title, to: "Captain Obvious")).to be_truthy
1720
+ expect(person.saved_change_to_attribute?(:title, from: "Grand Poobah", to: "Captain Obvious")).to be_truthy
1721
+ expect(person.saved_change_to_title?(from: "Grand Poobah", to: "Captain Obvious")).to be_truthy
1722
+ expect(person.saved_change_to_attribute?(:age)).to be_falsey
1723
+ expect(person.saved_change_to_age?).to be_falsey
1724
+ end
1725
+ end
1726
+
1727
+ context "when the document has not been saved" do
1728
+ let(:person) do
1729
+ Person.new(title: "Grand Poobah")
1730
+ end
1731
+
1732
+ it "returns changes for the previous save" do
1733
+ expect(person.saved_change_to_attribute?(:title)).to be_falsey
1734
+ expect(person.saved_change_to_title?).to be_falsey
1735
+ end
1736
+ end
1737
+ end
1738
+
1739
+ describe '#will_save_change_to_attribute?' do
1740
+ let(:person) do
1741
+ Person.create!(title: "Grand Poobah")
1742
+ end
1743
+
1744
+ before do
1745
+ person.title = "Captain Obvious"
1746
+ end
1747
+
1748
+ it 'correctly detects changes' do
1749
+ expect(person.will_save_change_to_attribute?(:title)).to eq(true)
1750
+ expect(person.will_save_change_to_title?).to eq(true)
1751
+ expect(person.will_save_change_to_attribute?(:score)).to eq(false)
1752
+ expect(person.will_save_change_to_score?).to eq(false)
1753
+ end
1754
+
1755
+ end
1756
+
1482
1757
  context "when fields have been defined pre-dirty inclusion" do
1483
1758
 
1484
1759
  let(:document) do