mongoid-history 0.8.3 → 0.8.5

Sign up to get free protection for your applications and to get access to all the features.
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