paper_trail 6.0.2 → 7.0.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/.github/CONTRIBUTING.md +20 -0
- data/.rubocop.yml +30 -2
- data/.rubocop_todo.yml +20 -0
- data/.travis.yml +3 -5
- data/Appraisals +5 -6
- data/CHANGELOG.md +33 -0
- data/README.md +43 -81
- data/Rakefile +1 -1
- data/doc/bug_report_template.rb +4 -2
- data/gemfiles/ar_4.0.gemfile +7 -0
- data/gemfiles/ar_4.2.gemfile +0 -1
- data/lib/generators/paper_trail/templates/create_version_associations.rb +1 -1
- data/lib/generators/paper_trail/templates/create_versions.rb +1 -1
- data/lib/paper_trail.rb +7 -9
- data/lib/paper_trail/config.rb +0 -15
- data/lib/paper_trail/frameworks/rspec.rb +8 -2
- data/lib/paper_trail/model_config.rb +6 -2
- data/lib/paper_trail/record_trail.rb +3 -1
- data/lib/paper_trail/reifier.rb +43 -354
- data/lib/paper_trail/reifiers/belongs_to.rb +48 -0
- data/lib/paper_trail/reifiers/has_and_belongs_to_many.rb +50 -0
- data/lib/paper_trail/reifiers/has_many.rb +110 -0
- data/lib/paper_trail/reifiers/has_many_through.rb +90 -0
- data/lib/paper_trail/reifiers/has_one.rb +76 -0
- data/lib/paper_trail/serializers/yaml.rb +2 -25
- data/lib/paper_trail/version_concern.rb +5 -5
- data/lib/paper_trail/version_number.rb +7 -3
- data/paper_trail.gemspec +7 -34
- data/spec/controllers/articles_controller_spec.rb +1 -1
- data/spec/generators/install_generator_spec.rb +40 -34
- data/spec/models/animal_spec.rb +50 -25
- data/spec/models/boolit_spec.rb +8 -7
- data/spec/models/callback_modifier_spec.rb +13 -13
- data/spec/models/document_spec.rb +21 -0
- data/spec/models/gadget_spec.rb +35 -39
- data/spec/models/joined_version_spec.rb +4 -4
- data/spec/models/json_version_spec.rb +14 -15
- data/spec/models/not_on_update_spec.rb +1 -1
- data/spec/models/post_with_status_spec.rb +2 -2
- data/spec/models/skipper_spec.rb +4 -4
- data/spec/models/thing_spec.rb +1 -1
- data/spec/models/truck_spec.rb +1 -1
- data/spec/models/vehicle_spec.rb +1 -1
- data/spec/models/version_spec.rb +152 -168
- data/spec/models/widget_spec.rb +170 -196
- data/spec/modules/paper_trail_spec.rb +3 -3
- data/spec/modules/version_concern_spec.rb +5 -8
- data/spec/modules/version_number_spec.rb +11 -36
- data/spec/paper_trail/cleaner_spec.rb +152 -0
- data/spec/paper_trail/config_spec.rb +1 -1
- data/spec/paper_trail/serializers/custom_yaml_serializer_spec.rb +45 -0
- data/spec/paper_trail/serializers/json_spec.rb +57 -0
- data/spec/paper_trail/version_limit_spec.rb +55 -0
- data/spec/paper_trail_spec.rb +45 -32
- data/spec/requests/articles_spec.rb +4 -4
- data/test/dummy/app/models/custom_primary_key_record.rb +4 -2
- data/test/dummy/app/models/document.rb +1 -1
- data/test/dummy/app/models/not_on_update.rb +1 -1
- data/test/dummy/app/models/on/create.rb +6 -0
- data/test/dummy/app/models/on/destroy.rb +6 -0
- data/test/dummy/app/models/on/empty_array.rb +6 -0
- data/test/dummy/app/models/on/update.rb +6 -0
- data/test/dummy/app/models/person.rb +1 -0
- data/test/dummy/app/models/song.rb +19 -28
- data/test/dummy/config/application.rb +10 -43
- data/test/dummy/config/routes.rb +1 -1
- data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +25 -51
- data/test/dummy/db/schema.rb +29 -19
- data/test/test_helper.rb +0 -16
- data/test/unit/associations_test.rb +81 -81
- data/test/unit/model_test.rb +48 -131
- data/test/unit/serializer_test.rb +34 -45
- data/test/unit/serializers/mixin_json_test.rb +3 -1
- data/test/unit/serializers/yaml_test.rb +1 -5
- metadata +44 -19
- data/lib/paper_trail/frameworks/sinatra.rb +0 -40
- data/test/functional/modular_sinatra_test.rb +0 -46
- data/test/functional/sinatra_test.rb +0 -51
- data/test/unit/cleaner_test.rb +0 -151
- data/test/unit/inheritance_column_test.rb +0 -41
- data/test/unit/serializers/json_test.rb +0 -95
- data/test/unit/serializers/mixin_yaml_test.rb +0 -53
data/spec/models/widget_spec.rb
CHANGED
@@ -38,65 +38,63 @@ describe Widget, type: :model do
|
|
38
38
|
|
39
39
|
describe "versioning option" do
|
40
40
|
context "enabled", versioning: true do
|
41
|
-
it "
|
41
|
+
it "enables versioning" do
|
42
42
|
expect(widget.versions.size).to eq(1)
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
46
|
context "disabled (default)" do
|
47
|
-
it "
|
47
|
+
it "does not enable versioning" do
|
48
48
|
expect(widget.versions.size).to eq(0)
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
53
|
describe "Callbacks", versioning: true do
|
54
|
-
describe
|
55
|
-
|
56
|
-
before { widget.update_attributes!(name: "Foobar") }
|
54
|
+
describe "before_save" do
|
55
|
+
before { widget.update_attributes!(name: "Foobar") }
|
57
56
|
|
58
|
-
|
57
|
+
subject { widget.versions.last.reify }
|
59
58
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
59
|
+
it "resets value for timestamp attrs for update so that value gets updated properly" do
|
60
|
+
# Travel 1 second because MySQL lacks sub-second resolution
|
61
|
+
Timecop.travel(1) do
|
62
|
+
expect { subject.save! }.to change(subject, :updated_at)
|
65
63
|
end
|
66
64
|
end
|
67
65
|
end
|
68
66
|
|
69
|
-
describe
|
67
|
+
describe "after_create" do
|
70
68
|
let(:widget) { Widget.create!(name: "Foobar", created_at: Time.now - 1.week) }
|
71
69
|
|
72
|
-
it "corresponding version
|
70
|
+
it "corresponding version uses the widget's `updated_at`" do
|
73
71
|
expect(widget.versions.last.created_at.to_i).to eq(widget.updated_at.to_i)
|
74
72
|
end
|
75
73
|
end
|
76
74
|
|
77
|
-
describe
|
75
|
+
describe "after_update" do
|
78
76
|
before { widget.update_attributes!(name: "Foobar", updated_at: Time.now + 1.week) }
|
79
77
|
|
80
78
|
subject { widget.versions.last.reify }
|
81
79
|
|
82
80
|
it { expect(subject.paper_trail).not_to be_live }
|
83
81
|
|
84
|
-
it "
|
82
|
+
it "clears the `versions_association_name` virtual attribute" do
|
85
83
|
subject.save!
|
86
84
|
expect(subject.paper_trail).to be_live
|
87
85
|
end
|
88
86
|
|
89
|
-
it "corresponding version
|
87
|
+
it "corresponding version uses the widget updated_at" do
|
90
88
|
expect(widget.versions.last.created_at.to_i).to eq(widget.updated_at.to_i)
|
91
89
|
end
|
92
90
|
end
|
93
91
|
|
94
|
-
describe
|
95
|
-
it "
|
92
|
+
describe "after_destroy" do
|
93
|
+
it "creates a version for that event" do
|
96
94
|
expect { widget.destroy }.to change(widget.versions, :count).by(1)
|
97
95
|
end
|
98
96
|
|
99
|
-
it "
|
97
|
+
it "assigns the version into the `versions_association_name`" do
|
100
98
|
expect(widget.version).to be_nil
|
101
99
|
widget.destroy
|
102
100
|
expect(widget.version).not_to be_nil
|
@@ -104,7 +102,7 @@ describe Widget, type: :model do
|
|
104
102
|
end
|
105
103
|
end
|
106
104
|
|
107
|
-
describe
|
105
|
+
describe "after_rollback" do
|
108
106
|
let(:rolled_back_name) { "Big Moo" }
|
109
107
|
|
110
108
|
before do
|
@@ -122,19 +120,19 @@ describe Widget, type: :model do
|
|
122
120
|
|
123
121
|
it "does not create an event for changes that did not happen" do
|
124
122
|
widget.versions.map(&:changeset).each do |changeset|
|
125
|
-
expect(changeset.fetch("name", [])).
|
123
|
+
expect(changeset.fetch("name", [])).not_to include(rolled_back_name)
|
126
124
|
end
|
127
125
|
end
|
128
126
|
|
129
127
|
it "has not yet loaded the assocation" do
|
130
|
-
expect(widget.versions).
|
128
|
+
expect(widget.versions).not_to be_loaded
|
131
129
|
end
|
132
130
|
end
|
133
131
|
end
|
134
132
|
|
135
133
|
describe "Association", versioning: true do
|
136
134
|
describe "sort order" do
|
137
|
-
it "
|
135
|
+
it "sorts by the timestamp order from the `VersionConcern`" do
|
138
136
|
expect(widget.versions.to_sql).to eq(
|
139
137
|
widget.versions.reorder(PaperTrail::Version.timestamp_sort_order).to_sql
|
140
138
|
)
|
@@ -144,7 +142,7 @@ describe Widget, type: :model do
|
|
144
142
|
|
145
143
|
if defined?(ActiveRecord::IdentityMap) && ActiveRecord::IdentityMap.respond_to?(:without)
|
146
144
|
describe "IdentityMap", versioning: true do
|
147
|
-
it "
|
145
|
+
it "does not clobber the IdentityMap when reifying" do
|
148
146
|
widget.update_attributes name: "Henry", created_at: Time.now - 1.day
|
149
147
|
widget.update_attributes name: "Harry"
|
150
148
|
expect(ActiveRecord::IdentityMap).to receive(:without).once
|
@@ -153,204 +151,180 @@ describe Widget, type: :model do
|
|
153
151
|
end
|
154
152
|
end
|
155
153
|
|
156
|
-
describe "
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
describe "#destroy" do
|
166
|
-
it "creates a version record" do
|
167
|
-
widget = Widget.create
|
168
|
-
assert_equal 1, widget.versions.length
|
169
|
-
widget.destroy
|
170
|
-
versions_for_widget = PaperTrail::Version.with_item_keys("Widget", widget.id)
|
171
|
-
assert_equal 2, versions_for_widget.length
|
172
|
-
end
|
173
|
-
|
174
|
-
it "can have multiple destruction records" do
|
175
|
-
versions = lambda { |widget|
|
176
|
-
# Workaround for AR 3. When we drop AR 3 support, we can simply use
|
177
|
-
# the `widget.versions` association, instead of `with_item_keys`.
|
178
|
-
PaperTrail::Version.with_item_keys("Widget", widget.id)
|
179
|
-
}
|
180
|
-
widget = Widget.create
|
181
|
-
assert_equal 1, widget.versions.length
|
182
|
-
widget.destroy
|
183
|
-
assert_equal 2, versions.call(widget).length
|
184
|
-
widget = widget.version.reify
|
185
|
-
widget.save
|
186
|
-
assert_equal 3, versions.call(widget).length
|
187
|
-
widget.destroy
|
188
|
-
assert_equal 4, versions.call(widget).length
|
189
|
-
assert_equal 2, versions.call(widget).where(event: "destroy").length
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
describe "#paper_trail.originator" do
|
194
|
-
describe "return value" do
|
195
|
-
let(:orig_name) { FFaker::Name.name }
|
196
|
-
let(:new_name) { FFaker::Name.name }
|
197
|
-
before { PaperTrail.whodunnit = orig_name }
|
198
|
-
|
199
|
-
context "accessed from live model instance" do
|
200
|
-
specify { expect(widget.paper_trail).to be_live }
|
201
|
-
|
202
|
-
it "should return the originator for the model at a given state" do
|
203
|
-
expect(widget.paper_trail.originator).to eq(orig_name)
|
204
|
-
widget.paper_trail.whodunnit(new_name) { |w|
|
205
|
-
w.update_attributes(name: "Elizabeth")
|
206
|
-
}
|
207
|
-
expect(widget.paper_trail.originator).to eq(new_name)
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
context "accessed from a reified model instance" do
|
212
|
-
before do
|
213
|
-
widget.update_attributes(name: "Andy")
|
214
|
-
PaperTrail.whodunnit = new_name
|
215
|
-
widget.update_attributes(name: "Elizabeth")
|
216
|
-
end
|
217
|
-
|
218
|
-
context "default behavior (no `options[:dup]` option passed in)" do
|
219
|
-
let(:reified_widget) { widget.versions[1].reify }
|
220
|
-
|
221
|
-
it "should return the appropriate originator" do
|
222
|
-
expect(reified_widget.paper_trail.originator).to eq(orig_name)
|
223
|
-
end
|
154
|
+
describe "#create", versioning: true do
|
155
|
+
it "creates a version record" do
|
156
|
+
wordget = Widget.create
|
157
|
+
assert_equal 1, wordget.versions.length
|
158
|
+
end
|
159
|
+
end
|
224
160
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
161
|
+
describe "#destroy", versioning: true do
|
162
|
+
it "creates a version record" do
|
163
|
+
widget = Widget.create
|
164
|
+
assert_equal 1, widget.versions.length
|
165
|
+
widget.destroy
|
166
|
+
versions_for_widget = PaperTrail::Version.with_item_keys("Widget", widget.id)
|
167
|
+
assert_equal 2, versions_for_widget.length
|
168
|
+
end
|
229
169
|
|
230
|
-
|
231
|
-
|
170
|
+
it "can have multiple destruction records" do
|
171
|
+
versions = lambda { |widget|
|
172
|
+
# Workaround for AR 3. When we drop AR 3 support, we can simply use
|
173
|
+
# the `widget.versions` association, instead of `with_item_keys`.
|
174
|
+
PaperTrail::Version.with_item_keys("Widget", widget.id)
|
175
|
+
}
|
176
|
+
widget = Widget.create
|
177
|
+
assert_equal 1, widget.versions.length
|
178
|
+
widget.destroy
|
179
|
+
assert_equal 2, versions.call(widget).length
|
180
|
+
widget = widget.version.reify
|
181
|
+
widget.save
|
182
|
+
assert_equal 3, versions.call(widget).length
|
183
|
+
widget.destroy
|
184
|
+
assert_equal 4, versions.call(widget).length
|
185
|
+
assert_equal 2, versions.call(widget).where(event: "destroy").length
|
186
|
+
end
|
187
|
+
end
|
232
188
|
|
233
|
-
|
234
|
-
|
235
|
-
|
189
|
+
describe "#paper_trail.originator", versioning: true do
|
190
|
+
describe "return value" do
|
191
|
+
let(:orig_name) { FFaker::Name.name }
|
192
|
+
let(:new_name) { FFaker::Name.name }
|
236
193
|
|
237
|
-
|
238
|
-
|
239
|
-
end
|
240
|
-
end
|
241
|
-
end
|
242
|
-
end
|
194
|
+
before do
|
195
|
+
PaperTrail.whodunnit = orig_name
|
243
196
|
end
|
244
197
|
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
end
|
198
|
+
it "returns the originator for the model at a given state" do
|
199
|
+
expect(widget.paper_trail).to be_live
|
200
|
+
expect(widget.paper_trail.originator).to eq(orig_name)
|
201
|
+
widget.paper_trail.whodunnit(new_name) { |w|
|
202
|
+
w.update_attributes(name: "Elizabeth")
|
203
|
+
}
|
204
|
+
expect(widget.paper_trail.originator).to eq(new_name)
|
253
205
|
end
|
254
206
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
207
|
+
it "returns the appropriate originator" do
|
208
|
+
widget.update_attributes(name: "Andy")
|
209
|
+
PaperTrail.whodunnit = new_name
|
210
|
+
widget.update_attributes(name: "Elizabeth")
|
211
|
+
reified_widget = widget.versions[1].reify
|
212
|
+
expect(reified_widget.paper_trail.originator).to eq(orig_name)
|
213
|
+
expect(reified_widget).not_to be_new_record
|
214
|
+
end
|
263
215
|
|
264
|
-
|
265
|
-
|
266
|
-
|
216
|
+
it "can create a new instance with options[:dup]" do
|
217
|
+
widget.update_attributes(name: "Andy")
|
218
|
+
PaperTrail.whodunnit = new_name
|
219
|
+
widget.update_attributes(name: "Elizabeth")
|
220
|
+
reified_widget = widget.versions[1].reify(dup: true)
|
221
|
+
expect(reified_widget.paper_trail.originator).to eq(orig_name)
|
222
|
+
expect(reified_widget).to be_new_record
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
267
226
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
227
|
+
describe "#version_at", versioning: true do
|
228
|
+
context "Timestamp argument is AFTER object has been destroyed" do
|
229
|
+
it "returns nil" do
|
230
|
+
widget.update_attribute(:name, "foobar")
|
231
|
+
widget.destroy
|
232
|
+
expect(widget.paper_trail.version_at(Time.now)).to be_nil
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
272
236
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
237
|
+
describe "#whodunnit", versioning: true do
|
238
|
+
context "no block given" do
|
239
|
+
it "raises an error" do
|
240
|
+
expect {
|
241
|
+
widget.paper_trail.whodunnit("Ben")
|
242
|
+
}.to raise_error(ArgumentError, "expected to receive a block")
|
243
|
+
end
|
244
|
+
end
|
280
245
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
w.update_attributes(name: "Elizabeth")
|
285
|
-
}
|
286
|
-
expect(PaperTrail.whodunnit).to eq(orig_name)
|
287
|
-
end
|
288
|
-
end
|
246
|
+
context "block given" do
|
247
|
+
let(:orig_name) { FFaker::Name.name }
|
248
|
+
let(:new_name) { FFaker::Name.name }
|
289
249
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
widget.paper_trail.whodunnit(new_name) { raise }
|
294
|
-
}.to raise_error(RuntimeError)
|
295
|
-
expect(PaperTrail.whodunnit).to eq(orig_name)
|
296
|
-
end
|
297
|
-
end
|
298
|
-
end
|
250
|
+
before do
|
251
|
+
PaperTrail.whodunnit = orig_name
|
252
|
+
expect(widget.versions.last.whodunnit).to eq(orig_name) # persist `widget`
|
299
253
|
end
|
300
254
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
Timecop.travel(1) do
|
306
|
-
widget.paper_trail.touch_with_version
|
307
|
-
end
|
308
|
-
expect(widget.versions.size).to eq(count + 1)
|
255
|
+
it "modifies value of `PaperTrail.whodunnit` while executing the block" do
|
256
|
+
widget.paper_trail.whodunnit(new_name) do
|
257
|
+
expect(PaperTrail.whodunnit).to eq(new_name)
|
258
|
+
widget.update_attributes(name: "Elizabeth")
|
309
259
|
end
|
260
|
+
expect(widget.versions.last.whodunnit).to eq(new_name)
|
261
|
+
end
|
310
262
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
end
|
317
|
-
expect(widget.updated_at).to be > time_was
|
318
|
-
end
|
263
|
+
it "reverts value of whodunnit to previous value after executing the block" do
|
264
|
+
widget.paper_trail.whodunnit(new_name) { |w|
|
265
|
+
w.update_attributes(name: "Elizabeth")
|
266
|
+
}
|
267
|
+
expect(PaperTrail.whodunnit).to eq(orig_name)
|
319
268
|
end
|
320
269
|
|
321
|
-
|
322
|
-
|
323
|
-
widget
|
324
|
-
|
325
|
-
|
326
|
-
assert_equal 2, widget.versions.length
|
327
|
-
end
|
270
|
+
it "reverts to previous value, even if error within block" do
|
271
|
+
expect {
|
272
|
+
widget.paper_trail.whodunnit(new_name) { raise }
|
273
|
+
}.to raise_error(RuntimeError)
|
274
|
+
expect(PaperTrail.whodunnit).to eq(orig_name)
|
328
275
|
end
|
329
276
|
end
|
277
|
+
end
|
330
278
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
279
|
+
describe "#touch_with_version", versioning: true do
|
280
|
+
it "creates a version" do
|
281
|
+
count = widget.versions.size
|
282
|
+
# Travel 1 second because MySQL lacks sub-second resolution
|
283
|
+
Timecop.travel(1) do
|
284
|
+
widget.paper_trail.touch_with_version
|
336
285
|
end
|
286
|
+
expect(widget.versions.size).to eq(count + 1)
|
287
|
+
end
|
337
288
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
end
|
289
|
+
it "increments the `:updated_at` timestamp" do
|
290
|
+
time_was = widget.updated_at
|
291
|
+
# Travel 1 second because MySQL lacks sub-second resolution
|
292
|
+
Timecop.travel(1) do
|
293
|
+
widget.paper_trail.touch_with_version
|
344
294
|
end
|
295
|
+
expect(widget.updated_at).to be > time_was
|
296
|
+
end
|
297
|
+
end
|
345
298
|
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
299
|
+
describe "#update", versioning: true do
|
300
|
+
it "creates a version record" do
|
301
|
+
widget = Widget.create
|
302
|
+
assert_equal 1, widget.versions.length
|
303
|
+
widget.update_attributes(name: "Bugle")
|
304
|
+
assert_equal 2, widget.versions.length
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
describe ".paper_trail.enabled?" do
|
309
|
+
it "returns true" do
|
310
|
+
expect(Widget.paper_trail.enabled?).to eq(true)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
describe ".disable" do
|
315
|
+
it "sets the `paper_trail.enabled?` to `false`" do
|
316
|
+
expect(Widget.paper_trail.enabled?).to eq(true)
|
317
|
+
Widget.paper_trail.disable
|
318
|
+
expect(Widget.paper_trail.enabled?).to eq(false)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
describe ".enable" do
|
323
|
+
it "sets the `paper_trail.enabled?` to `true`" do
|
324
|
+
Widget.paper_trail.disable
|
325
|
+
expect(Widget.paper_trail.enabled?).to eq(false)
|
326
|
+
Widget.paper_trail.enable
|
327
|
+
expect(Widget.paper_trail.enabled?).to eq(true)
|
354
328
|
end
|
355
329
|
end
|
356
330
|
end
|