mongoid-history 0.8.3 → 0.8.5

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -1
  3. data/.document +5 -5
  4. data/.github/workflows/test.yml +72 -0
  5. data/.gitignore +46 -46
  6. data/.rspec +2 -2
  7. data/.rubocop.yml +6 -6
  8. data/.rubocop_todo.yml +99 -99
  9. data/CHANGELOG.md +173 -163
  10. data/CONTRIBUTING.md +117 -118
  11. data/Dangerfile +1 -1
  12. data/Gemfile +49 -40
  13. data/LICENSE.txt +20 -20
  14. data/README.md +609 -608
  15. data/RELEASING.md +66 -67
  16. data/Rakefile +24 -24
  17. data/UPGRADING.md +53 -53
  18. data/lib/mongoid/history/attributes/base.rb +72 -72
  19. data/lib/mongoid/history/attributes/create.rb +45 -45
  20. data/lib/mongoid/history/attributes/destroy.rb +34 -34
  21. data/lib/mongoid/history/attributes/update.rb +104 -104
  22. data/lib/mongoid/history/options.rb +177 -177
  23. data/lib/mongoid/history/trackable.rb +588 -583
  24. data/lib/mongoid/history/tracker.rb +247 -247
  25. data/lib/mongoid/history/version.rb +5 -5
  26. data/lib/mongoid/history.rb +77 -77
  27. data/lib/mongoid-history.rb +1 -1
  28. data/mongoid-history.gemspec +25 -25
  29. data/perf/benchmark_modified_attributes_for_create.rb +65 -65
  30. data/perf/gc_suite.rb +21 -21
  31. data/spec/integration/embedded_in_polymorphic_spec.rb +112 -112
  32. data/spec/integration/integration_spec.rb +976 -976
  33. data/spec/integration/multi_relation_spec.rb +47 -47
  34. data/spec/integration/multiple_trackers_spec.rb +68 -68
  35. data/spec/integration/nested_embedded_documents_spec.rb +64 -64
  36. data/spec/integration/nested_embedded_documents_tracked_in_parent_spec.rb +124 -124
  37. data/spec/integration/nested_embedded_polymorphic_documents_spec.rb +115 -115
  38. data/spec/integration/subclasses_spec.rb +47 -47
  39. data/spec/integration/track_history_order_spec.rb +84 -84
  40. data/spec/integration/validation_failure_spec.rb +76 -76
  41. data/spec/spec_helper.rb +32 -30
  42. data/spec/support/error_helpers.rb +7 -0
  43. data/spec/support/mongoid.rb +11 -11
  44. data/spec/support/mongoid_history.rb +12 -12
  45. data/spec/unit/attributes/base_spec.rb +141 -141
  46. data/spec/unit/attributes/create_spec.rb +342 -342
  47. data/spec/unit/attributes/destroy_spec.rb +228 -228
  48. data/spec/unit/attributes/update_spec.rb +342 -342
  49. data/spec/unit/callback_options_spec.rb +165 -165
  50. data/spec/unit/embedded_methods_spec.rb +87 -87
  51. data/spec/unit/history_spec.rb +58 -58
  52. data/spec/unit/my_instance_methods_spec.rb +555 -555
  53. data/spec/unit/options_spec.rb +365 -365
  54. data/spec/unit/singleton_methods_spec.rb +406 -406
  55. data/spec/unit/store/default_store_spec.rb +11 -11
  56. data/spec/unit/store/request_store_spec.rb +13 -13
  57. data/spec/unit/trackable_spec.rb +1057 -987
  58. data/spec/unit/tracker_spec.rb +190 -190
  59. metadata +9 -7
  60. data/.travis.yml +0 -36
@@ -1,555 +1,555 @@
1
- require 'spec_helper'
2
-
3
- describe Mongoid::History::Trackable do
4
- describe 'MyInstanceMethods' do
5
- before :each do
6
- class ModelOne
7
- include Mongoid::Document
8
- include Mongoid::History::Trackable
9
-
10
- store_in collection: :model_ones
11
-
12
- field :foo
13
- field :b, as: :bar
14
-
15
- if Mongoid::Compatibility::Version.mongoid7_or_newer?
16
- embeds_one :emb_one
17
- embeds_one :emb_two, store_as: :emt
18
- embeds_many :emb_threes
19
- embeds_many :emb_fours, store_as: :emfs
20
- else
21
- embeds_one :emb_one, inverse_class_name: 'EmbOne'
22
- embeds_one :emb_two, store_as: :emt, inverse_class_name: 'EmbTwo'
23
- embeds_many :emb_threes, inverse_class_name: 'EmbThree'
24
- embeds_many :emb_fours, store_as: :emfs, inverse_class_name: 'EmbFour'
25
- end
26
- end
27
-
28
- class EmbOne
29
- include Mongoid::Document
30
- include Mongoid::History::Trackable
31
-
32
- field :f_em_foo
33
- field :fmb, as: :f_em_bar
34
-
35
- embedded_in :model_one
36
- end
37
-
38
- class EmbTwo
39
- include Mongoid::Document
40
- include Mongoid::History::Trackable
41
-
42
- field :baz
43
- embedded_in :model_one
44
- end
45
-
46
- class EmbThree
47
- include Mongoid::Document
48
- include Mongoid::History::Trackable
49
-
50
- field :f_em_foo
51
- field :fmb, as: :f_em_bar
52
-
53
- embedded_in :model_one
54
- end
55
-
56
- class EmbFour
57
- include Mongoid::Document
58
- include Mongoid::History::Trackable
59
-
60
- field :baz
61
- embedded_in :model_one
62
- end
63
- end
64
-
65
- after :each do
66
- Object.send(:remove_const, :ModelOne)
67
- Object.send(:remove_const, :EmbOne)
68
- Object.send(:remove_const, :EmbTwo)
69
- Object.send(:remove_const, :EmbThree)
70
- Object.send(:remove_const, :EmbFour)
71
- end
72
-
73
- let(:bson_class) { defined?(BSON::ObjectId) ? BSON::ObjectId : Moped::BSON::ObjectId }
74
-
75
- let(:emb_one) { EmbOne.new(f_em_foo: 'Foo', f_em_bar: 'Bar') }
76
- let(:emb_threes) { [EmbThree.new(f_em_foo: 'Foo', f_em_bar: 'Bar')] }
77
- let(:model_one) { ModelOne.new(foo: 'Foo', bar: 'Bar', emb_one: emb_one, emb_threes: emb_threes) }
78
-
79
- describe '#modified_attributes_for_create' do
80
- before :each do
81
- ModelOne.track_history modifier_field_optional: true, on: %i[foo emb_one emb_threes]
82
- end
83
-
84
- subject { model_one.send(:modified_attributes_for_create) }
85
-
86
- context 'with tracked embeds_one object' do
87
- before :each do
88
- ModelOne.track_history(modifier_field_optional: true, on: { emb_one: :f_em_foo })
89
- end
90
- it 'should include tracked attributes only' do
91
- expect(subject['emb_one'][0]).to be_nil
92
-
93
- expect(subject['emb_one'][1].keys.size).to eq 2
94
- expect(subject['emb_one'][1]['_id']).to eq emb_one._id
95
- expect(subject['emb_one'][1]['f_em_foo']).to eq 'Foo'
96
- end
97
- end
98
-
99
- context 'with untracked embeds_one object' do
100
- before :each do
101
- ModelOne.track_history(modifier_field_optional: true, on: :fields)
102
- end
103
- it 'should not include embeds_one attributes' do
104
- expect(subject['emb_one']).to be_nil
105
- end
106
- end
107
-
108
- context 'with tracked embeds_many objects' do
109
- before :each do
110
- ModelOne.track_history(modifier_field_optional: true, on: { emb_threes: :f_em_foo })
111
- end
112
- it 'should include tracked attributes only' do
113
- expect(subject['emb_threes'][0]).to be_nil
114
-
115
- expect(subject['emb_threes'][1][0].keys.count).to eq 2
116
- expect(subject['emb_threes'][1][0]['_id']).to eq emb_threes.first._id
117
- expect(subject['emb_threes'][1][0]['f_em_foo']).to eq 'Foo'
118
- end
119
- end
120
-
121
- context 'with untracked embeds_many objects' do
122
- before :each do
123
- ModelOne.track_history(modifier_field_optional: true, on: :fields)
124
- end
125
- it 'should include not tracked embeds_many attributes' do
126
- expect(subject['emb_threes']).to be_nil
127
- end
128
- end
129
-
130
- context 'when embeds_one object blank' do
131
- let(:emb_one) { nil }
132
-
133
- it 'should not include embeds_one model key' do
134
- expect(subject.keys).to_not include 'emb_one'
135
- end
136
- end
137
-
138
- describe 'embeds_one' do
139
- before :each do
140
- class Mail
141
- include Mongoid::Document
142
- include Mongoid::History::Trackable
143
-
144
- store_in collection: :mails
145
- field :provider
146
-
147
- if Mongoid::Compatibility::Version.mongoid7_or_newer?
148
- embeds_one :mail_subject
149
- else
150
- embeds_one :mail_subject, inverse_class_name: 'MailSubject'
151
- end
152
-
153
- track_history on: :mail_subject
154
- end
155
-
156
- class MailSubject
157
- include Mongoid::Document
158
-
159
- field :content
160
- embedded_in :mail
161
- end
162
- end
163
-
164
- after :each do
165
- Object.send(:remove_const, :MailSubject)
166
- Object.send(:remove_const, :Mail)
167
- end
168
-
169
- let(:mail) { Mail.new(mail_subject: mail_subject) }
170
- let(:mail_subject) { nil }
171
- subject { mail.send(:modified_attributes_for_create)['mail_subject'] }
172
-
173
- context 'when obj not built' do
174
- it { is_expected.to be_nil }
175
- end
176
-
177
- context 'when obj does not respond to paranoia_field' do
178
- let(:mail_subject) { MailSubject.new(content: 'Content') }
179
- it { is_expected.to eq [nil, { '_id' => mail_subject._id, 'content' => 'Content' }] }
180
- end
181
-
182
- context 'when obj not soft-deleted' do
183
- before :each do
184
- allow(mail_subject).to receive(:deleted_at) { nil }
185
- end
186
- let(:mail_subject) { MailSubject.new(content: 'Content') }
187
- it { is_expected.to eq [nil, { '_id' => mail_subject._id, 'content' => 'Content' }] }
188
- end
189
-
190
- context 'when obj soft-deleted' do
191
- before :each do
192
- allow(mail_subject).to receive(:deleted_at) { Time.now }
193
- end
194
- let(:mail_subject) { MailSubject.new(content: 'Content') }
195
- it { is_expected.to be_nil }
196
- end
197
- end
198
-
199
- describe 'paranoia' do
200
- before :each do
201
- class ModelParanoia
202
- include Mongoid::Document
203
- include Mongoid::History::Trackable
204
-
205
- store_in collection: :model_paranoias
206
-
207
- if Mongoid::Compatibility::Version.mongoid7_or_newer?
208
- embeds_many :emb_para_ones
209
- else
210
- embeds_many :emb_para_ones, inverse_class_name: 'EmbParaOne'
211
- end
212
- end
213
-
214
- class EmbParaOne
215
- include Mongoid::Document
216
-
217
- field :em_foo
218
- field :deleted_at
219
-
220
- embedded_in :model_paranoia
221
- end
222
- end
223
-
224
- after :each do
225
- Object.send(:remove_const, :ModelParanoia)
226
- Object.send(:remove_const, :EmbParaOne)
227
- end
228
-
229
- let(:emb_para_one) { EmbParaOne.new(em_foo: 'Em-Foo') }
230
- let(:model_paranoia) { ModelParanoia.new(emb_para_ones: [emb_para_one]) }
231
-
232
- context 'when does not respond to paranoia_field' do
233
- before :each do
234
- ModelParanoia.track_history(on: :emb_para_ones)
235
- end
236
-
237
- subject { model_paranoia.send(:modified_attributes_for_create) }
238
-
239
- it 'should include tracked embeds_many objects attributes' do
240
- expect(subject['emb_para_ones'][0]).to be_nil
241
- expect(subject['emb_para_ones'][1].size).to eq 1
242
- expect(subject['emb_para_ones'][1][0]['_id']).to be_a bson_class
243
- expect(subject['emb_para_ones'][1][0]['em_foo']).to eq 'Em-Foo'
244
- end
245
- end
246
-
247
- context 'when responds to paranoia_field' do
248
- before :each do
249
- ModelParanoia.track_history(on: :emb_para_ones)
250
- allow(emb_para_one).to receive(:deleted_at) { Time.now }
251
- allow(emb_para_one_2).to receive(:deleted_at) { nil }
252
- end
253
-
254
- let(:model_paranoia) { ModelParanoia.new(emb_para_ones: [emb_para_one, emb_para_one_2]) }
255
- let(:emb_para_one) { EmbParaOne.new(em_foo: 'Em-Foo') }
256
- let(:emb_para_one_2) { EmbParaOne.new(em_foo: 'Em-Foo-2') }
257
-
258
- subject { model_paranoia.send(:modified_attributes_for_create) }
259
-
260
- it 'should not include deleted objects attributes' do
261
- expect(subject['emb_para_ones'][0]).to be_nil
262
- expect(subject['emb_para_ones'][1]).to eq [{ '_id' => emb_para_one_2._id, 'em_foo' => 'Em-Foo-2' }]
263
- end
264
- end
265
- end
266
- end
267
-
268
- describe '#modified_attributes_for_update' do
269
- before :each do
270
- model_one.save!
271
- allow(ModelOne).to receive(:dynamic_enabled?) { false }
272
- allow(model_one).to receive(:changes) { changes }
273
- end
274
- let(:changes) { {} }
275
- subject { model_one.send(:modified_attributes_for_update) }
276
-
277
- context 'when embeds_one attributes passed in options' do
278
- before :each do
279
- ModelOne.track_history(modifier_field_optional: true, on: { emb_one: :f_em_foo })
280
- end
281
- let(:changes) { { 'emb_one' => [{ 'f_em_foo' => 'Foo', 'fmb' => 'Bar' }, { 'f_em_foo' => 'Foo-new', 'fmb' => 'Bar-new' }] } }
282
- it { expect(subject['emb_one'][0]).to eq('f_em_foo' => 'Foo') }
283
- it { expect(subject['emb_one'][1]).to eq('f_em_foo' => 'Foo-new') }
284
- end
285
-
286
- context 'when embeds_one relation passed in options' do
287
- before :each do
288
- ModelOne.track_history(modifier_field_optional: true, on: :emb_one)
289
- end
290
- let(:changes) { { 'emb_one' => [{ 'f_em_foo' => 'Foo', 'fmb' => 'Bar' }, { 'f_em_foo' => 'Foo-new', 'fmb' => 'Bar-new' }] } }
291
- it { expect(subject['emb_one'][0]).to eq('f_em_foo' => 'Foo', 'fmb' => 'Bar') }
292
- it { expect(subject['emb_one'][1]).to eq('f_em_foo' => 'Foo-new', 'fmb' => 'Bar-new') }
293
- end
294
-
295
- context 'when embeds_one relation not tracked' do
296
- before :each do
297
- ModelOne.track_history(modifier_field_optional: true, on: :fields)
298
- end
299
- let(:changes) { { 'emb_one' => [{ 'f_em_foo' => 'Foo' }, { 'f_em_foo' => 'Foo-new' }] } }
300
- it { expect(subject['emb_one']).to be_nil }
301
- end
302
-
303
- context 'when embeds_many attributes passed in options' do
304
- before :each do
305
- ModelOne.track_history(modifier_field_optional: true, on: { emb_threes: :f_em_foo })
306
- end
307
- let(:changes) { { 'emb_threes' => [[{ 'f_em_foo' => 'Foo', 'fmb' => 'Bar' }], [{ 'f_em_foo' => 'Foo-new', 'fmb' => 'Bar-new' }]] } }
308
- it { expect(subject['emb_threes']).to eq [[{ 'f_em_foo' => 'Foo' }], [{ 'f_em_foo' => 'Foo-new' }]] }
309
- end
310
-
311
- context 'when embeds_many relation passed in options' do
312
- before :each do
313
- ModelOne.track_history(modifier_field_optional: true, on: :emb_threes)
314
- end
315
- let(:changes) { { 'emb_threes' => [[{ 'f_em_foo' => 'Foo', 'fmb' => 'Bar' }], [{ 'f_em_foo' => 'Foo-new', 'fmb' => 'Bar-new' }]] } }
316
- it { expect(subject['emb_threes']).to eq [[{ 'f_em_foo' => 'Foo', 'fmb' => 'Bar' }], [{ 'f_em_foo' => 'Foo-new', 'fmb' => 'Bar-new' }]] }
317
- end
318
-
319
- context 'when embeds_many relation not tracked' do
320
- before :each do
321
- ModelOne.track_history(modifier_field_optional: true, on: :fields)
322
- end
323
- let(:changes) { { 'emb_threes' => [[{ 'f_em_foo' => 'Foo' }], [{ 'f_em_foo' => 'Foo-new' }]] } }
324
- it { expect(subject['emb_threes']).to be_nil }
325
- end
326
-
327
- context 'when field tracked' do
328
- before :each do
329
- ModelOne.track_history(modifier_field_optional: true, on: :foo)
330
- end
331
- let(:changes) { { 'foo' => ['Foo', 'Foo-new'], 'b' => ['Bar', 'Bar-new'] } }
332
- it { is_expected.to eq('foo' => ['Foo', 'Foo-new']) }
333
- end
334
-
335
- context 'when field not tracked' do
336
- before :each do
337
- ModelOne.track_history(modifier_field_optional: true, on: [])
338
- end
339
- let(:changes) { { 'foo' => ['Foo', 'Foo-new'] } }
340
- it { is_expected.to eq({}) }
341
- end
342
-
343
- describe 'embeds_one' do
344
- before :each do
345
- class Email
346
- include Mongoid::Document
347
- include Mongoid::History::Trackable
348
-
349
- store_in collection: :emails
350
- field :provider
351
-
352
- if Mongoid::Compatibility::Version.mongoid7_or_newer?
353
- embeds_one :email_subject
354
- else
355
- embeds_one :email_subject, inverse_class_name: 'EmailSubject'
356
- end
357
-
358
- track_history on: :email_subject
359
- end
360
-
361
- class EmailSubject
362
- include Mongoid::Document
363
- include Mongoid::History::Trackable
364
-
365
- field :content
366
- embedded_in :email_subject
367
- end
368
- end
369
-
370
- after :each do
371
- Object.send(:remove_const, :EmailSubject)
372
- Object.send(:remove_const, :Email)
373
- end
374
-
375
- before :each do
376
- allow(Email).to receive(:dynamic_enabled?) { false }
377
- allow(email).to receive(:changes) { changes }
378
- end
379
-
380
- let(:email) { Email.new }
381
- let(:changes) { {} }
382
- subject { email.send(:modified_attributes_for_update)['email_subject'] }
383
-
384
- context 'when paranoia_field not present' do
385
- let(:changes) { { 'email_subject' => [{ 'content' => 'Content' }, { 'content' => 'Content-new' }] } }
386
- it { is_expected.to eq [{ 'content' => 'Content' }, { 'content' => 'Content-new' }] }
387
- end
388
-
389
- context 'when older soft-deleted' do
390
- let(:changes) { { 'email_subject' => [{ 'content' => 'Content', 'deleted_at' => Time.now }, { 'content' => 'Content-new' }] } }
391
- it { is_expected.to eq [{}, { 'content' => 'Content-new' }] }
392
- end
393
-
394
- context 'when new soft-deleted' do
395
- let(:changes) { { 'email_subject' => [{ 'content' => 'Content' }, { 'content' => 'Content-new', 'deleted_at' => Time.now }] } }
396
- it { is_expected.to eq [{ 'content' => 'Content' }, {}] }
397
- end
398
-
399
- context 'when not soft-deleted' do
400
- let(:changes) do
401
- { 'email_subject' => [{ 'content' => 'Content', 'deleted_at' => nil }, { 'content' => 'Content-new', 'deleted_at' => nil }] }
402
- end
403
- it { is_expected.to eq [{ 'content' => 'Content' }, { 'content' => 'Content-new' }] }
404
- end
405
- end
406
-
407
- describe 'paranoia_field' do
408
- context 'when embeds_one has alias' do
409
- before :each do
410
- class ModelTwo
411
- include Mongoid::Document
412
- include Mongoid::History::Trackable
413
-
414
- store_in collection: :model_twos
415
-
416
- if Mongoid::Compatibility::Version.mongoid7_or_newer?
417
- embeds_one :emb_two_one
418
- else
419
- embeds_one :emb_two_one, inverse_class_name: 'EmbTwoOne'
420
- end
421
-
422
- track_history on: :emb_two_one
423
- end
424
-
425
- class EmbTwoOne
426
- include Mongoid::Document
427
- include Mongoid::History::Trackable
428
-
429
- field :foo
430
- field :cncl, as: :cancelled_at
431
-
432
- embedded_in :model_two
433
-
434
- history_settings paranoia_field: :cancelled_at
435
- end
436
- end
437
-
438
- after :each do
439
- Object.send(:remove_const, :ModelTwo)
440
- Object.send(:remove_const, :EmbTwoOne)
441
- end
442
-
443
- before :each do
444
- allow(ModelTwo).to receive(:dynamic_enabled?) { false }
445
- allow(model_two_obj).to receive(:changes) { changes }
446
- end
447
-
448
- let(:model_two_obj) { ModelTwo.new }
449
- let(:changes) { { 'emb_two_one' => [{ 'foo' => 'Foo', 'cncl' => Time.now }, { 'foo' => 'Foo-new' }] } }
450
-
451
- subject { model_two_obj.send(:modified_attributes_for_update)['emb_two_one'] }
452
- it { is_expected.to eq [{}, { 'foo' => 'Foo-new' }] }
453
- end
454
-
455
- context 'when embeds_many has alias' do
456
- before :each do
457
- class ModelTwo
458
- include Mongoid::Document
459
- include Mongoid::History::Trackable
460
-
461
- store_in collection: :model_twos
462
-
463
- if Mongoid::Compatibility::Version.mongoid7_or_newer?
464
- embeds_many :emb_two_ones
465
- else
466
- embeds_many :emb_two_ones, inverse_class_name: 'EmbTwoOne'
467
- end
468
-
469
- track_history on: :emb_two_ones
470
- end
471
-
472
- class EmbTwoOne
473
- include Mongoid::Document
474
- include Mongoid::History::Trackable
475
-
476
- field :foo
477
- field :cncl, as: :cancelled_at
478
-
479
- embedded_in :model_two
480
-
481
- history_settings paranoia_field: :cancelled_at
482
- end
483
- end
484
-
485
- after :each do
486
- Object.send(:remove_const, :ModelTwo)
487
- Object.send(:remove_const, :EmbTwoOne)
488
- end
489
-
490
- before :each do
491
- allow(ModelTwo).to receive(:dynamic_enabled?) { false }
492
- allow(model_two_obj).to receive(:changes) { changes }
493
- end
494
-
495
- let(:model_two_obj) { ModelTwo.new }
496
- let(:changes) { { 'emb_two_ones' => [[{ 'foo' => 'Foo', 'cncl' => Time.now }], [{ 'foo' => 'Foo-new' }]] } }
497
- subject { model_two_obj.send(:modified_attributes_for_update)['emb_two_ones'] }
498
- it { is_expected.to eq [[], [{ 'foo' => 'Foo-new' }]] }
499
- end
500
- end
501
- end
502
-
503
- describe '#modified_attributes_for_destroy' do
504
- before :each do
505
- allow(ModelOne).to receive(:dynamic_enabled?) { false }
506
- model_one.save!
507
- end
508
- subject { model_one.send(:modified_attributes_for_destroy) }
509
-
510
- context 'with tracked embeds_one object' do
511
- before :each do
512
- ModelOne.track_history(modifier_field_optional: true, on: { emb_one: :f_em_foo })
513
- end
514
- it 'should include tracked attributes only' do
515
- expect(subject['emb_one'][0].keys.size).to eq 2
516
- expect(subject['emb_one'][0]['_id']).to eq emb_one._id
517
- expect(subject['emb_one'][0]['f_em_foo']).to eq 'Foo'
518
-
519
- expect(subject['emb_one'][1]).to be_nil
520
- end
521
- end
522
-
523
- context 'with untracked embeds_one object' do
524
- before :each do
525
- ModelOne.track_history(modifier_field_optional: true, on: :fields)
526
- end
527
- it 'should not include embeds_one attributes' do
528
- expect(subject['emb_one']).to be_nil
529
- end
530
- end
531
-
532
- context 'with tracked embeds_many objects' do
533
- before :each do
534
- ModelOne.track_history(modifier_field_optional: true, on: { emb_threes: :f_em_foo })
535
- end
536
- it 'should include tracked attributes only' do
537
- expect(subject['emb_threes'][0][0].keys.count).to eq 2
538
- expect(subject['emb_threes'][0][0]['_id']).to eq emb_threes.first._id
539
- expect(subject['emb_threes'][0][0]['f_em_foo']).to eq 'Foo'
540
-
541
- expect(subject['emb_threes'][1]).to be_nil
542
- end
543
- end
544
-
545
- context 'with untracked embeds_many objects' do
546
- before :each do
547
- ModelOne.track_history(modifier_field_optional: true, on: :fields)
548
- end
549
- it 'should include not tracked embeds_many attributes' do
550
- expect(subject['emb_threes']).to be_nil
551
- end
552
- end
553
- end
554
- end
555
- end
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::History::Trackable do
4
+ describe 'MyInstanceMethods' do
5
+ before :each do
6
+ class ModelOne
7
+ include Mongoid::Document
8
+ include Mongoid::History::Trackable
9
+
10
+ store_in collection: :model_ones
11
+
12
+ field :foo
13
+ field :b, as: :bar
14
+
15
+ if Mongoid::Compatibility::Version.mongoid7_or_newer?
16
+ embeds_one :emb_one
17
+ embeds_one :emb_two, store_as: :emt
18
+ embeds_many :emb_threes
19
+ embeds_many :emb_fours, store_as: :emfs
20
+ else
21
+ embeds_one :emb_one, inverse_class_name: 'EmbOne'
22
+ embeds_one :emb_two, store_as: :emt, inverse_class_name: 'EmbTwo'
23
+ embeds_many :emb_threes, inverse_class_name: 'EmbThree'
24
+ embeds_many :emb_fours, store_as: :emfs, inverse_class_name: 'EmbFour'
25
+ end
26
+ end
27
+
28
+ class EmbOne
29
+ include Mongoid::Document
30
+ include Mongoid::History::Trackable
31
+
32
+ field :f_em_foo
33
+ field :fmb, as: :f_em_bar
34
+
35
+ embedded_in :model_one
36
+ end
37
+
38
+ class EmbTwo
39
+ include Mongoid::Document
40
+ include Mongoid::History::Trackable
41
+
42
+ field :baz
43
+ embedded_in :model_one
44
+ end
45
+
46
+ class EmbThree
47
+ include Mongoid::Document
48
+ include Mongoid::History::Trackable
49
+
50
+ field :f_em_foo
51
+ field :fmb, as: :f_em_bar
52
+
53
+ embedded_in :model_one
54
+ end
55
+
56
+ class EmbFour
57
+ include Mongoid::Document
58
+ include Mongoid::History::Trackable
59
+
60
+ field :baz
61
+ embedded_in :model_one
62
+ end
63
+ end
64
+
65
+ after :each do
66
+ Object.send(:remove_const, :ModelOne)
67
+ Object.send(:remove_const, :EmbOne)
68
+ Object.send(:remove_const, :EmbTwo)
69
+ Object.send(:remove_const, :EmbThree)
70
+ Object.send(:remove_const, :EmbFour)
71
+ end
72
+
73
+ let(:bson_class) { defined?(BSON::ObjectId) ? BSON::ObjectId : Moped::BSON::ObjectId }
74
+
75
+ let(:emb_one) { EmbOne.new(f_em_foo: 'Foo', f_em_bar: 'Bar') }
76
+ let(:emb_threes) { [EmbThree.new(f_em_foo: 'Foo', f_em_bar: 'Bar')] }
77
+ let(:model_one) { ModelOne.new(foo: 'Foo', bar: 'Bar', emb_one: emb_one, emb_threes: emb_threes) }
78
+
79
+ describe '#modified_attributes_for_create' do
80
+ before :each do
81
+ ModelOne.track_history modifier_field_optional: true, on: %i[foo emb_one emb_threes]
82
+ end
83
+
84
+ subject { model_one.send(:modified_attributes_for_create) }
85
+
86
+ context 'with tracked embeds_one object' do
87
+ before :each do
88
+ ModelOne.track_history(modifier_field_optional: true, on: { emb_one: :f_em_foo })
89
+ end
90
+ it 'should include tracked attributes only' do
91
+ expect(subject['emb_one'][0]).to be_nil
92
+
93
+ expect(subject['emb_one'][1].keys.size).to eq 2
94
+ expect(subject['emb_one'][1]['_id']).to eq emb_one._id
95
+ expect(subject['emb_one'][1]['f_em_foo']).to eq 'Foo'
96
+ end
97
+ end
98
+
99
+ context 'with untracked embeds_one object' do
100
+ before :each do
101
+ ModelOne.track_history(modifier_field_optional: true, on: :fields)
102
+ end
103
+ it 'should not include embeds_one attributes' do
104
+ expect(subject['emb_one']).to be_nil
105
+ end
106
+ end
107
+
108
+ context 'with tracked embeds_many objects' do
109
+ before :each do
110
+ ModelOne.track_history(modifier_field_optional: true, on: { emb_threes: :f_em_foo })
111
+ end
112
+ it 'should include tracked attributes only' do
113
+ expect(subject['emb_threes'][0]).to be_nil
114
+
115
+ expect(subject['emb_threes'][1][0].keys.count).to eq 2
116
+ expect(subject['emb_threes'][1][0]['_id']).to eq emb_threes.first._id
117
+ expect(subject['emb_threes'][1][0]['f_em_foo']).to eq 'Foo'
118
+ end
119
+ end
120
+
121
+ context 'with untracked embeds_many objects' do
122
+ before :each do
123
+ ModelOne.track_history(modifier_field_optional: true, on: :fields)
124
+ end
125
+ it 'should include not tracked embeds_many attributes' do
126
+ expect(subject['emb_threes']).to be_nil
127
+ end
128
+ end
129
+
130
+ context 'when embeds_one object blank' do
131
+ let(:emb_one) { nil }
132
+
133
+ it 'should not include embeds_one model key' do
134
+ expect(subject.keys).to_not include 'emb_one'
135
+ end
136
+ end
137
+
138
+ describe 'embeds_one' do
139
+ before :each do
140
+ class Mail
141
+ include Mongoid::Document
142
+ include Mongoid::History::Trackable
143
+
144
+ store_in collection: :mails
145
+ field :provider
146
+
147
+ if Mongoid::Compatibility::Version.mongoid7_or_newer?
148
+ embeds_one :mail_subject
149
+ else
150
+ embeds_one :mail_subject, inverse_class_name: 'MailSubject'
151
+ end
152
+
153
+ track_history on: :mail_subject
154
+ end
155
+
156
+ class MailSubject
157
+ include Mongoid::Document
158
+
159
+ field :content
160
+ embedded_in :mail
161
+ end
162
+ end
163
+
164
+ after :each do
165
+ Object.send(:remove_const, :MailSubject)
166
+ Object.send(:remove_const, :Mail)
167
+ end
168
+
169
+ let(:mail) { Mail.new(mail_subject: mail_subject) }
170
+ let(:mail_subject) { nil }
171
+ subject { mail.send(:modified_attributes_for_create)['mail_subject'] }
172
+
173
+ context 'when obj not built' do
174
+ it { is_expected.to be_nil }
175
+ end
176
+
177
+ context 'when obj does not respond to paranoia_field' do
178
+ let(:mail_subject) { MailSubject.new(content: 'Content') }
179
+ it { is_expected.to eq [nil, { '_id' => mail_subject._id, 'content' => 'Content' }] }
180
+ end
181
+
182
+ context 'when obj not soft-deleted' do
183
+ before :each do
184
+ allow(mail_subject).to receive(:deleted_at) { nil }
185
+ end
186
+ let(:mail_subject) { MailSubject.new(content: 'Content') }
187
+ it { is_expected.to eq [nil, { '_id' => mail_subject._id, 'content' => 'Content' }] }
188
+ end
189
+
190
+ context 'when obj soft-deleted' do
191
+ before :each do
192
+ allow(mail_subject).to receive(:deleted_at) { Time.now }
193
+ end
194
+ let(:mail_subject) { MailSubject.new(content: 'Content') }
195
+ it { is_expected.to be_nil }
196
+ end
197
+ end
198
+
199
+ describe 'paranoia' do
200
+ before :each do
201
+ class ModelParanoia
202
+ include Mongoid::Document
203
+ include Mongoid::History::Trackable
204
+
205
+ store_in collection: :model_paranoias
206
+
207
+ if Mongoid::Compatibility::Version.mongoid7_or_newer?
208
+ embeds_many :emb_para_ones
209
+ else
210
+ embeds_many :emb_para_ones, inverse_class_name: 'EmbParaOne'
211
+ end
212
+ end
213
+
214
+ class EmbParaOne
215
+ include Mongoid::Document
216
+
217
+ field :em_foo
218
+ field :deleted_at
219
+
220
+ embedded_in :model_paranoia
221
+ end
222
+ end
223
+
224
+ after :each do
225
+ Object.send(:remove_const, :ModelParanoia)
226
+ Object.send(:remove_const, :EmbParaOne)
227
+ end
228
+
229
+ let(:emb_para_one) { EmbParaOne.new(em_foo: 'Em-Foo') }
230
+ let(:model_paranoia) { ModelParanoia.new(emb_para_ones: [emb_para_one]) }
231
+
232
+ context 'when does not respond to paranoia_field' do
233
+ before :each do
234
+ ModelParanoia.track_history(on: :emb_para_ones)
235
+ end
236
+
237
+ subject { model_paranoia.send(:modified_attributes_for_create) }
238
+
239
+ it 'should include tracked embeds_many objects attributes' do
240
+ expect(subject['emb_para_ones'][0]).to be_nil
241
+ expect(subject['emb_para_ones'][1].size).to eq 1
242
+ expect(subject['emb_para_ones'][1][0]['_id']).to be_a bson_class
243
+ expect(subject['emb_para_ones'][1][0]['em_foo']).to eq 'Em-Foo'
244
+ end
245
+ end
246
+
247
+ context 'when responds to paranoia_field' do
248
+ before :each do
249
+ ModelParanoia.track_history(on: :emb_para_ones)
250
+ allow(emb_para_one).to receive(:deleted_at) { Time.now }
251
+ allow(emb_para_one_2).to receive(:deleted_at) { nil }
252
+ end
253
+
254
+ let(:model_paranoia) { ModelParanoia.new(emb_para_ones: [emb_para_one, emb_para_one_2]) }
255
+ let(:emb_para_one) { EmbParaOne.new(em_foo: 'Em-Foo') }
256
+ let(:emb_para_one_2) { EmbParaOne.new(em_foo: 'Em-Foo-2') }
257
+
258
+ subject { model_paranoia.send(:modified_attributes_for_create) }
259
+
260
+ it 'should not include deleted objects attributes' do
261
+ expect(subject['emb_para_ones'][0]).to be_nil
262
+ expect(subject['emb_para_ones'][1]).to eq [{ '_id' => emb_para_one_2._id, 'em_foo' => 'Em-Foo-2' }]
263
+ end
264
+ end
265
+ end
266
+ end
267
+
268
+ describe '#modified_attributes_for_update' do
269
+ before :each do
270
+ model_one.save!
271
+ allow(ModelOne).to receive(:dynamic_enabled?) { false }
272
+ allow(model_one).to receive(:changes) { changes }
273
+ end
274
+ let(:changes) { {} }
275
+ subject { model_one.send(:modified_attributes_for_update) }
276
+
277
+ context 'when embeds_one attributes passed in options' do
278
+ before :each do
279
+ ModelOne.track_history(modifier_field_optional: true, on: { emb_one: :f_em_foo })
280
+ end
281
+ let(:changes) { { 'emb_one' => [{ 'f_em_foo' => 'Foo', 'fmb' => 'Bar' }, { 'f_em_foo' => 'Foo-new', 'fmb' => 'Bar-new' }] } }
282
+ it { expect(subject['emb_one'][0]).to eq('f_em_foo' => 'Foo') }
283
+ it { expect(subject['emb_one'][1]).to eq('f_em_foo' => 'Foo-new') }
284
+ end
285
+
286
+ context 'when embeds_one relation passed in options' do
287
+ before :each do
288
+ ModelOne.track_history(modifier_field_optional: true, on: :emb_one)
289
+ end
290
+ let(:changes) { { 'emb_one' => [{ 'f_em_foo' => 'Foo', 'fmb' => 'Bar' }, { 'f_em_foo' => 'Foo-new', 'fmb' => 'Bar-new' }] } }
291
+ it { expect(subject['emb_one'][0]).to eq('f_em_foo' => 'Foo', 'fmb' => 'Bar') }
292
+ it { expect(subject['emb_one'][1]).to eq('f_em_foo' => 'Foo-new', 'fmb' => 'Bar-new') }
293
+ end
294
+
295
+ context 'when embeds_one relation not tracked' do
296
+ before :each do
297
+ ModelOne.track_history(modifier_field_optional: true, on: :fields)
298
+ end
299
+ let(:changes) { { 'emb_one' => [{ 'f_em_foo' => 'Foo' }, { 'f_em_foo' => 'Foo-new' }] } }
300
+ it { expect(subject['emb_one']).to be_nil }
301
+ end
302
+
303
+ context 'when embeds_many attributes passed in options' do
304
+ before :each do
305
+ ModelOne.track_history(modifier_field_optional: true, on: { emb_threes: :f_em_foo })
306
+ end
307
+ let(:changes) { { 'emb_threes' => [[{ 'f_em_foo' => 'Foo', 'fmb' => 'Bar' }], [{ 'f_em_foo' => 'Foo-new', 'fmb' => 'Bar-new' }]] } }
308
+ it { expect(subject['emb_threes']).to eq [[{ 'f_em_foo' => 'Foo' }], [{ 'f_em_foo' => 'Foo-new' }]] }
309
+ end
310
+
311
+ context 'when embeds_many relation passed in options' do
312
+ before :each do
313
+ ModelOne.track_history(modifier_field_optional: true, on: :emb_threes)
314
+ end
315
+ let(:changes) { { 'emb_threes' => [[{ 'f_em_foo' => 'Foo', 'fmb' => 'Bar' }], [{ 'f_em_foo' => 'Foo-new', 'fmb' => 'Bar-new' }]] } }
316
+ it { expect(subject['emb_threes']).to eq [[{ 'f_em_foo' => 'Foo', 'fmb' => 'Bar' }], [{ 'f_em_foo' => 'Foo-new', 'fmb' => 'Bar-new' }]] }
317
+ end
318
+
319
+ context 'when embeds_many relation not tracked' do
320
+ before :each do
321
+ ModelOne.track_history(modifier_field_optional: true, on: :fields)
322
+ end
323
+ let(:changes) { { 'emb_threes' => [[{ 'f_em_foo' => 'Foo' }], [{ 'f_em_foo' => 'Foo-new' }]] } }
324
+ it { expect(subject['emb_threes']).to be_nil }
325
+ end
326
+
327
+ context 'when field tracked' do
328
+ before :each do
329
+ ModelOne.track_history(modifier_field_optional: true, on: :foo)
330
+ end
331
+ let(:changes) { { 'foo' => ['Foo', 'Foo-new'], 'b' => ['Bar', 'Bar-new'] } }
332
+ it { is_expected.to eq('foo' => ['Foo', 'Foo-new']) }
333
+ end
334
+
335
+ context 'when field not tracked' do
336
+ before :each do
337
+ ModelOne.track_history(modifier_field_optional: true, on: [])
338
+ end
339
+ let(:changes) { { 'foo' => ['Foo', 'Foo-new'] } }
340
+ it { is_expected.to eq({}) }
341
+ end
342
+
343
+ describe 'embeds_one' do
344
+ before :each do
345
+ class Email
346
+ include Mongoid::Document
347
+ include Mongoid::History::Trackable
348
+
349
+ store_in collection: :emails
350
+ field :provider
351
+
352
+ if Mongoid::Compatibility::Version.mongoid7_or_newer?
353
+ embeds_one :email_subject
354
+ else
355
+ embeds_one :email_subject, inverse_class_name: 'EmailSubject'
356
+ end
357
+
358
+ track_history on: :email_subject
359
+ end
360
+
361
+ class EmailSubject
362
+ include Mongoid::Document
363
+ include Mongoid::History::Trackable
364
+
365
+ field :content
366
+ embedded_in :email_subject
367
+ end
368
+ end
369
+
370
+ after :each do
371
+ Object.send(:remove_const, :EmailSubject)
372
+ Object.send(:remove_const, :Email)
373
+ end
374
+
375
+ before :each do
376
+ allow(Email).to receive(:dynamic_enabled?) { false }
377
+ allow(email).to receive(:changes) { changes }
378
+ end
379
+
380
+ let(:email) { Email.new }
381
+ let(:changes) { {} }
382
+ subject { email.send(:modified_attributes_for_update)['email_subject'] }
383
+
384
+ context 'when paranoia_field not present' do
385
+ let(:changes) { { 'email_subject' => [{ 'content' => 'Content' }, { 'content' => 'Content-new' }] } }
386
+ it { is_expected.to eq [{ 'content' => 'Content' }, { 'content' => 'Content-new' }] }
387
+ end
388
+
389
+ context 'when older soft-deleted' do
390
+ let(:changes) { { 'email_subject' => [{ 'content' => 'Content', 'deleted_at' => Time.now }, { 'content' => 'Content-new' }] } }
391
+ it { is_expected.to eq [{}, { 'content' => 'Content-new' }] }
392
+ end
393
+
394
+ context 'when new soft-deleted' do
395
+ let(:changes) { { 'email_subject' => [{ 'content' => 'Content' }, { 'content' => 'Content-new', 'deleted_at' => Time.now }] } }
396
+ it { is_expected.to eq [{ 'content' => 'Content' }, {}] }
397
+ end
398
+
399
+ context 'when not soft-deleted' do
400
+ let(:changes) do
401
+ { 'email_subject' => [{ 'content' => 'Content', 'deleted_at' => nil }, { 'content' => 'Content-new', 'deleted_at' => nil }] }
402
+ end
403
+ it { is_expected.to eq [{ 'content' => 'Content' }, { 'content' => 'Content-new' }] }
404
+ end
405
+ end
406
+
407
+ describe 'paranoia_field' do
408
+ context 'when embeds_one has alias' do
409
+ before :each do
410
+ class ModelTwo
411
+ include Mongoid::Document
412
+ include Mongoid::History::Trackable
413
+
414
+ store_in collection: :model_twos
415
+
416
+ if Mongoid::Compatibility::Version.mongoid7_or_newer?
417
+ embeds_one :emb_two_one
418
+ else
419
+ embeds_one :emb_two_one, inverse_class_name: 'EmbTwoOne'
420
+ end
421
+
422
+ track_history on: :emb_two_one
423
+ end
424
+
425
+ class EmbTwoOne
426
+ include Mongoid::Document
427
+ include Mongoid::History::Trackable
428
+
429
+ field :foo
430
+ field :cncl, as: :cancelled_at
431
+
432
+ embedded_in :model_two
433
+
434
+ history_settings paranoia_field: :cancelled_at
435
+ end
436
+ end
437
+
438
+ after :each do
439
+ Object.send(:remove_const, :ModelTwo)
440
+ Object.send(:remove_const, :EmbTwoOne)
441
+ end
442
+
443
+ before :each do
444
+ allow(ModelTwo).to receive(:dynamic_enabled?) { false }
445
+ allow(model_two_obj).to receive(:changes) { changes }
446
+ end
447
+
448
+ let(:model_two_obj) { ModelTwo.new }
449
+ let(:changes) { { 'emb_two_one' => [{ 'foo' => 'Foo', 'cncl' => Time.now }, { 'foo' => 'Foo-new' }] } }
450
+
451
+ subject { model_two_obj.send(:modified_attributes_for_update)['emb_two_one'] }
452
+ it { is_expected.to eq [{}, { 'foo' => 'Foo-new' }] }
453
+ end
454
+
455
+ context 'when embeds_many has alias' do
456
+ before :each do
457
+ class ModelTwo
458
+ include Mongoid::Document
459
+ include Mongoid::History::Trackable
460
+
461
+ store_in collection: :model_twos
462
+
463
+ if Mongoid::Compatibility::Version.mongoid7_or_newer?
464
+ embeds_many :emb_two_ones
465
+ else
466
+ embeds_many :emb_two_ones, inverse_class_name: 'EmbTwoOne'
467
+ end
468
+
469
+ track_history on: :emb_two_ones
470
+ end
471
+
472
+ class EmbTwoOne
473
+ include Mongoid::Document
474
+ include Mongoid::History::Trackable
475
+
476
+ field :foo
477
+ field :cncl, as: :cancelled_at
478
+
479
+ embedded_in :model_two
480
+
481
+ history_settings paranoia_field: :cancelled_at
482
+ end
483
+ end
484
+
485
+ after :each do
486
+ Object.send(:remove_const, :ModelTwo)
487
+ Object.send(:remove_const, :EmbTwoOne)
488
+ end
489
+
490
+ before :each do
491
+ allow(ModelTwo).to receive(:dynamic_enabled?) { false }
492
+ allow(model_two_obj).to receive(:changes) { changes }
493
+ end
494
+
495
+ let(:model_two_obj) { ModelTwo.new }
496
+ let(:changes) { { 'emb_two_ones' => [[{ 'foo' => 'Foo', 'cncl' => Time.now }], [{ 'foo' => 'Foo-new' }]] } }
497
+ subject { model_two_obj.send(:modified_attributes_for_update)['emb_two_ones'] }
498
+ it { is_expected.to eq [[], [{ 'foo' => 'Foo-new' }]] }
499
+ end
500
+ end
501
+ end
502
+
503
+ describe '#modified_attributes_for_destroy' do
504
+ before :each do
505
+ allow(ModelOne).to receive(:dynamic_enabled?) { false }
506
+ model_one.save!
507
+ end
508
+ subject { model_one.send(:modified_attributes_for_destroy) }
509
+
510
+ context 'with tracked embeds_one object' do
511
+ before :each do
512
+ ModelOne.track_history(modifier_field_optional: true, on: { emb_one: :f_em_foo })
513
+ end
514
+ it 'should include tracked attributes only' do
515
+ expect(subject['emb_one'][0].keys.size).to eq 2
516
+ expect(subject['emb_one'][0]['_id']).to eq emb_one._id
517
+ expect(subject['emb_one'][0]['f_em_foo']).to eq 'Foo'
518
+
519
+ expect(subject['emb_one'][1]).to be_nil
520
+ end
521
+ end
522
+
523
+ context 'with untracked embeds_one object' do
524
+ before :each do
525
+ ModelOne.track_history(modifier_field_optional: true, on: :fields)
526
+ end
527
+ it 'should not include embeds_one attributes' do
528
+ expect(subject['emb_one']).to be_nil
529
+ end
530
+ end
531
+
532
+ context 'with tracked embeds_many objects' do
533
+ before :each do
534
+ ModelOne.track_history(modifier_field_optional: true, on: { emb_threes: :f_em_foo })
535
+ end
536
+ it 'should include tracked attributes only' do
537
+ expect(subject['emb_threes'][0][0].keys.count).to eq 2
538
+ expect(subject['emb_threes'][0][0]['_id']).to eq emb_threes.first._id
539
+ expect(subject['emb_threes'][0][0]['f_em_foo']).to eq 'Foo'
540
+
541
+ expect(subject['emb_threes'][1]).to be_nil
542
+ end
543
+ end
544
+
545
+ context 'with untracked embeds_many objects' do
546
+ before :each do
547
+ ModelOne.track_history(modifier_field_optional: true, on: :fields)
548
+ end
549
+ it 'should include not tracked embeds_many attributes' do
550
+ expect(subject['emb_threes']).to be_nil
551
+ end
552
+ end
553
+ end
554
+ end
555
+ end