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,987 +1,1057 @@
1
- require 'spec_helper'
2
-
3
- describe Mongoid::History::Trackable do
4
- before :each do
5
- class MyModel
6
- include Mongoid::Document
7
- include Mongoid::History::Trackable
8
-
9
- field :foo
10
- end
11
-
12
- class MyDynamicModel
13
- include Mongoid::Document
14
- include Mongoid::History::Trackable
15
- include Mongoid::Attributes::Dynamic unless Mongoid::Compatibility::Version.mongoid3?
16
- end
17
-
18
- class HistoryTracker
19
- include Mongoid::History::Tracker
20
- end
21
-
22
- class User
23
- include Mongoid::Document
24
- end
25
- end
26
-
27
- after :each do
28
- Object.send(:remove_const, :MyModel)
29
- Object.send(:remove_const, :MyDynamicModel)
30
- Object.send(:remove_const, :HistoryTracker)
31
- Object.send(:remove_const, :User)
32
- end
33
-
34
- let(:user) { User.create! }
35
-
36
- it 'should have #track_history' do
37
- expect(MyModel).to respond_to :track_history
38
- end
39
-
40
- describe '#track_history' do
41
- before :each do
42
- class MyModelWithNoModifier
43
- include Mongoid::Document
44
- include Mongoid::History::Trackable
45
-
46
- field :foo
47
- end
48
- end
49
-
50
- after :each do
51
- Object.send(:remove_const, :MyModelWithNoModifier)
52
- end
53
-
54
- before :each do
55
- MyModel.track_history
56
- MyModelWithNoModifier.track_history modifier_field: nil
57
- end
58
-
59
- let(:expected_option) do
60
- {
61
- on: %i[foo],
62
- except: %w[created_at updated_at],
63
- tracker_class_name: nil,
64
- modifier_field: :modifier,
65
- version_field: :version,
66
- changes_method: :changes,
67
- scope: :my_model,
68
- track_create: true,
69
- track_update: true,
70
- track_destroy: true,
71
- fields: %w[foo],
72
- relations: { embeds_one: {}, embeds_many: {} },
73
- dynamic: [],
74
- format: {}
75
- }
76
- end
77
-
78
- let(:regular_fields) { ['foo'] }
79
- let(:reserved_fields) { %w[_id version modifier_id] }
80
-
81
- it 'should have default options' do
82
- expect(MyModel.mongoid_history_options.prepared).to eq(expected_option)
83
- end
84
-
85
- it 'should define callback function #track_update' do
86
- expect(MyModel.new.private_methods.collect(&:to_sym)).to include(:track_update)
87
- end
88
-
89
- it 'should define callback function #track_create' do
90
- expect(MyModel.new.private_methods.collect(&:to_sym)).to include(:track_create)
91
- end
92
-
93
- it 'should define callback function #track_destroy' do
94
- expect(MyModel.new.private_methods.collect(&:to_sym)).to include(:track_destroy)
95
- end
96
-
97
- it 'should define #history_trackable_options' do
98
- expect(MyModel.history_trackable_options).to eq(expected_option)
99
- end
100
-
101
- describe '#modifier' do
102
- context 'modifier_field set to nil' do
103
- it 'should not have a modifier relationship' do
104
- expect(MyModelWithNoModifier.reflect_on_association(:modifier)).to be_nil
105
- end
106
- end
107
-
108
- context 'modifier_field_optional true' do
109
- before :each do
110
- class MyModelWithOptionalModifier
111
- include Mongoid::Document
112
- include Mongoid::History::Trackable
113
-
114
- field :foo
115
- end
116
- end
117
-
118
- after :each do
119
- Object.send(:remove_const, :MyModelWithOptionalModifier)
120
- end
121
-
122
- it 'marks modifier relationship optional' do
123
- MyModelWithOptionalModifier.track_history modifier_field_optional: true
124
- if Mongoid::Compatibility::Version.mongoid7_or_newer?
125
- expect(MyModelWithOptionalModifier.reflect_on_association(:modifier).options[:optional]).to be true
126
- elsif Mongoid::Compatibility::Version.mongoid6_or_newer?
127
- expect(MyModelWithOptionalModifier.reflect_on_association(:modifier)[:optional]).to be true
128
- else
129
- expect(MyModelWithOptionalModifier.reflect_on_association(:modifier)).not_to be_nil
130
- end
131
- end
132
- end
133
- end
134
-
135
- describe '#tracked_fields' do
136
- it 'should return the tracked field list' do
137
- expect(MyModel.tracked_fields).to eq(regular_fields)
138
- end
139
- end
140
-
141
- describe '#reserved_tracked_fields' do
142
- it 'should return the protected field list' do
143
- expect(MyModel.reserved_tracked_fields).to eq(reserved_fields)
144
- end
145
-
146
- it 'should not include modifier_field if not specified' do
147
- expect(MyModelWithNoModifier.reserved_tracked_fields).not_to include('modifier')
148
- end
149
- end
150
-
151
- describe '#tracked_fields_for_action' do
152
- it 'should include the reserved fields for destroy' do
153
- expect(MyModel.tracked_fields_for_action(:destroy)).to eq(regular_fields + reserved_fields)
154
- end
155
- it 'should not include the reserved fields for update' do
156
- expect(MyModel.tracked_fields_for_action(:update)).to eq(regular_fields)
157
- end
158
- it 'should not include the reserved fields for create' do
159
- expect(MyModel.tracked_fields_for_action(:create)).to eq(regular_fields)
160
- end
161
- end
162
-
163
- describe '#tracked_field?' do
164
- it 'should not include the reserved fields by default' do
165
- expect(MyModel.tracked_field?(:_id)).to be false
166
- end
167
- it 'should include the reserved fields for destroy' do
168
- expect(MyModel.tracked_field?(:_id, :destroy)).to be true
169
- end
170
- it 'should allow field aliases' do
171
- expect(MyModel.tracked_field?(:id, :destroy)).to be true
172
- end
173
-
174
- context 'when model is dynamic' do
175
- it 'should allow dynamic fields tracking' do
176
- MyDynamicModel.track_history
177
- expect(MyDynamicModel.tracked_field?(:dynamic_field, :destroy)).to be true
178
- end
179
- end
180
-
181
- unless Mongoid::Compatibility::Version.mongoid3?
182
- context 'when model is not dynamic' do
183
- it 'should not allow dynamic fields tracking' do
184
- MyModel.track_history
185
- expect(MyModel.tracked_field?(:dynamic_field, :destroy)).to be false
186
- end
187
- end
188
- end
189
-
190
- it 'allows a non-database field to be specified' do
191
- class MyNonDatabaseModel
192
- include Mongoid::Document
193
- include Mongoid::History::Trackable
194
- track_history on: ['baz']
195
- end
196
-
197
- expect(MyNonDatabaseModel.tracked_field?(:baz)).to be true
198
- end
199
- end
200
-
201
- context '#dynamic_field?' do
202
- context 'when model is dynamic' do
203
- it 'should return true' do
204
- MyDynamicModel.track_history
205
- expect(MyDynamicModel.dynamic_field?(:dynamic_field)).to be true
206
- end
207
- end
208
-
209
- unless Mongoid::Compatibility::Version.mongoid3?
210
- context 'when model is not dynamic' do
211
- it 'should return false' do
212
- MyModel.track_history
213
- expect(MyModel.dynamic_field?(:dynamic_field)).to be false
214
- end
215
- end
216
- end
217
- end
218
-
219
- describe '#field_format' do
220
- before :each do
221
- class ModelOne
222
- include Mongoid::Document
223
- include Mongoid::History::Trackable
224
-
225
- field :foo
226
- end
227
- end
228
-
229
- after :each do
230
- Object.send(:remove_const, :ModelOne)
231
- end
232
-
233
- let(:format) { '***' }
234
-
235
- before do
236
- ModelOne.track_history format: { foo: format }
237
- end
238
-
239
- context 'when field is formatted' do
240
- it 'should return the format' do
241
- expect(ModelOne.field_format(:foo)).to be format
242
- end
243
- end
244
-
245
- context 'when field is not formatted' do
246
- it 'should return nil' do
247
- expect(ModelOne.field_format(:bar)).to be_nil
248
- end
249
- end
250
- end
251
-
252
- context 'sub-model' do
253
- before :each do
254
- class MySubModel < MyModel
255
- end
256
- end
257
-
258
- after :each do
259
- Object.send(:remove_const, :MySubModel)
260
- end
261
-
262
- it 'should have default options' do
263
- expect(MyModel.mongoid_history_options.prepared).to eq(expected_option)
264
- end
265
-
266
- it 'should define #history_trackable_options' do
267
- expect(MySubModel.history_trackable_options).to eq(expected_option)
268
- end
269
- end
270
-
271
- describe '#track_history?' do
272
- shared_examples_for 'history tracking' do
273
- after do
274
- Mongoid::History.store[Mongoid::History::GLOBAL_TRACK_HISTORY_FLAG] = true
275
- Mongoid::History.store[MyModel.track_history_flag] = true
276
- end
277
-
278
- context 'when tracking is globally enabled' do
279
- it 'should be enabled on the current thread' do
280
- expect(Mongoid::History.enabled?).to eq(true)
281
- expect(MyModel.new.track_history?).to eq(true)
282
- end
283
-
284
- it 'should be disabled within disable_tracking' do
285
- MyModel.disable_tracking do
286
- expect(Mongoid::History.enabled?).to eq(true)
287
- expect(MyModel.new.track_history?).to eq(false)
288
- end
289
- end
290
-
291
- it 'should be enabled within enable_tracking' do
292
- MyModel.disable_tracking do
293
- MyModel.enable_tracking do
294
- expect(Mongoid::History.enabled?).to eq(true)
295
- expect(MyModel.new.track_history?).to eq(true)
296
- end
297
- end
298
- end
299
-
300
- it 'should still be disabled after completing a nested disable_tracking' do
301
- MyModel.disable_tracking do
302
- MyModel.disable_tracking {}
303
- expect(Mongoid::History.enabled?).to eq(true)
304
- expect(MyModel.new.track_history?).to eq(false)
305
- end
306
- end
307
-
308
- it 'should still be enabled after completing a nested enable_tracking' do
309
- MyModel.enable_tracking do
310
- MyModel.enable_tracking {}
311
- expect(Mongoid::History.enabled?).to eq(true)
312
- expect(MyModel.new.track_history?).to eq(true)
313
- end
314
- end
315
-
316
- it 'should restore the original state after completing enable_tracking' do
317
- MyModel.disable_tracking do
318
- MyModel.enable_tracking {}
319
- expect(Mongoid::History.enabled?).to eq(true)
320
- expect(MyModel.new.track_history?).to eq(false)
321
- end
322
- end
323
-
324
- it 'should be rescued if an exception occurs in disable_tracking' do
325
- begin
326
- MyModel.disable_tracking do
327
- raise 'exception'
328
- end
329
- rescue
330
- end
331
- expect(Mongoid::History.enabled?).to eq(true)
332
- expect(MyModel.new.track_history?).to eq(true)
333
- end
334
-
335
- it 'should be rescued if an exception occurs in enable_tracking' do
336
- MyModel.disable_tracking do
337
- begin
338
- MyModel.enable_tracking do
339
- raise 'exception'
340
- end
341
- rescue
342
- end
343
- expect(Mongoid::History.enabled?).to eq(true)
344
- expect(MyModel.new.track_history?).to eq(false)
345
- end
346
- end
347
-
348
- it 'should stay disabled if disable_tracking called without a block' do
349
- MyModel.disable_tracking!
350
- expect(Mongoid::History.enabled?).to eq(true)
351
- expect(MyModel.new.track_history?).to eq(false)
352
- end
353
-
354
- it 'should stay enabled if enable_tracking called without a block' do
355
- MyModel.disable_tracking do
356
- MyModel.enable_tracking!
357
- expect(Mongoid::History.enabled?).to eq(true)
358
- expect(MyModel.new.track_history?).to eq(true)
359
- end
360
- end
361
-
362
- context 'with multiple classes' do
363
- before :each do
364
- class MyModel2
365
- include Mongoid::Document
366
- include Mongoid::History::Trackable
367
-
368
- track_history
369
- end
370
- end
371
-
372
- after :each do
373
- Object.send(:remove_const, :MyModel2)
374
- end
375
-
376
- it 'should be disabled only for the class that calls disable_tracking' do
377
- MyModel.disable_tracking do
378
- expect(Mongoid::History.enabled?).to eq(true)
379
- expect(MyModel2.new.track_history?).to eq(true)
380
- end
381
- end
382
- end
383
- end
384
-
385
- context 'when changing global tracking' do
386
- it 'should be disabled by the global disablement' do
387
- Mongoid::History.disable do
388
- expect(Mongoid::History.enabled?).to eq(false)
389
- expect(MyModel.new.track_history?).to eq(false)
390
- end
391
- end
392
-
393
- it 'should be enabled by the global enablement' do
394
- Mongoid::History.disable do
395
- Mongoid::History.enable do
396
- expect(Mongoid::History.enabled?).to eq(true)
397
- expect(MyModel.new.track_history?).to eq(true)
398
- end
399
- end
400
- end
401
-
402
- it 'should restore the original state after completing enable' do
403
- Mongoid::History.disable do
404
- Mongoid::History.enable {}
405
- expect(Mongoid::History.enabled?).to eq(false)
406
- expect(MyModel.new.track_history?).to eq(false)
407
- end
408
- end
409
-
410
- it 'should still be disabled after completing a nested disable' do
411
- Mongoid::History.disable do
412
- Mongoid::History.disable {}
413
- expect(Mongoid::History.enabled?).to eq(false)
414
- expect(MyModel.new.track_history?).to eq(false)
415
- end
416
- end
417
-
418
- it 'should still be enabled after completing a nested enable' do
419
- Mongoid::History.disable do
420
- Mongoid::History.enable do
421
- Mongoid::History.enable {}
422
- expect(Mongoid::History.enabled?).to eq(true)
423
- expect(MyModel.new.track_history?).to eq(true)
424
- end
425
- end
426
- end
427
-
428
- it 'should be disabled within disable_tracking' do
429
- Mongoid::History.disable do
430
- MyModel.disable_tracking do
431
- expect(Mongoid::History.enabled?).to eq(false)
432
- expect(MyModel.new.track_history?).to eq(false)
433
- end
434
- end
435
- end
436
-
437
- it 'should be rescued if an exception occurs in disable' do
438
- Mongoid::History.disable do
439
- begin
440
- MyModel.disable_tracking do
441
- raise 'exception'
442
- end
443
- rescue
444
- end
445
- expect(Mongoid::History.enabled?).to eq(false)
446
- expect(MyModel.new.track_history?).to eq(false)
447
- end
448
- end
449
-
450
- it 'should be rescued if an exception occurs in enable' do
451
- Mongoid::History.disable do
452
- begin
453
- Mongoid::History.enable do
454
- raise 'exception'
455
- end
456
- rescue
457
- end
458
- expect(Mongoid::History.enabled?).to eq(false)
459
- expect(MyModel.new.track_history?).to eq(false)
460
- end
461
- end
462
-
463
- it 'should stay disabled if disable called without a block' do
464
- Mongoid::History.disable!
465
- expect(Mongoid::History.enabled?).to eq(false)
466
- expect(MyModel.new.track_history?).to eq(false)
467
- end
468
-
469
- it 'should stay enabled if enable called without a block' do
470
- Mongoid::History.disable do
471
- Mongoid::History.enable!
472
- expect(Mongoid::History.enabled?).to eq(true)
473
- expect(MyModel.new.track_history?).to eq(true)
474
- end
475
- end
476
-
477
- context 'with multiple classes' do
478
- before :each do
479
- class MyModel2
480
- include Mongoid::Document
481
- include Mongoid::History::Trackable
482
-
483
- track_history
484
- end
485
- end
486
-
487
- after :each do
488
- Object.send(:remove_const, :MyModel2)
489
- end
490
-
491
- it 'should be disabled for all classes' do
492
- Mongoid::History.disable do
493
- MyModel.disable_tracking do
494
- expect(Mongoid::History.enabled?).to eq(false)
495
- expect(MyModel2.new.track_history?).to eq(false)
496
- end
497
- end
498
- end
499
- end
500
- end
501
-
502
- it 'should rescue errors through both local and global tracking scopes' do
503
- begin
504
- Mongoid::History.disable do
505
- MyModel.disable_tracking do
506
- raise 'exception'
507
- end
508
- end
509
- rescue
510
- end
511
- expect(Mongoid::History.enabled?).to eq(true)
512
- expect(MyModel.new.track_history?).to eq(true)
513
- end
514
- end
515
-
516
- context 'when store is Thread' do
517
- it_behaves_like 'history tracking'
518
- end
519
-
520
- context 'when store is RequestStore' do
521
- before { stub_const('RequestStore', RequestStoreTemp) }
522
- it_behaves_like 'history tracking'
523
- end
524
- end
525
-
526
- describe ':changes_method' do
527
- it 'should be set in parent class' do
528
- expect(MyModel.history_trackable_options[:changes_method]).to eq :changes
529
- end
530
-
531
- context 'subclass' do
532
- before :each do
533
- # BUGBUG: if this is not prepared, it inherits the subclass settings
534
- MyModel.history_trackable_options
535
-
536
- class CustomTracker < MyModel
537
- field :key
538
-
539
- track_history on: :key, changes_method: :my_changes, track_create: true
540
-
541
- def my_changes
542
- changes.merge('key' => ["Save history-#{key}", "Save history-#{key}"])
543
- end
544
- end
545
- end
546
-
547
- after :each do
548
- Object.send(:remove_const, :CustomTracker)
549
- end
550
-
551
- it 'should not override in parent class' do
552
- expect(MyModel.history_trackable_options[:changes_method]).to eq :changes
553
- expect(CustomTracker.history_trackable_options[:changes_method]).to eq :my_changes
554
- end
555
-
556
- it 'should default to :changes' do
557
- m = MyModel.create!(modifier: user)
558
- expect(m).to receive(:changes).exactly(3).times.and_call_original
559
- expect(m).not_to receive(:my_changes)
560
- m.save!
561
- end
562
-
563
- context 'with another model' do
564
- before :each do
565
- class MyModel3 < MyModel
566
- track_history changes_method: :my_changes
567
-
568
- def my_changes
569
- {}
570
- end
571
- end
572
- end
573
-
574
- after :each do
575
- Object.send(:remove_const, :MyModel3)
576
- end
577
-
578
- it 'should allow an alternate method to be specified' do
579
- m = MyModel3.create!(modifier: user)
580
- expect(m).to receive(:changes).twice.and_call_original
581
- expect(m).to receive(:my_changes).once.and_call_original
582
- m.save
583
- end
584
- end
585
-
586
- it 'should allow an alternate method to be specified on object creation' do
587
- m = if Mongoid::Compatibility::Version.mongoid7_or_newer? # BUGBUG
588
- CustomTracker.create!(key: 'on object creation', modifier: user)
589
- else
590
- CustomTracker.create!(key: 'on object creation')
591
- end
592
- history_track = m.history_tracks.last
593
- expect(history_track.modified['key']).to eq('Save history-on object creation')
594
- end
595
- end
596
- end
597
- end
598
-
599
- describe '#history_settings' do
600
- before(:each) { Mongoid::History.trackable_settings = nil }
601
-
602
- before :each do
603
- class ModelOne
604
- include Mongoid::Document
605
- include Mongoid::History::Trackable
606
-
607
- store_in collection: :model_ones
608
-
609
- if Mongoid::Compatibility::Version.mongoid7_or_newer?
610
- embeds_one :emb_one
611
- embeds_many :emb_twos
612
- else
613
- embeds_one :emb_one, inverse_class_name: 'EmbOne'
614
- embeds_many :emb_twos, inverse_class_name: 'EmbTwo'
615
- end
616
- end
617
-
618
- class EmbOne
619
- include Mongoid::Document
620
- include Mongoid::History::Trackable
621
-
622
- embedded_in :model_one
623
- end
624
-
625
- class EmbTwo
626
- include Mongoid::Document
627
- include Mongoid::History::Trackable
628
-
629
- embedded_in :model_one
630
- end
631
- end
632
-
633
- after :each do
634
- Object.send(:remove_const, :ModelOne)
635
- Object.send(:remove_const, :EmbOne)
636
- Object.send(:remove_const, :EmbTwo)
637
- end
638
-
639
- let(:default_options) { { paranoia_field: 'deleted_at' } }
640
-
641
- context 'when options not passed' do
642
- before(:each) do
643
- ModelOne.history_settings
644
- EmbOne.history_settings
645
- EmbTwo.history_settings
646
- end
647
-
648
- it 'should use default options' do
649
- expect(Mongoid::History.trackable_settings[:ModelOne]).to eq(default_options)
650
- expect(Mongoid::History.trackable_settings[:EmbOne]).to eq(default_options)
651
- expect(Mongoid::History.trackable_settings[:EmbTwo]).to eq(default_options)
652
- end
653
- end
654
-
655
- context 'when extra invalid options passed' do
656
- before(:each) do
657
- ModelOne.history_settings foo: :bar
658
- EmbOne.history_settings em_foo: :em_bar
659
- EmbTwo.history_settings em_foo: :em_baz
660
- end
661
-
662
- it 'should ignore invalid options' do
663
- expect(Mongoid::History.trackable_settings[:ModelOne]).to eq(default_options)
664
- expect(Mongoid::History.trackable_settings[:EmbOne]).to eq(default_options)
665
- expect(Mongoid::History.trackable_settings[:EmbTwo]).to eq(default_options)
666
- end
667
- end
668
-
669
- context 'when valid options passed' do
670
- before(:each) do
671
- ModelOne.history_settings paranoia_field: :disabled_at
672
- EmbOne.history_settings paranoia_field: :deactivated_at
673
- EmbTwo.history_settings paranoia_field: :omitted_at
674
- end
675
-
676
- it 'should override default options' do
677
- expect(Mongoid::History.trackable_settings[:ModelOne]).to eq(paranoia_field: 'disabled_at')
678
- expect(Mongoid::History.trackable_settings[:EmbOne]).to eq(paranoia_field: 'deactivated_at')
679
- expect(Mongoid::History.trackable_settings[:EmbTwo]).to eq(paranoia_field: 'omitted_at')
680
- end
681
- end
682
-
683
- context 'when string keys' do
684
- before(:each) { ModelOne.history_settings 'paranoia_field' => 'erased_at' }
685
-
686
- it 'should convert option keys to symbols' do
687
- expect(Mongoid::History.trackable_settings[:ModelOne]).to eq(paranoia_field: 'erased_at')
688
- end
689
- end
690
-
691
- context 'when paranoia field has alias' do
692
- before :each do
693
- class ModelTwo
694
- include Mongoid::Document
695
- include Mongoid::History::Trackable
696
-
697
- field :nglt, as: :neglected_at
698
- end
699
- end
700
-
701
- after :each do
702
- Object.send(:remove_const, :ModelTwo)
703
- end
704
-
705
- before :each do
706
- ModelTwo.history_settings paranoia_field: :neglected_at
707
- end
708
-
709
- it { expect(Mongoid::History.trackable_settings[:ModelTwo]).to eq(paranoia_field: 'nglt') }
710
- end
711
- end
712
-
713
- describe '#tracker_class' do
714
- before :each do
715
- class MyTrackerClass
716
- end
717
- end
718
-
719
- after :each do
720
- Object.send(:remove_const, :MyTrackerClass)
721
- end
722
-
723
- context 'when options contain tracker_class_name' do
724
- context 'when underscored' do
725
- before { MyModel.track_history tracker_class_name: 'my_tracker_class' }
726
- it { expect(MyModel.tracker_class).to eq MyTrackerClass }
727
- end
728
-
729
- context 'when camelcased' do
730
- before { MyModel.track_history tracker_class_name: 'MyTrackerClass' }
731
- it { expect(MyModel.tracker_class).to eq MyTrackerClass }
732
- end
733
-
734
- context 'when constant' do
735
- before { MyModel.track_history tracker_class_name: MyTrackerClass }
736
- it { expect(MyModel.tracker_class).to eq MyTrackerClass }
737
- end
738
- end
739
-
740
- describe '#modified_attributes_for_update' do
741
- before :each do
742
- class ModelOne
743
- include Mongoid::Document
744
- include Mongoid::History::Trackable
745
-
746
- store_in collection: :model_ones
747
- field :foo
748
-
749
- if Mongoid::Compatibility::Version.mongoid7_or_newer?
750
- embeds_many :emb_ones
751
- else
752
- embeds_many :emb_ones, inverse_class_name: 'EmbOne'
753
- end
754
- end
755
-
756
- class EmbOne
757
- include Mongoid::Document
758
- include Mongoid::History::Trackable
759
-
760
- field :em_foo
761
- embedded_in :model_one
762
- end
763
- end
764
-
765
- after :each do
766
- Object.send(:remove_const, :ModelOne)
767
- Object.send(:remove_const, :EmbOne)
768
- end
769
-
770
- before :each do
771
- model_one.save!
772
- end
773
-
774
- let(:model_one) { ModelOne.new(foo: 'Foo') }
775
- let(:changes) { {} }
776
- subject { model_one.send(:modified_attributes_for_update) }
777
-
778
- describe 'embeds_many' do
779
- before(:each) { allow(model_one).to receive(:changes) { changes } }
780
-
781
- context 'when not paranoia' do
782
- before(:each) { ModelOne.track_history(on: :emb_ones, modifier_field_optional: true) }
783
- let(:changes) { { 'emb_ones' => [[{ 'em_foo' => 'Foo' }], [{ 'em_foo' => 'Foo-new' }]] } }
784
- it { expect(subject['emb_ones'][0]).to eq [{ 'em_foo' => 'Foo' }] }
785
- it { expect(subject['emb_ones'][1]).to eq [{ 'em_foo' => 'Foo-new' }] }
786
- end
787
-
788
- context 'when default field for paranoia' do
789
- before(:each) { ModelOne.track_history(on: :emb_ones, modifier_field_optional: true) }
790
- let(:changes) do
791
- { 'emb_ones' => [[{ 'em_foo' => 'Foo' }, { 'em_foo' => 'Foo-2', 'deleted_at' => Time.now }],
792
- [{ 'em_foo' => 'Foo-new' }, { 'em_foo' => 'Foo-2-new', 'deleted_at' => Time.now }]] }
793
- end
794
- it { expect(subject['emb_ones'][0]).to eq [{ 'em_foo' => 'Foo' }] }
795
- it { expect(subject['emb_ones'][1]).to eq [{ 'em_foo' => 'Foo-new' }] }
796
- end
797
-
798
- context 'when custom field for paranoia' do
799
- before(:each) do
800
- ModelOne.track_history on: :emb_ones, modifier_field_optional: true
801
- EmbOne.history_settings paranoia_field: :my_paranoia_field
802
- end
803
- let(:changes) do
804
- { 'emb_ones' => [[{ 'em_foo' => 'Foo', 'my_paranoia_field' => Time.now },
805
- { 'em_foo' => 'Foo-2' }],
806
- [{ 'em_foo' => 'Foo-new', 'my_paranoia_field' => Time.now },
807
- { 'em_foo' => 'Foo-2-new' }]] }
808
- end
809
- it { expect(subject['emb_ones'][0]).to eq [{ 'em_foo' => 'Foo-2' }] }
810
- it { expect(subject['emb_ones'][1]).to eq [{ 'em_foo' => 'Foo-2-new' }] }
811
- end
812
- end
813
-
814
- describe 'fields' do
815
- context 'when custom method for changes' do
816
- before(:each) do
817
- ModelOne.track_history(on: :foo, changes_method: :my_changes_method)
818
- allow(ModelOne).to receive(:dynamic_enabled?) { false }
819
- allow(model_one).to receive(:my_changes_method) { changes }
820
- end
821
-
822
- let(:changes) { { 'foo' => ['Foo', 'Foo-new'], 'bar' => ['Bar', 'Bar-new'] } }
823
- it { is_expected.to eq('foo' => ['Foo', 'Foo-new']) }
824
- end
825
- end
826
- end
827
-
828
- context 'when options not contain tracker_class_name' do
829
- before { MyModel.track_history }
830
- it { expect(MyModel.tracker_class).to eq Tracker }
831
- end
832
- end
833
-
834
- describe '#track_update' do
835
- before :each do
836
- MyModel.track_history(on: :foo, track_update: true)
837
- end
838
-
839
- let!(:m) { MyModel.create!(foo: 'bar', modifier: user) }
840
-
841
- it 'should create history' do
842
- expect { m.update_attributes!(foo: 'bar2') }.to change(Tracker, :count).by(1)
843
- end
844
-
845
- it 'should not create history when error raised' do
846
- expect(m).to receive(:update_attributes!).and_raise(StandardError)
847
- expect do
848
- expect { m.update_attributes!(foo: 'bar2') }.to raise_error(StandardError)
849
- end.to change(Tracker, :count).by(0)
850
- end
851
- end
852
-
853
- describe '#track_destroy' do
854
- before :each do
855
- MyModel.track_history(on: :foo, track_destroy: true)
856
- end
857
-
858
- let!(:m) { MyModel.create!(foo: 'bar', modifier: user) }
859
-
860
- it 'should create history' do
861
- expect { m.destroy }.to change(Tracker, :count).by(1)
862
- end
863
-
864
- it 'should not create history when error raised' do
865
- expect(m).to receive(:destroy).and_raise(StandardError)
866
- expect do
867
- expect { m.destroy }.to raise_error(StandardError)
868
- end.to change(Tracker, :count).by(0)
869
- end
870
- end
871
-
872
- describe '#track_create' do
873
- before :each do
874
- class MyModelWithNoModifier
875
- include Mongoid::Document
876
- include Mongoid::History::Trackable
877
-
878
- field :foo
879
- end
880
- end
881
-
882
- after :each do
883
- Object.send(:remove_const, :MyModelWithNoModifier)
884
- end
885
-
886
- before :each do
887
- MyModel.track_history(on: :foo, track_create: true)
888
- MyModelWithNoModifier.track_history modifier_field: nil
889
- end
890
-
891
- it 'should create history' do
892
- expect { MyModel.create!(foo: 'bar', modifier: user) }.to change(Tracker, :count).by(1)
893
- end
894
-
895
- context 'no modifier_field' do
896
- it 'should create history' do
897
- expect { MyModelWithNoModifier.create!(foo: 'bar').to change(Tracker, :count).by(1) }
898
- end
899
- end
900
-
901
- it 'should not create history when error raised' do
902
- expect(MyModel).to receive(:create!).and_raise(StandardError)
903
- expect do
904
- expect { MyModel.create!(foo: 'bar') }.to raise_error(StandardError)
905
- end.to change(Tracker, :count).by(0)
906
- end
907
- end
908
-
909
- context 'changing collection' do
910
- before :each do
911
- class Fish
912
- include Mongoid::Document
913
- include Mongoid::History::Trackable
914
-
915
- track_history on: [:species], modifier_field_optional: true
916
- store_in collection: :animals
917
-
918
- field :species
919
- end
920
- end
921
-
922
- after :each do
923
- Object.send(:remove_const, :Fish)
924
- end
925
-
926
- it 'should track history' do
927
- expect do
928
- expect { Fish.new.save! }.to_not raise_error
929
- end.to change(Tracker, :count).by(1)
930
- end
931
- end
932
-
933
- context "extending a #{described_class}" do
934
- before :each do
935
- MyModel.track_history
936
-
937
- class CustomTracker < MyModel
938
- field :key
939
-
940
- track_history on: :key, changes_method: :my_changes, track_create: true
941
-
942
- def my_changes
943
- changes.merge('key' => ["Save history-#{key}", "Save history-#{key}"])
944
- end
945
- end
946
-
947
- MyModel.history_trackable_options
948
- end
949
-
950
- after :each do
951
- Object.send(:remove_const, :CustomTracker)
952
- end
953
-
954
- it 'should not override in parent class' do
955
- expect(MyModel.history_trackable_options[:changes_method]).to eq :changes
956
- expect(CustomTracker.history_trackable_options[:changes_method]).to eq :my_changes
957
- end
958
-
959
- it 'should default to :changes' do
960
- m = MyModel.create!(modifier: user)
961
- expect(m).to receive(:changes).exactly(3).times.and_call_original
962
- expect(m).not_to receive(:my_changes)
963
- m.save!
964
- end
965
- end
966
-
967
- context "subclassing a #{described_class}" do
968
- before :each do
969
- MyModel.track_history(track_destroy: false)
970
-
971
- class MySubclassModel < MyModel
972
- end
973
- end
974
-
975
- after :each do
976
- Object.send(:remove_const, :MySubclassModel)
977
- end
978
-
979
- describe '.inherited' do
980
- it 'creates new history options for the subclass' do
981
- options = MySubclassModel.mongoid_history_options
982
- expect(options.trackable).to eq MySubclassModel
983
- expect(options.options).to eq MyModel.mongoid_history_options.options
984
- end
985
- end
986
- end
987
- end
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::History::Trackable do
4
+ before :each do
5
+ class MyModel
6
+ include Mongoid::Document
7
+ include Mongoid::History::Trackable
8
+
9
+ field :foo
10
+ end
11
+
12
+ class MyDynamicModel
13
+ include Mongoid::Document
14
+ include Mongoid::History::Trackable
15
+ include Mongoid::Attributes::Dynamic unless Mongoid::Compatibility::Version.mongoid3?
16
+ end
17
+
18
+ class MyDeeplyNestedModel
19
+ include Mongoid::Document
20
+ include Mongoid::History::Trackable
21
+
22
+ embeds_many :children, class_name: 'MyNestableModel', cascade_callbacks: true # The problem only occurs if callbacks are cascaded
23
+ accepts_nested_attributes_for :children, allow_destroy: true
24
+ track_history modifier_field: nil
25
+ end
26
+
27
+ class MyNestableModel
28
+ include Mongoid::Document
29
+ include Mongoid::History::Trackable
30
+
31
+ embedded_in :parent, class_name: 'MyDeeplyNestedModel'
32
+ embeds_many :children, class_name: 'MyNestableModel', cascade_callbacks: true
33
+ accepts_nested_attributes_for :children, allow_destroy: true
34
+ field :name, type: String
35
+ track_history modifier_field: nil
36
+ end
37
+
38
+ class HistoryTracker
39
+ include Mongoid::History::Tracker
40
+ end
41
+
42
+ class User
43
+ include Mongoid::Document
44
+ end
45
+ end
46
+
47
+ after :each do
48
+ Object.send(:remove_const, :MyModel)
49
+ Object.send(:remove_const, :MyDynamicModel)
50
+ Object.send(:remove_const, :HistoryTracker)
51
+ Object.send(:remove_const, :User)
52
+ Object.send(:remove_const, :MyDeeplyNestedModel)
53
+ Object.send(:remove_const, :MyNestableModel)
54
+ end
55
+
56
+ let(:user) { User.create! }
57
+
58
+ it 'should have #track_history' do
59
+ expect(MyModel).to respond_to :track_history
60
+ end
61
+
62
+ describe '#track_history' do
63
+ before :each do
64
+ class MyModelWithNoModifier
65
+ include Mongoid::Document
66
+ include Mongoid::History::Trackable
67
+
68
+ field :foo
69
+ end
70
+ end
71
+
72
+ after :each do
73
+ Object.send(:remove_const, :MyModelWithNoModifier)
74
+ end
75
+
76
+ before :each do
77
+ MyModel.track_history
78
+ MyModelWithNoModifier.track_history modifier_field: nil
79
+ end
80
+
81
+ let(:expected_option) do
82
+ {
83
+ on: %i[foo],
84
+ except: %w[created_at updated_at],
85
+ tracker_class_name: nil,
86
+ modifier_field: :modifier,
87
+ version_field: :version,
88
+ changes_method: :changes,
89
+ scope: :my_model,
90
+ track_create: true,
91
+ track_update: true,
92
+ track_destroy: true,
93
+ fields: %w[foo],
94
+ relations: { embeds_one: {}, embeds_many: {} },
95
+ dynamic: [],
96
+ format: {}
97
+ }
98
+ end
99
+
100
+ let(:regular_fields) { ['foo'] }
101
+ let(:reserved_fields) { %w[_id version modifier_id] }
102
+
103
+ it 'should have default options' do
104
+ expect(MyModel.mongoid_history_options.prepared).to eq(expected_option)
105
+ end
106
+
107
+ it 'should define callback function #track_update' do
108
+ expect(MyModel.new.private_methods.collect(&:to_sym)).to include(:track_update)
109
+ end
110
+
111
+ it 'should define callback function #track_create' do
112
+ expect(MyModel.new.private_methods.collect(&:to_sym)).to include(:track_create)
113
+ end
114
+
115
+ it 'should define callback function #track_destroy' do
116
+ expect(MyModel.new.private_methods.collect(&:to_sym)).to include(:track_destroy)
117
+ end
118
+
119
+ it 'should define #history_trackable_options' do
120
+ expect(MyModel.history_trackable_options).to eq(expected_option)
121
+ end
122
+
123
+ describe '#modifier' do
124
+ context 'modifier_field set to nil' do
125
+ it 'should not have a modifier relationship' do
126
+ expect(MyModelWithNoModifier.reflect_on_association(:modifier)).to be_nil
127
+ end
128
+ end
129
+
130
+ context 'modifier_field_optional true' do
131
+ before :each do
132
+ class MyModelWithOptionalModifier
133
+ include Mongoid::Document
134
+ include Mongoid::History::Trackable
135
+
136
+ field :foo
137
+ end
138
+ end
139
+
140
+ after :each do
141
+ Object.send(:remove_const, :MyModelWithOptionalModifier)
142
+ end
143
+
144
+ it 'marks modifier relationship optional' do
145
+ MyModelWithOptionalModifier.track_history modifier_field_optional: true
146
+ if Mongoid::Compatibility::Version.mongoid7_or_newer?
147
+ expect(MyModelWithOptionalModifier.reflect_on_association(:modifier).options[:optional]).to be true
148
+ elsif Mongoid::Compatibility::Version.mongoid6_or_newer?
149
+ expect(MyModelWithOptionalModifier.reflect_on_association(:modifier)[:optional]).to be true
150
+ else
151
+ expect(MyModelWithOptionalModifier.reflect_on_association(:modifier)).not_to be_nil
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+ describe '#tracked_fields' do
158
+ it 'should return the tracked field list' do
159
+ expect(MyModel.tracked_fields).to eq(regular_fields)
160
+ end
161
+ end
162
+
163
+ describe '#reserved_tracked_fields' do
164
+ it 'should return the protected field list' do
165
+ expect(MyModel.reserved_tracked_fields).to eq(reserved_fields)
166
+ end
167
+
168
+ it 'should not include modifier_field if not specified' do
169
+ expect(MyModelWithNoModifier.reserved_tracked_fields).not_to include('modifier')
170
+ end
171
+ end
172
+
173
+ describe '#tracked_fields_for_action' do
174
+ it 'should include the reserved fields for destroy' do
175
+ expect(MyModel.tracked_fields_for_action(:destroy)).to eq(regular_fields + reserved_fields)
176
+ end
177
+ it 'should not include the reserved fields for update' do
178
+ expect(MyModel.tracked_fields_for_action(:update)).to eq(regular_fields)
179
+ end
180
+ it 'should not include the reserved fields for create' do
181
+ expect(MyModel.tracked_fields_for_action(:create)).to eq(regular_fields)
182
+ end
183
+ end
184
+
185
+ describe '#tracked_field?' do
186
+ it 'should not include the reserved fields by default' do
187
+ expect(MyModel.tracked_field?(:_id)).to be false
188
+ end
189
+ it 'should include the reserved fields for destroy' do
190
+ expect(MyModel.tracked_field?(:_id, :destroy)).to be true
191
+ end
192
+ it 'should allow field aliases' do
193
+ expect(MyModel.tracked_field?(:id, :destroy)).to be true
194
+ end
195
+
196
+ context 'when model is dynamic' do
197
+ it 'should allow dynamic fields tracking' do
198
+ MyDynamicModel.track_history
199
+ expect(MyDynamicModel.tracked_field?(:dynamic_field, :destroy)).to be true
200
+ end
201
+ end
202
+
203
+ unless Mongoid::Compatibility::Version.mongoid3?
204
+ context 'when model is not dynamic' do
205
+ it 'should not allow dynamic fields tracking' do
206
+ MyModel.track_history
207
+ expect(MyModel.tracked_field?(:dynamic_field, :destroy)).to be false
208
+ end
209
+ end
210
+ end
211
+
212
+ it 'allows a non-database field to be specified' do
213
+ class MyNonDatabaseModel
214
+ include Mongoid::Document
215
+ include Mongoid::History::Trackable
216
+ track_history on: ['baz']
217
+ end
218
+
219
+ expect(MyNonDatabaseModel.tracked_field?(:baz)).to be true
220
+ end
221
+ end
222
+
223
+ context '#dynamic_field?' do
224
+ context 'when model is dynamic' do
225
+ it 'should return true' do
226
+ MyDynamicModel.track_history
227
+ expect(MyDynamicModel.dynamic_field?(:dynamic_field)).to be true
228
+ end
229
+ end
230
+
231
+ unless Mongoid::Compatibility::Version.mongoid3?
232
+ context 'when model is not dynamic' do
233
+ it 'should return false' do
234
+ MyModel.track_history
235
+ expect(MyModel.dynamic_field?(:dynamic_field)).to be false
236
+ end
237
+ end
238
+ end
239
+ end
240
+
241
+ describe '#field_format' do
242
+ before :each do
243
+ class ModelOne
244
+ include Mongoid::Document
245
+ include Mongoid::History::Trackable
246
+
247
+ field :foo
248
+ end
249
+ end
250
+
251
+ after :each do
252
+ Object.send(:remove_const, :ModelOne)
253
+ end
254
+
255
+ let(:format) { '***' }
256
+
257
+ before do
258
+ ModelOne.track_history format: { foo: format }
259
+ end
260
+
261
+ context 'when field is formatted' do
262
+ it 'should return the format' do
263
+ expect(ModelOne.field_format(:foo)).to be format
264
+ end
265
+ end
266
+
267
+ context 'when field is not formatted' do
268
+ it 'should return nil' do
269
+ expect(ModelOne.field_format(:bar)).to be_nil
270
+ end
271
+ end
272
+ end
273
+
274
+ context 'sub-model' do
275
+ before :each do
276
+ class MySubModel < MyModel
277
+ end
278
+ end
279
+
280
+ after :each do
281
+ Object.send(:remove_const, :MySubModel)
282
+ end
283
+
284
+ it 'should have default options' do
285
+ expect(MyModel.mongoid_history_options.prepared).to eq(expected_option)
286
+ end
287
+
288
+ it 'should define #history_trackable_options' do
289
+ expect(MySubModel.history_trackable_options).to eq(expected_option)
290
+ end
291
+ end
292
+
293
+ describe '#track_history?' do
294
+ shared_examples_for 'history tracking' do
295
+ after do
296
+ Mongoid::History.store[Mongoid::History::GLOBAL_TRACK_HISTORY_FLAG] = true
297
+ Mongoid::History.store[MyModel.track_history_flag] = true
298
+ end
299
+
300
+ context 'when tracking is globally enabled' do
301
+ it 'should be enabled on the current thread' do
302
+ expect(Mongoid::History.enabled?).to eq(true)
303
+ expect(MyModel.new.track_history?).to eq(true)
304
+ end
305
+
306
+ it 'should be disabled within disable_tracking' do
307
+ MyModel.disable_tracking do
308
+ expect(Mongoid::History.enabled?).to eq(true)
309
+ expect(MyModel.new.track_history?).to eq(false)
310
+ end
311
+ end
312
+
313
+ it 'should be enabled within enable_tracking' do
314
+ MyModel.disable_tracking do
315
+ MyModel.enable_tracking do
316
+ expect(Mongoid::History.enabled?).to eq(true)
317
+ expect(MyModel.new.track_history?).to eq(true)
318
+ end
319
+ end
320
+ end
321
+
322
+ it 'should still be disabled after completing a nested disable_tracking' do
323
+ MyModel.disable_tracking do
324
+ MyModel.disable_tracking {}
325
+ expect(Mongoid::History.enabled?).to eq(true)
326
+ expect(MyModel.new.track_history?).to eq(false)
327
+ end
328
+ end
329
+
330
+ it 'should still be enabled after completing a nested enable_tracking' do
331
+ MyModel.enable_tracking do
332
+ MyModel.enable_tracking {}
333
+ expect(Mongoid::History.enabled?).to eq(true)
334
+ expect(MyModel.new.track_history?).to eq(true)
335
+ end
336
+ end
337
+
338
+ it 'should restore the original state after completing enable_tracking' do
339
+ MyModel.disable_tracking do
340
+ MyModel.enable_tracking {}
341
+ expect(Mongoid::History.enabled?).to eq(true)
342
+ expect(MyModel.new.track_history?).to eq(false)
343
+ end
344
+ end
345
+
346
+ it 'should be rescued if an exception occurs in disable_tracking' do
347
+ ignore_errors { MyModel.disable_tracking { raise 'exception' } }
348
+ expect(Mongoid::History.enabled?).to eq(true)
349
+ expect(MyModel.new.track_history?).to eq(true)
350
+ end
351
+
352
+ it 'should be rescued if an exception occurs in enable_tracking' do
353
+ MyModel.disable_tracking do
354
+ ignore_errors { MyModel.enable_tracking { raise 'exception' } }
355
+ expect(Mongoid::History.enabled?).to eq(true)
356
+ expect(MyModel.new.track_history?).to eq(false)
357
+ end
358
+ end
359
+
360
+ it 'should stay disabled if disable_tracking called without a block' do
361
+ MyModel.disable_tracking!
362
+ expect(Mongoid::History.enabled?).to eq(true)
363
+ expect(MyModel.new.track_history?).to eq(false)
364
+ end
365
+
366
+ it 'should stay enabled if enable_tracking called without a block' do
367
+ MyModel.disable_tracking do
368
+ MyModel.enable_tracking!
369
+ expect(Mongoid::History.enabled?).to eq(true)
370
+ expect(MyModel.new.track_history?).to eq(true)
371
+ end
372
+ end
373
+
374
+ context 'with multiple classes' do
375
+ before :each do
376
+ class MyModel2
377
+ include Mongoid::Document
378
+ include Mongoid::History::Trackable
379
+
380
+ track_history
381
+ end
382
+ end
383
+
384
+ after :each do
385
+ Object.send(:remove_const, :MyModel2)
386
+ end
387
+
388
+ it 'should be disabled only for the class that calls disable_tracking' do
389
+ MyModel.disable_tracking do
390
+ expect(Mongoid::History.enabled?).to eq(true)
391
+ expect(MyModel2.new.track_history?).to eq(true)
392
+ end
393
+ end
394
+ end
395
+ end
396
+
397
+ context 'when changing global tracking' do
398
+ it 'should be disabled by the global disablement' do
399
+ Mongoid::History.disable do
400
+ expect(Mongoid::History.enabled?).to eq(false)
401
+ expect(MyModel.new.track_history?).to eq(false)
402
+ end
403
+ end
404
+
405
+ it 'should be enabled by the global enablement' do
406
+ Mongoid::History.disable do
407
+ Mongoid::History.enable do
408
+ expect(Mongoid::History.enabled?).to eq(true)
409
+ expect(MyModel.new.track_history?).to eq(true)
410
+ end
411
+ end
412
+ end
413
+
414
+ it 'should restore the original state after completing enable' do
415
+ Mongoid::History.disable do
416
+ Mongoid::History.enable {}
417
+ expect(Mongoid::History.enabled?).to eq(false)
418
+ expect(MyModel.new.track_history?).to eq(false)
419
+ end
420
+ end
421
+
422
+ it 'should still be disabled after completing a nested disable' do
423
+ Mongoid::History.disable do
424
+ Mongoid::History.disable {}
425
+ expect(Mongoid::History.enabled?).to eq(false)
426
+ expect(MyModel.new.track_history?).to eq(false)
427
+ end
428
+ end
429
+
430
+ it 'should still be enabled after completing a nested enable' do
431
+ Mongoid::History.disable do
432
+ Mongoid::History.enable do
433
+ Mongoid::History.enable {}
434
+ expect(Mongoid::History.enabled?).to eq(true)
435
+ expect(MyModel.new.track_history?).to eq(true)
436
+ end
437
+ end
438
+ end
439
+
440
+ it 'should be disabled within disable_tracking' do
441
+ Mongoid::History.disable do
442
+ MyModel.disable_tracking do
443
+ expect(Mongoid::History.enabled?).to eq(false)
444
+ expect(MyModel.new.track_history?).to eq(false)
445
+ end
446
+ end
447
+ end
448
+
449
+ it 'should be rescued if an exception occurs in disable' do
450
+ Mongoid::History.disable do
451
+ ignore_errors { MyModel.disable_tracking { raise 'exception' } }
452
+ expect(Mongoid::History.enabled?).to eq(false)
453
+ expect(MyModel.new.track_history?).to eq(false)
454
+ end
455
+ end
456
+
457
+ it 'should be rescued if an exception occurs in enable' do
458
+ Mongoid::History.disable do
459
+ ignore_errors { Mongoid::History.enable { raise 'exception' } }
460
+ expect(Mongoid::History.enabled?).to eq(false)
461
+ expect(MyModel.new.track_history?).to eq(false)
462
+ end
463
+ end
464
+
465
+ it 'should stay disabled if disable called without a block' do
466
+ Mongoid::History.disable!
467
+ expect(Mongoid::History.enabled?).to eq(false)
468
+ expect(MyModel.new.track_history?).to eq(false)
469
+ end
470
+
471
+ it 'should stay enabled if enable called without a block' do
472
+ Mongoid::History.disable do
473
+ Mongoid::History.enable!
474
+ expect(Mongoid::History.enabled?).to eq(true)
475
+ expect(MyModel.new.track_history?).to eq(true)
476
+ end
477
+ end
478
+
479
+ context 'with multiple classes' do
480
+ before :each do
481
+ class MyModel2
482
+ include Mongoid::Document
483
+ include Mongoid::History::Trackable
484
+
485
+ track_history
486
+ end
487
+ end
488
+
489
+ after :each do
490
+ Object.send(:remove_const, :MyModel2)
491
+ end
492
+
493
+ it 'should be disabled for all classes' do
494
+ Mongoid::History.disable do
495
+ MyModel.disable_tracking do
496
+ expect(Mongoid::History.enabled?).to eq(false)
497
+ expect(MyModel2.new.track_history?).to eq(false)
498
+ end
499
+ end
500
+ end
501
+ end
502
+ end
503
+
504
+ it 'should rescue errors through both local and global tracking scopes' do
505
+ ignore_errors { Mongoid::History.disable { MyModel.disable_tracking { raise 'exception' } } }
506
+ expect(Mongoid::History.enabled?).to eq(true)
507
+ expect(MyModel.new.track_history?).to eq(true)
508
+ end
509
+ end
510
+
511
+ context 'when store is Thread' do
512
+ it_behaves_like 'history tracking'
513
+ end
514
+
515
+ context 'when store is RequestStore' do
516
+ before { stub_const('RequestStore', RequestStoreTemp) }
517
+ it_behaves_like 'history tracking'
518
+ end
519
+ end
520
+
521
+ describe ':changes_method' do
522
+ it 'should be set in parent class' do
523
+ expect(MyModel.history_trackable_options[:changes_method]).to eq :changes
524
+ end
525
+
526
+ context 'subclass' do
527
+ before :each do
528
+ # BUGBUG: if this is not prepared, it inherits the subclass settings
529
+ MyModel.history_trackable_options
530
+
531
+ class CustomTracker < MyModel
532
+ field :key
533
+
534
+ track_history on: :key, changes_method: :my_changes, track_create: true
535
+
536
+ def my_changes
537
+ changes.merge('key' => ["Save history-#{key}", "Save history-#{key}"])
538
+ end
539
+ end
540
+ end
541
+
542
+ after :each do
543
+ Object.send(:remove_const, :CustomTracker)
544
+ end
545
+
546
+ it 'should not override in parent class' do
547
+ expect(MyModel.history_trackable_options[:changes_method]).to eq :changes
548
+ expect(CustomTracker.history_trackable_options[:changes_method]).to eq :my_changes
549
+ end
550
+
551
+ it 'should default to :changes' do
552
+ m = MyModel.create!(modifier: user)
553
+ expect(m).to receive(:changes).exactly(3).times.and_call_original
554
+ expect(m).not_to receive(:my_changes)
555
+ m.save!
556
+ end
557
+
558
+ context 'with another model' do
559
+ before :each do
560
+ class MyModel3 < MyModel
561
+ track_history changes_method: :my_changes
562
+
563
+ def my_changes
564
+ {}
565
+ end
566
+ end
567
+ end
568
+
569
+ after :each do
570
+ Object.send(:remove_const, :MyModel3)
571
+ end
572
+
573
+ it 'should allow an alternate method to be specified' do
574
+ m = MyModel3.create!(modifier: user)
575
+ expect(m).to receive(:changes).twice.and_call_original
576
+ expect(m).to receive(:my_changes).once.and_call_original
577
+ m.save
578
+ end
579
+ end
580
+
581
+ it 'should allow an alternate method to be specified on object creation' do
582
+ m = if Mongoid::Compatibility::Version.mongoid7_or_newer? # BUGBUG
583
+ CustomTracker.create!(key: 'on object creation', modifier: user)
584
+ else
585
+ CustomTracker.create!(key: 'on object creation')
586
+ end
587
+ history_track = m.history_tracks.last
588
+ expect(history_track.modified['key']).to eq('Save history-on object creation')
589
+ end
590
+ end
591
+ end
592
+ end
593
+
594
+ describe '#history_settings' do
595
+ before(:each) { Mongoid::History.trackable_settings = nil }
596
+
597
+ before :each do
598
+ class ModelOne
599
+ include Mongoid::Document
600
+ include Mongoid::History::Trackable
601
+
602
+ store_in collection: :model_ones
603
+
604
+ if Mongoid::Compatibility::Version.mongoid7_or_newer?
605
+ embeds_one :emb_one
606
+ embeds_many :emb_twos
607
+ else
608
+ embeds_one :emb_one, inverse_class_name: 'EmbOne'
609
+ embeds_many :emb_twos, inverse_class_name: 'EmbTwo'
610
+ end
611
+ end
612
+
613
+ class EmbOne
614
+ include Mongoid::Document
615
+ include Mongoid::History::Trackable
616
+
617
+ embedded_in :model_one
618
+ end
619
+
620
+ class EmbTwo
621
+ include Mongoid::Document
622
+ include Mongoid::History::Trackable
623
+
624
+ embedded_in :model_one
625
+ end
626
+ end
627
+
628
+ after :each do
629
+ Object.send(:remove_const, :ModelOne)
630
+ Object.send(:remove_const, :EmbOne)
631
+ Object.send(:remove_const, :EmbTwo)
632
+ end
633
+
634
+ let(:default_options) { { paranoia_field: 'deleted_at' } }
635
+
636
+ context 'when options not passed' do
637
+ before(:each) do
638
+ ModelOne.history_settings
639
+ EmbOne.history_settings
640
+ EmbTwo.history_settings
641
+ end
642
+
643
+ it 'should use default options' do
644
+ expect(Mongoid::History.trackable_settings[:ModelOne]).to eq(default_options)
645
+ expect(Mongoid::History.trackable_settings[:EmbOne]).to eq(default_options)
646
+ expect(Mongoid::History.trackable_settings[:EmbTwo]).to eq(default_options)
647
+ end
648
+ end
649
+
650
+ context 'when extra invalid options passed' do
651
+ before(:each) do
652
+ ModelOne.history_settings foo: :bar
653
+ EmbOne.history_settings em_foo: :em_bar
654
+ EmbTwo.history_settings em_foo: :em_baz
655
+ end
656
+
657
+ it 'should ignore invalid options' do
658
+ expect(Mongoid::History.trackable_settings[:ModelOne]).to eq(default_options)
659
+ expect(Mongoid::History.trackable_settings[:EmbOne]).to eq(default_options)
660
+ expect(Mongoid::History.trackable_settings[:EmbTwo]).to eq(default_options)
661
+ end
662
+ end
663
+
664
+ context 'when valid options passed' do
665
+ before(:each) do
666
+ ModelOne.history_settings paranoia_field: :disabled_at
667
+ EmbOne.history_settings paranoia_field: :deactivated_at
668
+ EmbTwo.history_settings paranoia_field: :omitted_at
669
+ end
670
+
671
+ it 'should override default options' do
672
+ expect(Mongoid::History.trackable_settings[:ModelOne]).to eq(paranoia_field: 'disabled_at')
673
+ expect(Mongoid::History.trackable_settings[:EmbOne]).to eq(paranoia_field: 'deactivated_at')
674
+ expect(Mongoid::History.trackable_settings[:EmbTwo]).to eq(paranoia_field: 'omitted_at')
675
+ end
676
+ end
677
+
678
+ context 'when string keys' do
679
+ before(:each) { ModelOne.history_settings 'paranoia_field' => 'erased_at' }
680
+
681
+ it 'should convert option keys to symbols' do
682
+ expect(Mongoid::History.trackable_settings[:ModelOne]).to eq(paranoia_field: 'erased_at')
683
+ end
684
+ end
685
+
686
+ context 'when paranoia field has alias' do
687
+ before :each do
688
+ class ModelTwo
689
+ include Mongoid::Document
690
+ include Mongoid::History::Trackable
691
+
692
+ field :nglt, as: :neglected_at
693
+ end
694
+ end
695
+
696
+ after(:each) { Object.send(:remove_const, :ModelTwo) }
697
+
698
+ before(:each) { ModelTwo.history_settings paranoia_field: :neglected_at }
699
+
700
+ it { expect(Mongoid::History.trackable_settings[:ModelTwo]).to eq(paranoia_field: 'nglt') }
701
+ end
702
+ end
703
+
704
+ describe '#tracker_class' do
705
+ before :each do
706
+ class MyTrackerClass
707
+ end
708
+ end
709
+
710
+ after(:each) { Object.send(:remove_const, :MyTrackerClass) }
711
+
712
+ context 'when options contain tracker_class_name' do
713
+ context 'when underscored' do
714
+ before { MyModel.track_history tracker_class_name: 'my_tracker_class' }
715
+ it { expect(MyModel.tracker_class).to eq MyTrackerClass }
716
+ end
717
+
718
+ context 'when camelcased' do
719
+ before { MyModel.track_history tracker_class_name: 'MyTrackerClass' }
720
+ it { expect(MyModel.tracker_class).to eq MyTrackerClass }
721
+ end
722
+
723
+ context 'when constant' do
724
+ before { MyModel.track_history tracker_class_name: MyTrackerClass }
725
+ it { expect(MyModel.tracker_class).to eq MyTrackerClass }
726
+ end
727
+ end
728
+
729
+ describe '#modified_attributes_for_update' do
730
+ before :each do
731
+ class ModelOne
732
+ include Mongoid::Document
733
+ include Mongoid::History::Trackable
734
+
735
+ store_in collection: :model_ones
736
+ field :foo
737
+
738
+ if Mongoid::Compatibility::Version.mongoid7_or_newer?
739
+ embeds_many :emb_ones
740
+ else
741
+ embeds_many :emb_ones, inverse_class_name: 'EmbOne'
742
+ end
743
+ end
744
+
745
+ class EmbOne
746
+ include Mongoid::Document
747
+ include Mongoid::History::Trackable
748
+
749
+ field :em_foo
750
+ embedded_in :model_one
751
+ end
752
+ end
753
+
754
+ after :each do
755
+ Object.send(:remove_const, :ModelOne)
756
+ Object.send(:remove_const, :EmbOne)
757
+ end
758
+
759
+ before(:each) { model_one.save! }
760
+
761
+ let(:model_one) { ModelOne.new(foo: 'Foo') }
762
+ let(:changes) { {} }
763
+ subject { model_one.send(:modified_attributes_for_update) }
764
+
765
+ describe 'embeds_many' do
766
+ before(:each) { allow(model_one).to receive(:changes) { changes } }
767
+
768
+ context 'when not paranoia' do
769
+ before(:each) { ModelOne.track_history(on: :emb_ones, modifier_field_optional: true) }
770
+ let(:changes) { { 'emb_ones' => [[{ 'em_foo' => 'Foo' }], [{ 'em_foo' => 'Foo-new' }]] } }
771
+ it { expect(subject['emb_ones'][0]).to eq [{ 'em_foo' => 'Foo' }] }
772
+ it { expect(subject['emb_ones'][1]).to eq [{ 'em_foo' => 'Foo-new' }] }
773
+ end
774
+
775
+ context 'when default field for paranoia' do
776
+ before(:each) { ModelOne.track_history(on: :emb_ones, modifier_field_optional: true) }
777
+ let(:changes) do
778
+ { 'emb_ones' => [[{ 'em_foo' => 'Foo' }, { 'em_foo' => 'Foo-2', 'deleted_at' => Time.now }],
779
+ [{ 'em_foo' => 'Foo-new' }, { 'em_foo' => 'Foo-2-new', 'deleted_at' => Time.now }]] }
780
+ end
781
+ it { expect(subject['emb_ones'][0]).to eq [{ 'em_foo' => 'Foo' }] }
782
+ it { expect(subject['emb_ones'][1]).to eq [{ 'em_foo' => 'Foo-new' }] }
783
+ end
784
+
785
+ context 'when custom field for paranoia' do
786
+ before(:each) do
787
+ ModelOne.track_history on: :emb_ones, modifier_field_optional: true
788
+ EmbOne.history_settings paranoia_field: :my_paranoia_field
789
+ end
790
+ let(:changes) do
791
+ { 'emb_ones' => [[{ 'em_foo' => 'Foo', 'my_paranoia_field' => Time.now },
792
+ { 'em_foo' => 'Foo-2' }],
793
+ [{ 'em_foo' => 'Foo-new', 'my_paranoia_field' => Time.now },
794
+ { 'em_foo' => 'Foo-2-new' }]] }
795
+ end
796
+ it { expect(subject['emb_ones'][0]).to eq [{ 'em_foo' => 'Foo-2' }] }
797
+ it { expect(subject['emb_ones'][1]).to eq [{ 'em_foo' => 'Foo-2-new' }] }
798
+ end
799
+ end
800
+
801
+ describe 'fields' do
802
+ context 'when custom method for changes' do
803
+ before(:each) do
804
+ ModelOne.track_history(on: :foo, changes_method: :my_changes_method)
805
+ allow(ModelOne).to receive(:dynamic_enabled?) { false }
806
+ allow(model_one).to receive(:my_changes_method) { changes }
807
+ end
808
+
809
+ let(:changes) { { 'foo' => ['Foo', 'Foo-new'], 'bar' => ['Bar', 'Bar-new'] } }
810
+ it { is_expected.to eq('foo' => ['Foo', 'Foo-new']) }
811
+ end
812
+ end
813
+ end
814
+
815
+ context 'when options not contain tracker_class_name' do
816
+ before { MyModel.track_history }
817
+ it { expect(MyModel.tracker_class).to eq Tracker }
818
+ end
819
+ end
820
+
821
+ describe '#track_update' do
822
+ before(:each) { MyModel.track_history(on: :foo, track_update: true) }
823
+
824
+ let!(:m) { MyModel.create!(foo: 'bar', modifier: user) }
825
+
826
+ it 'should create history' do
827
+ expect { m.update_attributes!(foo: 'bar2') }.to change(Tracker, :count).by(1)
828
+ end
829
+
830
+ it 'should not create history when error raised' do
831
+ expect(m).to receive(:update_attributes!).and_raise(StandardError)
832
+ expect do
833
+ expect { m.update_attributes!(foo: 'bar2') }.to raise_error(StandardError)
834
+ end.to change(Tracker, :count).by(0)
835
+ end
836
+ end
837
+
838
+ describe '#track_destroy' do
839
+ before(:each) { MyModel.track_history(on: :foo, track_destroy: true) }
840
+
841
+ let!(:m) { MyModel.create!(foo: 'bar', modifier: user) }
842
+
843
+ it 'should create history' do
844
+ expect { m.destroy }.to change(Tracker, :count).by(1)
845
+ end
846
+
847
+ it 'should not create history when error raised' do
848
+ expect(m).to receive(:destroy).and_raise(StandardError)
849
+ expect do
850
+ expect { m.destroy }.to raise_error(StandardError)
851
+ end.to change(Tracker, :count).by(0)
852
+ end
853
+
854
+ context 'with a deeply nested model' do
855
+ let(:m) do
856
+ MyDeeplyNestedModel.create!(
857
+ children: [
858
+ MyNestableModel.new(
859
+ name: 'grandparent',
860
+ children: [
861
+ MyNestableModel.new(name: 'parent 1', children: [MyNestableModel.new(name: 'child 1')]),
862
+ MyNestableModel.new(name: 'parent 2', children: [MyNestableModel.new(name: 'child 2')])
863
+ ]
864
+ )
865
+ ]
866
+ )
867
+ end
868
+ let(:attributes) do
869
+ {
870
+ 'children_attributes' => [
871
+ {
872
+ 'id' => m.children[0].id,
873
+ 'children_attributes' => [
874
+ { 'id' => m.children[0].children[0].id, '_destroy' => '0' },
875
+ { 'id' => m.children[0].children[1].id, '_destroy' => '1' }
876
+ ]
877
+ }
878
+ ]
879
+ }
880
+ end
881
+
882
+ subject(:updated) do
883
+ m.update_attributes attributes
884
+ m.reload
885
+ end
886
+
887
+ let(:names_of_destroyed) do
888
+ MyDeeplyNestedModel.tracker_class
889
+ .where('association_chain.id' => updated.id, 'action' => 'destroy')
890
+ .map { |track| track.original['name'] }
891
+ end
892
+
893
+ it 'does not corrupt embedded models' do
894
+ expect(updated.children[0].children.count).to eq 1 # When the problem occurs, the 2nd child will continue to be present, but will only contain the version attribute
895
+ end
896
+
897
+ it 'creates a history track for the doc explicitly destroyed' do
898
+ expect(names_of_destroyed).to include 'parent 2'
899
+ end
900
+
901
+ it 'creates a history track for the doc implicitly destroyed' do
902
+ expect(names_of_destroyed).to include 'child 2'
903
+ end
904
+ end
905
+
906
+ context 'with multiple embeds_many models' do
907
+ let(:m) do
908
+ MyDeeplyNestedModel.create!(
909
+ children: [
910
+ MyNestableModel.new(
911
+ name: 'parent',
912
+ children: [
913
+ MyNestableModel.new(name: 'child 1'),
914
+ MyNestableModel.new(name: 'child 2'),
915
+ MyNestableModel.new(name: 'child 3')
916
+ ]
917
+ )
918
+ ]
919
+ )
920
+ end
921
+
922
+ let(:attributes) do
923
+ {
924
+ 'children_attributes' => [
925
+ {
926
+ 'id' => m.children[0].id,
927
+ 'children_attributes' => [
928
+ { 'id' => m.children[0].children[0].id, '_destroy' => '0' },
929
+ { 'id' => m.children[0].children[1].id, '_destroy' => '1' },
930
+ { 'id' => m.children[0].children[2].id, '_destroy' => '1' }
931
+ ]
932
+ }
933
+ ]
934
+ }
935
+ end
936
+
937
+ subject(:updated) do
938
+ m.update_attributes attributes
939
+ m.reload
940
+ end
941
+
942
+ it 'does not corrupt the document' do
943
+ expect(updated.children[0].children.length).to eq(1)
944
+ end
945
+ end
946
+ end
947
+
948
+ describe '#track_create' do
949
+ before :each do
950
+ class MyModelWithNoModifier
951
+ include Mongoid::Document
952
+ include Mongoid::History::Trackable
953
+
954
+ field :foo
955
+ end
956
+ end
957
+
958
+ after(:each) { Object.send(:remove_const, :MyModelWithNoModifier) }
959
+
960
+ before :each do
961
+ MyModel.track_history(on: :foo, track_create: true)
962
+ MyModelWithNoModifier.track_history modifier_field: nil
963
+ end
964
+
965
+ it 'should create history' do
966
+ expect { MyModel.create!(foo: 'bar', modifier: user) }.to change(Tracker, :count).by(1)
967
+ end
968
+
969
+ context 'no modifier_field' do
970
+ it 'should create history' do
971
+ expect { MyModelWithNoModifier.create!(foo: 'bar').to change(Tracker, :count).by(1) }
972
+ end
973
+ end
974
+
975
+ it 'should not create history when error raised' do
976
+ expect(MyModel).to receive(:create!).and_raise(StandardError)
977
+ expect do
978
+ expect { MyModel.create!(foo: 'bar') }.to raise_error(StandardError)
979
+ end.to change(Tracker, :count).by(0)
980
+ end
981
+ end
982
+
983
+ context 'changing collection' do
984
+ before :each do
985
+ class Fish
986
+ include Mongoid::Document
987
+ include Mongoid::History::Trackable
988
+
989
+ track_history on: [:species], modifier_field_optional: true
990
+ store_in collection: :animals
991
+
992
+ field :species
993
+ end
994
+ end
995
+
996
+ after(:each) { Object.send(:remove_const, :Fish) }
997
+
998
+ it 'should track history' do
999
+ expect do
1000
+ expect { Fish.new.save! }.to_not raise_error
1001
+ end.to change(Tracker, :count).by(1)
1002
+ end
1003
+ end
1004
+
1005
+ context "extending a #{described_class}" do
1006
+ before :each do
1007
+ MyModel.track_history
1008
+
1009
+ class CustomTracker < MyModel
1010
+ field :key
1011
+
1012
+ track_history on: :key, changes_method: :my_changes, track_create: true
1013
+
1014
+ def my_changes
1015
+ changes.merge('key' => ["Save history-#{key}", "Save history-#{key}"])
1016
+ end
1017
+ end
1018
+
1019
+ MyModel.history_trackable_options
1020
+ end
1021
+
1022
+ after(:each) { Object.send(:remove_const, :CustomTracker) }
1023
+
1024
+ it 'should not override in parent class' do
1025
+ expect(MyModel.history_trackable_options[:changes_method]).to eq :changes
1026
+ expect(CustomTracker.history_trackable_options[:changes_method]).to eq :my_changes
1027
+ end
1028
+
1029
+ it 'should default to :changes' do
1030
+ m = MyModel.create!(modifier: user)
1031
+ expect(m).to receive(:changes).exactly(3).times.and_call_original
1032
+ expect(m).not_to receive(:my_changes)
1033
+ m.save!
1034
+ end
1035
+ end
1036
+
1037
+ context "subclassing a #{described_class}" do
1038
+ before :each do
1039
+ MyModel.track_history(track_destroy: false)
1040
+
1041
+ class MySubclassModel < MyModel
1042
+ end
1043
+ end
1044
+
1045
+ after :each do
1046
+ Object.send(:remove_const, :MySubclassModel)
1047
+ end
1048
+
1049
+ describe '.inherited' do
1050
+ it 'creates new history options for the subclass' do
1051
+ options = MySubclassModel.mongoid_history_options
1052
+ expect(options.trackable).to eq MySubclassModel
1053
+ expect(options.options).to eq MyModel.mongoid_history_options.options
1054
+ end
1055
+ end
1056
+ end
1057
+ end