mongoid-history 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +16 -3
- data/.travis.yml +23 -8
- data/CHANGELOG.md +20 -6
- data/Dangerfile +1 -0
- data/Gemfile +22 -1
- data/LICENSE.txt +1 -1
- data/README.md +130 -18
- data/lib/mongoid/history.rb +32 -15
- data/lib/mongoid/history/attributes/base.rb +31 -0
- data/lib/mongoid/history/attributes/create.rb +52 -0
- data/lib/mongoid/history/attributes/destroy.rb +36 -0
- data/lib/mongoid/history/attributes/update.rb +43 -0
- data/lib/mongoid/history/options.rb +153 -0
- data/lib/mongoid/history/trackable.rb +170 -72
- data/lib/mongoid/history/tracker.rb +39 -23
- data/lib/mongoid/history/version.rb +1 -1
- data/mongoid-history.gemspec +0 -8
- data/spec/integration/embedded_in_polymorphic_spec.rb +8 -0
- data/spec/integration/integration_spec.rb +4 -0
- data/spec/integration/nested_embedded_documents_spec.rb +13 -6
- data/spec/integration/nested_embedded_polymorphic_documents_spec.rb +24 -24
- data/spec/spec_helper.rb +6 -0
- data/spec/unit/attributes/base_spec.rb +54 -0
- data/spec/unit/attributes/create_spec.rb +315 -0
- data/spec/unit/attributes/destroy_spec.rb +218 -0
- data/spec/unit/attributes/update_spec.rb +210 -0
- data/spec/unit/embedded_methods_spec.rb +69 -0
- data/spec/unit/history_spec.rb +35 -0
- data/spec/unit/my_instance_methods_spec.rb +485 -0
- data/spec/unit/options_spec.rb +294 -0
- data/spec/unit/singleton_methods_spec.rb +338 -0
- data/spec/unit/store/default_store_spec.rb +11 -0
- data/spec/unit/store/request_store_spec.rb +13 -0
- data/spec/unit/trackable_spec.rb +335 -68
- data/spec/unit/tracker_spec.rb +153 -0
- metadata +31 -102
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'RequestStore' do
|
4
|
+
before { stub_const('RequestStore', RequestStoreTemp) }
|
5
|
+
|
6
|
+
describe 'Mongoid::History' do
|
7
|
+
describe '.store' do
|
8
|
+
it 'should return RequestStore' do
|
9
|
+
expect(Mongoid::History.store).to be_a Hash
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/spec/unit/trackable_spec.rb
CHANGED
@@ -34,15 +34,19 @@ describe Mongoid::History::Trackable do
|
|
34
34
|
end
|
35
35
|
before(:each) { Mongoid::History.trackable_class_options = @persisted_history_options }
|
36
36
|
let(:expected_option) do
|
37
|
-
{ on:
|
37
|
+
{ on: %i(foo),
|
38
|
+
except: %w(created_at updated_at),
|
39
|
+
tracker_class_name: nil,
|
38
40
|
modifier_field: :modifier,
|
39
41
|
version_field: :version,
|
40
42
|
changes_method: :changes,
|
41
43
|
scope: :my_model,
|
42
|
-
except: %w(created_at updated_at),
|
43
44
|
track_create: false,
|
44
45
|
track_update: true,
|
45
|
-
track_destroy: false
|
46
|
+
track_destroy: false,
|
47
|
+
fields: %w(foo),
|
48
|
+
relations: { embeds_one: {}, embeds_many: {} },
|
49
|
+
dynamic: [] }
|
46
50
|
end
|
47
51
|
let(:regular_fields) { ['foo'] }
|
48
52
|
let(:reserved_fields) { %w(_id version modifier_id) }
|
@@ -163,103 +167,128 @@ describe Mongoid::History::Trackable do
|
|
163
167
|
end
|
164
168
|
|
165
169
|
describe '#track_history?' do
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
170
|
+
shared_examples_for 'history tracking' do
|
171
|
+
context 'when tracking is globally enabled' do
|
172
|
+
it 'should be enabled on the current thread' do
|
173
|
+
expect(Mongoid::History.enabled?).to eq(true)
|
174
|
+
expect(MyModel.new.track_history?).to eq(true)
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'should be disabled within disable_tracking' do
|
178
|
+
MyModel.disable_tracking do
|
179
|
+
expect(Mongoid::History.enabled?).to eq(true)
|
180
|
+
expect(MyModel.new.track_history?).to eq(false)
|
181
|
+
end
|
182
|
+
end
|
171
183
|
|
172
|
-
|
173
|
-
|
184
|
+
it 'should be rescued if an exception occurs' do
|
185
|
+
begin
|
186
|
+
MyModel.disable_tracking do
|
187
|
+
fail 'exception'
|
188
|
+
end
|
189
|
+
rescue
|
190
|
+
end
|
174
191
|
expect(Mongoid::History.enabled?).to eq(true)
|
175
|
-
expect(MyModel.new.track_history?).to eq(
|
192
|
+
expect(MyModel.new.track_history?).to eq(true)
|
176
193
|
end
|
177
|
-
end
|
178
194
|
|
179
|
-
|
180
|
-
|
195
|
+
it 'should be disabled only for the class that calls disable_tracking' do
|
196
|
+
class MyModel2
|
197
|
+
include Mongoid::Document
|
198
|
+
include Mongoid::History::Trackable
|
199
|
+
track_history
|
200
|
+
end
|
201
|
+
|
181
202
|
MyModel.disable_tracking do
|
182
|
-
|
203
|
+
expect(Mongoid::History.enabled?).to eq(true)
|
204
|
+
expect(MyModel2.new.track_history?).to eq(true)
|
183
205
|
end
|
184
|
-
rescue
|
185
206
|
end
|
186
|
-
expect(Mongoid::History.enabled?).to eq(true)
|
187
|
-
expect(MyModel.new.track_history?).to eq(true)
|
188
207
|
end
|
189
208
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
209
|
+
context 'when tracking is globally disabled' do
|
210
|
+
it 'should be disabled by the global disablement' do
|
211
|
+
Mongoid::History.disable do
|
212
|
+
expect(Mongoid::History.enabled?).to eq(false)
|
213
|
+
expect(MyModel.new.track_history?).to eq(false)
|
214
|
+
end
|
195
215
|
end
|
196
216
|
|
197
|
-
|
198
|
-
|
199
|
-
|
217
|
+
it 'should be disabled within disable_tracking' do
|
218
|
+
Mongoid::History.disable do
|
219
|
+
MyModel.disable_tracking do
|
220
|
+
expect(Mongoid::History.enabled?).to eq(false)
|
221
|
+
expect(MyModel.new.track_history?).to eq(false)
|
222
|
+
end
|
223
|
+
end
|
200
224
|
end
|
201
|
-
end
|
202
|
-
end
|
203
225
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
226
|
+
it 'should be rescued if an exception occurs' do
|
227
|
+
Mongoid::History.disable do
|
228
|
+
begin
|
229
|
+
MyModel.disable_tracking do
|
230
|
+
fail 'exception'
|
231
|
+
end
|
232
|
+
rescue
|
233
|
+
end
|
234
|
+
expect(Mongoid::History.enabled?).to eq(false)
|
235
|
+
expect(MyModel.new.track_history?).to eq(false)
|
236
|
+
end
|
208
237
|
end
|
209
|
-
end
|
210
238
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
239
|
+
it 'should be disabled only for the class that calls disable_tracking' do
|
240
|
+
class MyModel2
|
241
|
+
include Mongoid::Document
|
242
|
+
include Mongoid::History::Trackable
|
243
|
+
track_history
|
244
|
+
end
|
215
245
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
246
|
+
Mongoid::History.disable do
|
247
|
+
MyModel.disable_tracking do
|
248
|
+
expect(Mongoid::History.enabled?).to eq(false)
|
249
|
+
expect(MyModel2.new.track_history?).to eq(false)
|
250
|
+
end
|
251
|
+
end
|
220
252
|
end
|
221
253
|
end
|
222
254
|
|
223
|
-
it 'should
|
255
|
+
it 'should rescue errors through both local and global tracking scopes' do
|
224
256
|
begin
|
225
|
-
|
226
|
-
|
257
|
+
Mongoid::History.disable do
|
258
|
+
MyModel.disable_tracking do
|
259
|
+
fail 'exception'
|
260
|
+
end
|
227
261
|
end
|
228
262
|
rescue
|
229
263
|
end
|
230
|
-
expect(Mongoid::History.enabled?).to eq(
|
231
|
-
expect(MyModel.new.track_history?).to eq(
|
264
|
+
expect(Mongoid::History.enabled?).to eq(true)
|
265
|
+
expect(MyModel.new.track_history?).to eq(true)
|
232
266
|
end
|
267
|
+
end
|
233
268
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
include Mongoid::History::Trackable
|
238
|
-
track_history
|
239
|
-
end
|
269
|
+
context 'when store is Thread' do
|
270
|
+
it_behaves_like 'history tracking'
|
271
|
+
end
|
240
272
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
end
|
245
|
-
end
|
273
|
+
context 'when store is RequestStore' do
|
274
|
+
before { stub_const('RequestStore', RequestStoreTemp) }
|
275
|
+
it_behaves_like 'history tracking'
|
246
276
|
end
|
277
|
+
end
|
247
278
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
279
|
+
describe ':changes_method' do
|
280
|
+
let(:custom_tracker) do
|
281
|
+
CustomTracker = Class.new(MyModel) do
|
282
|
+
field :key
|
283
|
+
|
284
|
+
track_history on: :key, changes_method: :my_changes, track_create: true
|
285
|
+
|
286
|
+
def my_changes
|
287
|
+
changes.merge('key' => "Save history-#{key}")
|
254
288
|
end
|
255
|
-
rescue
|
256
289
|
end
|
257
|
-
expect(Mongoid::History.enabled?).to eq(true)
|
258
|
-
expect(MyModel.new.track_history?).to eq(true)
|
259
290
|
end
|
260
|
-
end
|
261
291
|
|
262
|
-
describe ':changes_method' do
|
263
292
|
it 'should default to :changes' do
|
264
293
|
m = MyModel.create
|
265
294
|
expect(m).to receive(:changes).exactly(3).times.and_call_original
|
@@ -281,6 +310,244 @@ describe Mongoid::History::Trackable do
|
|
281
310
|
expect(m).to receive(:my_changes).once.and_call_original
|
282
311
|
m.save
|
283
312
|
end
|
313
|
+
|
314
|
+
it 'should allow an alternate method to be specified on object creation' do
|
315
|
+
m = custom_tracker.create(key: 'on object creation')
|
316
|
+
history_track = m.history_tracks.last
|
317
|
+
expect(history_track.modified['key']).to eq('Save history-on object creation')
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
describe '#history_settings' do
|
323
|
+
before(:each) { Mongoid::History.trackable_settings = nil }
|
324
|
+
|
325
|
+
let(:model_one) do
|
326
|
+
Class.new do
|
327
|
+
include Mongoid::Document
|
328
|
+
include Mongoid::History::Trackable
|
329
|
+
store_in collection: :model_ones
|
330
|
+
embeds_one :emb_one, inverse_class_name: 'EmbOne'
|
331
|
+
embeds_many :emb_twos, inverse_class_name: 'EmbTwo'
|
332
|
+
|
333
|
+
def self.name
|
334
|
+
'ModelOne'
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
let(:emb_one) do
|
340
|
+
Class.new do
|
341
|
+
include Mongoid::Document
|
342
|
+
include Mongoid::History::Trackable
|
343
|
+
embedded_in :model_one
|
344
|
+
|
345
|
+
def self.name
|
346
|
+
'EmbOne'
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
let(:emb_two) do
|
352
|
+
Class.new do
|
353
|
+
include Mongoid::Document
|
354
|
+
include Mongoid::History::Trackable
|
355
|
+
embedded_in :model_one
|
356
|
+
|
357
|
+
def self.name
|
358
|
+
'EmbTwo'
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
let(:default_options) { { paranoia_field: 'deleted_at' } }
|
364
|
+
|
365
|
+
context 'when options not passed' do
|
366
|
+
before(:each) do
|
367
|
+
model_one.history_settings
|
368
|
+
emb_one.history_settings
|
369
|
+
emb_two.history_settings
|
370
|
+
end
|
371
|
+
|
372
|
+
it 'should use default options' do
|
373
|
+
expect(Mongoid::History.trackable_settings[:ModelOne]).to eq(default_options)
|
374
|
+
expect(Mongoid::History.trackable_settings[:EmbOne]).to eq(default_options)
|
375
|
+
expect(Mongoid::History.trackable_settings[:EmbTwo]).to eq(default_options)
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
context 'when extra invalid options passed' do
|
380
|
+
before(:each) do
|
381
|
+
model_one.history_settings foo: :bar
|
382
|
+
emb_one.history_settings em_foo: :em_bar
|
383
|
+
emb_two.history_settings em_foo: :em_baz
|
384
|
+
end
|
385
|
+
|
386
|
+
it 'should ignore invalid options' do
|
387
|
+
expect(Mongoid::History.trackable_settings[:ModelOne]).to eq(default_options)
|
388
|
+
expect(Mongoid::History.trackable_settings[:EmbOne]).to eq(default_options)
|
389
|
+
expect(Mongoid::History.trackable_settings[:EmbTwo]).to eq(default_options)
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
context 'when valid options passed' do
|
394
|
+
before(:each) do
|
395
|
+
model_one.history_settings paranoia_field: :disabled_at
|
396
|
+
emb_one.history_settings paranoia_field: :deactivated_at
|
397
|
+
emb_two.history_settings paranoia_field: :omitted_at
|
398
|
+
end
|
399
|
+
|
400
|
+
it 'should override default options' do
|
401
|
+
expect(Mongoid::History.trackable_settings[:ModelOne]).to eq(paranoia_field: 'disabled_at')
|
402
|
+
expect(Mongoid::History.trackable_settings[:EmbOne]).to eq(paranoia_field: 'deactivated_at')
|
403
|
+
expect(Mongoid::History.trackable_settings[:EmbTwo]).to eq(paranoia_field: 'omitted_at')
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
context 'when string keys' do
|
408
|
+
before(:each) { model_one.history_settings 'paranoia_field' => 'erased_at' }
|
409
|
+
|
410
|
+
it 'should convert option keys to symbols' do
|
411
|
+
expect(Mongoid::History.trackable_settings[:ModelOne]).to eq(paranoia_field: 'erased_at')
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
context 'when paranoia field has alias' do
|
416
|
+
before(:each) do
|
417
|
+
Mongoid::History.trackable_settings = nil
|
418
|
+
model_two.history_settings paranoia_field: :neglected_at
|
419
|
+
end
|
420
|
+
|
421
|
+
let(:model_two) do
|
422
|
+
Class.new do
|
423
|
+
include Mongoid::Document
|
424
|
+
include Mongoid::History::Trackable
|
425
|
+
field :nglt, as: :neglected_at
|
426
|
+
|
427
|
+
def self.name
|
428
|
+
'ModelTwo'
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
it { expect(Mongoid::History.trackable_settings[:ModelTwo]).to eq(paranoia_field: 'nglt') }
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
describe '#tracker_class' do
|
438
|
+
before :all do
|
439
|
+
MyTrackerClass = Class.new
|
440
|
+
end
|
441
|
+
|
442
|
+
before { MyModel.instance_variable_set(:@history_trackable_options, nil) }
|
443
|
+
|
444
|
+
context 'when options contain tracker_class_name' do
|
445
|
+
context 'when underscored' do
|
446
|
+
before { MyModel.track_history tracker_class_name: 'my_tracker_class' }
|
447
|
+
it { expect(MyModel.tracker_class).to eq MyTrackerClass }
|
448
|
+
end
|
449
|
+
|
450
|
+
context 'when camelcased' do
|
451
|
+
before { MyModel.track_history tracker_class_name: 'MyTrackerClass' }
|
452
|
+
it { expect(MyModel.tracker_class).to eq MyTrackerClass }
|
453
|
+
end
|
454
|
+
|
455
|
+
context 'when constant' do
|
456
|
+
before { MyModel.track_history tracker_class_name: MyTrackerClass }
|
457
|
+
it { expect(MyModel.tracker_class).to eq MyTrackerClass }
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
describe '#modified_attributes_for_update' do
|
462
|
+
before(:all) do
|
463
|
+
ModelOne = Class.new do
|
464
|
+
include Mongoid::Document
|
465
|
+
include Mongoid::History::Trackable
|
466
|
+
store_in collection: :model_ones
|
467
|
+
field :foo
|
468
|
+
embeds_many :emb_ones, inverse_class_name: 'EmbOne'
|
469
|
+
end
|
470
|
+
|
471
|
+
EmbOne = Class.new do
|
472
|
+
include Mongoid::Document
|
473
|
+
include Mongoid::History::Trackable
|
474
|
+
field :em_foo
|
475
|
+
embedded_in :model_one
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
before(:each) do
|
480
|
+
model_one.save!
|
481
|
+
ModelOne.instance_variable_set(:@history_trackable_options, nil)
|
482
|
+
ModelOne.instance_variable_set(:@trackable_settings, nil)
|
483
|
+
EmbOne.instance_variable_set(:@trackable_settings, nil)
|
484
|
+
end
|
485
|
+
|
486
|
+
let(:model_one) { ModelOne.new(foo: 'Foo') }
|
487
|
+
let(:changes) { {} }
|
488
|
+
subject { model_one.send(:modified_attributes_for_update) }
|
489
|
+
|
490
|
+
describe 'embeds_many' do
|
491
|
+
before(:each) { allow(model_one).to receive(:changes) { changes } }
|
492
|
+
|
493
|
+
context 'when not paranoia' do
|
494
|
+
before(:each) { ModelOne.track_history(on: :emb_ones) }
|
495
|
+
let(:changes) { { 'emb_ones' => [[{ 'em_foo' => 'Foo' }], [{ 'em_foo' => 'Foo-new' }]] } }
|
496
|
+
it { expect(subject['emb_ones'][0]).to eq [{ 'em_foo' => 'Foo' }] }
|
497
|
+
it { expect(subject['emb_ones'][1]).to eq [{ 'em_foo' => 'Foo-new' }] }
|
498
|
+
end
|
499
|
+
|
500
|
+
context 'when default field for paranoia' do
|
501
|
+
before(:each) { ModelOne.track_history(on: :emb_ones) }
|
502
|
+
let(:changes) do
|
503
|
+
{ 'emb_ones' => [[{ 'em_foo' => 'Foo' }, { 'em_foo' => 'Foo-2', 'deleted_at' => Time.now }],
|
504
|
+
[{ 'em_foo' => 'Foo-new' }, { 'em_foo' => 'Foo-2-new', 'deleted_at' => Time.now }]] }
|
505
|
+
end
|
506
|
+
it { expect(subject['emb_ones'][0]).to eq [{ 'em_foo' => 'Foo' }] }
|
507
|
+
it { expect(subject['emb_ones'][1]).to eq [{ 'em_foo' => 'Foo-new' }] }
|
508
|
+
end
|
509
|
+
|
510
|
+
context 'when custom field for paranoia' do
|
511
|
+
before(:each) do
|
512
|
+
ModelOne.track_history on: :emb_ones
|
513
|
+
EmbOne.history_settings paranoia_field: :my_paranoia_field
|
514
|
+
end
|
515
|
+
let(:changes) do
|
516
|
+
{ 'emb_ones' => [[{ 'em_foo' => 'Foo', 'my_paranoia_field' => Time.now },
|
517
|
+
{ 'em_foo' => 'Foo-2' }],
|
518
|
+
[{ 'em_foo' => 'Foo-new', 'my_paranoia_field' => Time.now },
|
519
|
+
{ 'em_foo' => 'Foo-2-new' }]] }
|
520
|
+
end
|
521
|
+
it { expect(subject['emb_ones'][0]).to eq [{ 'em_foo' => 'Foo-2' }] }
|
522
|
+
it { expect(subject['emb_ones'][1]).to eq [{ 'em_foo' => 'Foo-2-new' }] }
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
describe 'fields' do
|
527
|
+
context 'when custom method for changes' do
|
528
|
+
before(:each) do
|
529
|
+
ModelOne.track_history(on: :foo, changes_method: :my_changes_method)
|
530
|
+
allow(ModelOne).to receive(:dynamic_enabled?) { false }
|
531
|
+
allow(model_one).to receive(:my_changes_method) { changes }
|
532
|
+
end
|
533
|
+
let(:changes) { { 'foo' => ['Foo', 'Foo-new'], 'bar' => ['Bar', 'Bar-new'] } }
|
534
|
+
it { is_expected.to eq('foo' => ['Foo', 'Foo-new']) }
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
after(:all) do
|
539
|
+
Object.send(:remove_const, :ModelOne)
|
540
|
+
Object.send(:remove_const, :EmbOne)
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
context 'when options not contain tracker_class_name' do
|
545
|
+
before { MyModel.track_history }
|
546
|
+
it { expect(MyModel.tracker_class).to eq Tracker }
|
547
|
+
end
|
548
|
+
|
549
|
+
after :all do
|
550
|
+
Object.send(:remove_const, :MyTrackerClass)
|
284
551
|
end
|
285
552
|
end
|
286
553
|
end
|