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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +16 -3
  3. data/.travis.yml +23 -8
  4. data/CHANGELOG.md +20 -6
  5. data/Dangerfile +1 -0
  6. data/Gemfile +22 -1
  7. data/LICENSE.txt +1 -1
  8. data/README.md +130 -18
  9. data/lib/mongoid/history.rb +32 -15
  10. data/lib/mongoid/history/attributes/base.rb +31 -0
  11. data/lib/mongoid/history/attributes/create.rb +52 -0
  12. data/lib/mongoid/history/attributes/destroy.rb +36 -0
  13. data/lib/mongoid/history/attributes/update.rb +43 -0
  14. data/lib/mongoid/history/options.rb +153 -0
  15. data/lib/mongoid/history/trackable.rb +170 -72
  16. data/lib/mongoid/history/tracker.rb +39 -23
  17. data/lib/mongoid/history/version.rb +1 -1
  18. data/mongoid-history.gemspec +0 -8
  19. data/spec/integration/embedded_in_polymorphic_spec.rb +8 -0
  20. data/spec/integration/integration_spec.rb +4 -0
  21. data/spec/integration/nested_embedded_documents_spec.rb +13 -6
  22. data/spec/integration/nested_embedded_polymorphic_documents_spec.rb +24 -24
  23. data/spec/spec_helper.rb +6 -0
  24. data/spec/unit/attributes/base_spec.rb +54 -0
  25. data/spec/unit/attributes/create_spec.rb +315 -0
  26. data/spec/unit/attributes/destroy_spec.rb +218 -0
  27. data/spec/unit/attributes/update_spec.rb +210 -0
  28. data/spec/unit/embedded_methods_spec.rb +69 -0
  29. data/spec/unit/history_spec.rb +35 -0
  30. data/spec/unit/my_instance_methods_spec.rb +485 -0
  31. data/spec/unit/options_spec.rb +294 -0
  32. data/spec/unit/singleton_methods_spec.rb +338 -0
  33. data/spec/unit/store/default_store_spec.rb +11 -0
  34. data/spec/unit/store/request_store_spec.rb +13 -0
  35. data/spec/unit/trackable_spec.rb +335 -68
  36. data/spec/unit/tracker_spec.rb +153 -0
  37. metadata +31 -102
@@ -0,0 +1,218 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::History::Attributes::Destroy do
4
+ let(:model_one) do
5
+ Class.new do
6
+ include Mongoid::Document
7
+ include Mongoid::History::Trackable
8
+ store_in collection: :model_ones
9
+ field :foo
10
+ field :b, as: :bar
11
+ def self.name
12
+ 'ModelOne'
13
+ end
14
+ end
15
+ end
16
+
17
+ let(:obj_one) { model_one.new }
18
+ let(:base) { described_class.new(obj_one) }
19
+ subject { base }
20
+
21
+ describe '#attributes' do
22
+ subject { base.attributes }
23
+
24
+ describe '#fields' do
25
+ before(:each) do
26
+ model_one.instance_variable_set(:@history_trackable_options, nil)
27
+ model_one.track_history on: :foo
28
+ obj_one.save!
29
+ end
30
+ let(:obj_one) { model_one.new(foo: 'Foo', bar: 'Bar') }
31
+ it { is_expected.to eq('_id' => [obj_one._id, nil], 'foo' => ['Foo', nil]) }
32
+ end
33
+
34
+ describe '#insert_embeds_one_changes' do
35
+ before(:all) do
36
+ # Need class name constant. So, defining class like this
37
+ class ModelTwo
38
+ include Mongoid::Document
39
+ include Mongoid::History::Trackable
40
+ store_in collection: :model_twos
41
+ embeds_one :emb_two
42
+ track_history on: :fields
43
+ end
44
+
45
+ class EmbTwo
46
+ include Mongoid::Document
47
+ field :em_foo
48
+ field :em_bar
49
+ embedded_in :model_two
50
+ end
51
+ end
52
+
53
+ before(:each) { ModelTwo.clear_trackable_memoization }
54
+ let(:obj_two) { ModelTwo.new(emb_two: emb_obj_two) }
55
+ let(:emb_obj_two) { EmbTwo.new(em_foo: 'Em-Foo', em_bar: 'Em-Bar') }
56
+ let(:base) { described_class.new(obj_two) }
57
+
58
+ context 'when relation tracked' do
59
+ before(:each) do
60
+ ModelTwo.track_history on: :emb_two
61
+ obj_two.save!
62
+ end
63
+ it { expect(subject['emb_two']).to eq [{ '_id' => emb_obj_two._id, 'em_foo' => 'Em-Foo', 'em_bar' => 'Em-Bar' }, nil] }
64
+ end
65
+
66
+ context 'when relation not tracked' do
67
+ before(:each) do
68
+ ModelTwo.track_history on: :fields
69
+ allow(ModelTwo).to receive(:dynamic_enabled?) { false }
70
+ obj_two.save!
71
+ end
72
+ it { expect(subject['emb_two']).to be_nil }
73
+ end
74
+
75
+ context 'when relation with alias' do
76
+ before(:all) do
77
+ # Need class name constant. So, defining class like this
78
+ class ModelThree
79
+ include Mongoid::Document
80
+ include Mongoid::History::Trackable
81
+ store_in collection: :model_threes
82
+ embeds_one :emb_three, store_as: :emtr
83
+ end
84
+
85
+ class EmbThree
86
+ include Mongoid::Document
87
+ field :em_foo
88
+ embedded_in :model_three
89
+ end
90
+ end
91
+
92
+ before(:each) do
93
+ ModelThree.instance_variable_set(:@history_trackable_options, nil)
94
+ ModelThree.track_history on: :emb_three
95
+ obj_three.save!
96
+ end
97
+
98
+ let(:obj_three) { ModelThree.new(emb_three: emb_obj_three) }
99
+ let(:emb_obj_three) { EmbThree.new(em_foo: 'Em-Foo') }
100
+ let(:base) { described_class.new(obj_three) }
101
+ it { expect(subject['emb_three']).to eq [{ '_id' => emb_obj_three._id, 'em_foo' => 'Em-Foo' }, nil] }
102
+
103
+ after(:all) do
104
+ Object.send(:remove_const, :ModelThree)
105
+ Object.send(:remove_const, :EmbThree)
106
+ end
107
+ end
108
+
109
+ context 'relation with permitted attributes' do
110
+ before(:each) do
111
+ ModelTwo.track_history on: [{ emb_two: :em_foo }]
112
+ obj_two.save!
113
+ end
114
+ it { expect(subject['emb_two']).to eq [{ '_id' => emb_obj_two._id, 'em_foo' => 'Em-Foo' }, nil] }
115
+ end
116
+
117
+ context 'when relation object not built' do
118
+ before(:each) do
119
+ ModelTwo.track_history on: :emb_two
120
+ obj_two.save!
121
+ end
122
+ let(:obj_two) { ModelTwo.new }
123
+ it { expect(subject['emb_two']).to be_nil }
124
+ end
125
+
126
+ after(:all) do
127
+ Object.send(:remove_const, :ModelTwo)
128
+ Object.send(:remove_const, :EmbTwo)
129
+ end
130
+ end
131
+
132
+ describe '#insert_embeds_many_changes' do
133
+ context 'Case 1:' do
134
+ before(:all) do
135
+ class ModelTwo
136
+ include Mongoid::Document
137
+ include Mongoid::History::Trackable
138
+ embeds_many :em_twos
139
+ track_history on: :fields
140
+ end
141
+
142
+ class EmTwo
143
+ include Mongoid::Document
144
+ field :em_foo
145
+ field :em_bar
146
+ embedded_in :model_two
147
+ end
148
+ end
149
+
150
+ let(:obj_two) { ModelTwo.new(em_twos: [em_obj_two]) }
151
+ let(:em_obj_two) { EmTwo.new(em_foo: 'Em-Foo', em_bar: 'Em-Bar') }
152
+ let(:base) { described_class.new(obj_two) }
153
+
154
+ context 'when relation tracked' do
155
+ before(:each) do
156
+ ModelTwo.clear_trackable_memoization
157
+ ModelTwo.track_history on: :em_twos
158
+ end
159
+ it { expect(subject['em_twos']).to eq [[{ '_id' => em_obj_two._id, 'em_foo' => 'Em-Foo', 'em_bar' => 'Em-Bar' }], nil] }
160
+ end
161
+
162
+ context 'when relation not tracked' do
163
+ before(:each) do
164
+ ModelTwo.clear_trackable_memoization
165
+ ModelTwo.track_history on: :fields
166
+ end
167
+ it { expect(subject['em_twos']).to be_nil }
168
+ end
169
+
170
+ context 'when relation with permitted attributes for tracking' do
171
+ before(:each) do
172
+ ModelTwo.clear_trackable_memoization
173
+ ModelTwo.track_history on: { em_twos: :em_foo }
174
+ end
175
+ it { expect(subject['em_twos']).to eq [[{ '_id' => em_obj_two._id, 'em_foo' => 'Em-Foo' }], nil] }
176
+ end
177
+
178
+ after(:all) do
179
+ Object.send(:remove_const, :ModelTwo)
180
+ Object.send(:remove_const, :EmTwo)
181
+ end
182
+ end
183
+
184
+ context 'when relation with alias' do
185
+ before(:all) do
186
+ class ModelTwo
187
+ include Mongoid::Document
188
+ include Mongoid::History::Trackable
189
+ embeds_many :em_twos, store_as: :emws
190
+ track_history on: :fields
191
+ end
192
+
193
+ class EmTwo
194
+ include Mongoid::Document
195
+ field :em_foo
196
+ embedded_in :model_two
197
+ end
198
+ end
199
+
200
+ before(:each) do
201
+ ModelTwo.clear_trackable_memoization
202
+ ModelTwo.track_history on: :em_twos
203
+ end
204
+
205
+ let(:obj_two) { ModelTwo.new(em_twos: [em_obj_two]) }
206
+ let(:em_obj_two) { EmTwo.new(em_foo: 'Em-Foo') }
207
+ let(:base) { described_class.new(obj_two) }
208
+
209
+ it { expect(subject['em_twos']).to eq [[{ '_id' => em_obj_two._id, 'em_foo' => 'Em-Foo' }], nil] }
210
+
211
+ after(:all) do
212
+ Object.send(:remove_const, :ModelTwo)
213
+ Object.send(:remove_const, :EmTwo)
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,210 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::History::Attributes::Update do
4
+ describe '#attributes' do
5
+ describe '#insert_embeds_one_changes' do
6
+ context 'Case: relation without alias' do
7
+ before(:all) do
8
+ class ModelOne
9
+ include Mongoid::Document
10
+ include Mongoid::History::Trackable
11
+ store_in collection: :model_ones
12
+ embeds_one :emb_one
13
+ track_history on: :fields
14
+ end
15
+
16
+ class EmbOne
17
+ include Mongoid::Document
18
+ field :em_foo
19
+ field :em_bar
20
+ embedded_in :model_one
21
+ end
22
+ end
23
+
24
+ before(:each) do
25
+ ModelOne.clear_trackable_memoization
26
+ allow(base).to receive(:changes) { changes }
27
+ end
28
+
29
+ let(:obj_one) { ModelOne.new }
30
+ let(:base) { described_class.new(obj_one) }
31
+ let(:changes) do
32
+ { 'emb_one' => [{ 'em_foo' => 'Em-Foo', 'em_bar' => 'Em-Bar' }, { 'em_foo' => 'Em-Foo-new', 'em_bar' => 'Em-Bar-new' }] }
33
+ end
34
+ subject { base.attributes }
35
+
36
+ context 'with permitted attributes' do
37
+ before(:each) { ModelOne.track_history on: { emb_one: :em_foo } }
38
+ it { expect(subject['emb_one']).to eq [{ 'em_foo' => 'Em-Foo' }, { 'em_foo' => 'Em-Foo-new' }] }
39
+ end
40
+
41
+ context 'without permitted attributes' do
42
+ before(:each) { ModelOne.track_history on: :emb_one }
43
+ it { expect(subject['emb_one']).to eq [{ 'em_foo' => 'Em-Foo', 'em_bar' => 'Em-Bar' }, { 'em_foo' => 'Em-Foo-new', 'em_bar' => 'Em-Bar-new' }] }
44
+ end
45
+
46
+ context 'when old value soft-deleted' do
47
+ before(:each) { ModelOne.track_history on: :emb_one }
48
+ let(:changes) do
49
+ { 'emb_one' => [{ 'em_foo' => 'Em-Foo', 'deleted_at' => Time.now }, { 'em_foo' => 'Em-Foo-new', 'em_bar' => 'Em-Bar-new' }] }
50
+ end
51
+ it { expect(subject['emb_one']).to eq [{}, { 'em_foo' => 'Em-Foo-new', 'em_bar' => 'Em-Bar-new' }] }
52
+ end
53
+
54
+ context 'when new value soft-deleted' do
55
+ before(:each) { ModelOne.track_history on: :emb_one }
56
+ let(:changes) do
57
+ { 'emb_one' => [{ 'em_foo' => 'Em-Foo' }, { 'em_foo' => 'Em-Foo-new', 'deleted_at' => Time.now }] }
58
+ end
59
+ it { expect(subject['emb_one']).to eq [{ 'em_foo' => 'Em-Foo' }, {}] }
60
+ end
61
+
62
+ context 'when not tracked' do
63
+ before(:each) do
64
+ ModelOne.track_history on: :fields
65
+ allow(ModelOne).to receive(:dynamic_enabled?) { false }
66
+ end
67
+ it { expect(subject['emb_one']).to be_nil }
68
+ end
69
+
70
+ after(:all) do
71
+ Object.send(:remove_const, :ModelOne)
72
+ Object.send(:remove_const, :EmbOne)
73
+ end
74
+ end
75
+
76
+ context 'Case: relation with alias' do
77
+ before(:all) do
78
+ class ModelOne
79
+ include Mongoid::Document
80
+ include Mongoid::History::Trackable
81
+ store_in collection: :model_ones
82
+ embeds_one :emb_one, store_as: :eon
83
+ track_history on: :fields
84
+ end
85
+
86
+ class EmbOne
87
+ include Mongoid::Document
88
+ field :em_foo
89
+ field :em_bar
90
+ embedded_in :model_one
91
+ end
92
+ end
93
+
94
+ before(:each) do
95
+ ModelOne.clear_trackable_memoization
96
+ ModelOne.track_history on: :emb_one
97
+ allow(base).to receive(:changes) { changes }
98
+ end
99
+
100
+ let(:obj_one) { ModelOne.new }
101
+ let(:base) { described_class.new(obj_one) }
102
+ let(:changes) do
103
+ { 'emb_one' => [{ 'em_foo' => 'Em-Foo' }, { 'em_foo' => 'Em-Foo-new', 'em_bar' => 'Em-Bar-new' }] }
104
+ end
105
+ subject { base.attributes }
106
+ it { expect(subject['eon']).to eq [{ 'em_foo' => 'Em-Foo' }, { 'em_foo' => 'Em-Foo-new', 'em_bar' => 'Em-Bar-new' }] }
107
+
108
+ after(:all) do
109
+ Object.send(:remove_const, :ModelOne)
110
+ Object.send(:remove_const, :EmbOne)
111
+ end
112
+ end
113
+ end
114
+
115
+ describe '#insert_embeds_many_changes' do
116
+ context 'Case: relation without alias' do
117
+ before(:all) do
118
+ class ModelOne
119
+ include Mongoid::Document
120
+ include Mongoid::History::Trackable
121
+ store_in collection: :model_ones
122
+ embeds_many :emb_ones
123
+ track_history on: :fields
124
+ end
125
+
126
+ class EmbOne
127
+ include Mongoid::Document
128
+ field :em_foo
129
+ field :em_bar
130
+ embedded_in :model_one
131
+ end
132
+ end
133
+
134
+ before(:each) do
135
+ ModelOne.clear_trackable_memoization
136
+ allow(base).to receive(:changes) { changes }
137
+ end
138
+
139
+ let(:obj_one) { ModelOne.new }
140
+ let(:base) { described_class.new(obj_one) }
141
+ subject { base.attributes }
142
+
143
+ context 'with whitelist attributes' do
144
+ before(:each) { ModelOne.track_history on: { emb_ones: :em_foo } }
145
+ let(:changes) do
146
+ { 'emb_ones' => [[{ 'em_foo' => 'Em-Foo', 'em_bar' => 'Em-Bar' }], [{ 'em_foo' => 'Em-Foo-new', 'em_bar' => 'Em-Bar-new' }]] }
147
+ end
148
+ it 'should track only whitelisted attributes' do
149
+ expect(subject['emb_ones']).to eq [[{ 'em_foo' => 'Em-Foo' }], [{ 'em_foo' => 'Em-Foo-new' }]]
150
+ end
151
+ end
152
+
153
+ context 'without whitelist attributes' do
154
+ before(:each) { ModelOne.track_history on: :emb_ones }
155
+ let(:changes) do
156
+ { 'emb_ones' => [[{ 'em_foo' => 'Em-Foo', 'deleted_at' => Time.now }], [{ 'em_foo' => 'Em-Foo-new', 'em_bar' => 'Em-Bar-new' }]] }
157
+ end
158
+ it 'should ignore soft-deleted objects' do
159
+ expect(subject['emb_ones']).to eq [[], [{ 'em_foo' => 'Em-Foo-new', 'em_bar' => 'Em-Bar-new' }]]
160
+ end
161
+ end
162
+
163
+ after(:all) do
164
+ Object.send(:remove_const, :ModelOne)
165
+ Object.send(:remove_const, :EmbOne)
166
+ end
167
+ end
168
+
169
+ context 'Case: relation with alias' do
170
+ before(:all) do
171
+ class ModelOne
172
+ include Mongoid::Document
173
+ include Mongoid::History::Trackable
174
+ store_in collection: :model_ones
175
+ embeds_many :emb_ones, store_as: :eons
176
+ track_history on: :fields
177
+ end
178
+
179
+ class EmbOne
180
+ include Mongoid::Document
181
+ field :em_foo
182
+ field :em_bar
183
+ embedded_in :model_one
184
+ end
185
+ end
186
+
187
+ before(:each) do
188
+ ModelOne.clear_trackable_memoization
189
+ ModelOne.track_history on: :emb_ones
190
+ allow(base).to receive(:changes) { changes }
191
+ end
192
+
193
+ let(:obj_one) { ModelOne.new }
194
+ let(:base) { described_class.new(obj_one) }
195
+ let(:changes) do
196
+ { 'emb_ones' => [[{ 'em_foo' => 'Em-Foo' }], [{ 'em_foo' => 'Em-Foo-new', 'em_bar' => 'Em-Bar-new' }]] }
197
+ end
198
+ subject { base.attributes }
199
+ it 'should save audit history under relation alias' do
200
+ expect(subject['eons']).to eq [[{ 'em_foo' => 'Em-Foo' }], [{ 'em_foo' => 'Em-Foo-new', 'em_bar' => 'Em-Bar-new' }]]
201
+ end
202
+ end
203
+
204
+ after(:all) do
205
+ Object.send(:remove_const, :ModelOne)
206
+ Object.send(:remove_const, :EmbOne)
207
+ end
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::History::Trackable do
4
+ describe 'EmbeddedMethods' do
5
+ describe 'embeds_one_class' do
6
+ before :all do
7
+ ModelOne = Class.new do
8
+ include Mongoid::Document
9
+ include Mongoid::History::Trackable
10
+ embeds_one :emb_one, inverse_class_name: 'EmbOne'
11
+ embeds_one :emb_two, store_as: 'emt', inverse_class_name: 'EmbTwo'
12
+ track_history
13
+ end
14
+
15
+ EmbOne = Class.new do
16
+ include Mongoid::Document
17
+ embedded_in :model_one
18
+ end
19
+
20
+ EmbTwo = Class.new do
21
+ include Mongoid::Document
22
+ embedded_in :model_one
23
+ end
24
+ end
25
+
26
+ it { expect(ModelOne.embeds_one_class('emb_one')).to eq EmbOne }
27
+ it { expect(ModelOne.embeds_one_class('emt')).to eq EmbTwo }
28
+ it { expect(ModelOne.embeds_one_class('invalid')).to be_nil }
29
+
30
+ after :all do
31
+ Object.send(:remove_const, :ModelOne)
32
+ Object.send(:remove_const, :EmbOne)
33
+ Object.send(:remove_const, :EmbTwo)
34
+ end
35
+ end
36
+
37
+ describe 'embeds_many_class' do
38
+ before :all do
39
+ ModelOne = Class.new do
40
+ include Mongoid::Document
41
+ include Mongoid::History::Trackable
42
+ embeds_many :emb_ones, inverse_class_name: 'EmbOne'
43
+ embeds_many :emb_twos, store_as: 'emts', inverse_class_name: 'EmbTwo'
44
+ track_history
45
+ end
46
+
47
+ EmbOne = Class.new do
48
+ include Mongoid::Document
49
+ embedded_in :model_one
50
+ end
51
+
52
+ EmbTwo = Class.new do
53
+ include Mongoid::Document
54
+ embedded_in :model_one
55
+ end
56
+ end
57
+
58
+ it { expect(ModelOne.embeds_many_class('emb_ones')).to eq EmbOne }
59
+ it { expect(ModelOne.embeds_many_class('emts')).to eq EmbTwo }
60
+ it { expect(ModelOne.embeds_many_class('invalid')).to be_nil }
61
+
62
+ after :all do
63
+ Object.send(:remove_const, :ModelOne)
64
+ Object.send(:remove_const, :EmbOne)
65
+ Object.send(:remove_const, :EmbTwo)
66
+ end
67
+ end
68
+ end
69
+ end