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