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