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,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