paper_trail 4.0.0.rc1 → 4.0.0.rc2
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/CHANGELOG.md +100 -58
- data/README.md +528 -346
- data/lib/generators/paper_trail/templates/add_object_changes_to_versions.rb +6 -1
- data/lib/generators/paper_trail/templates/create_versions.rb +8 -1
- data/lib/paper_trail.rb +2 -2
- data/lib/paper_trail/config.rb +15 -4
- data/lib/paper_trail/frameworks/active_record.rb +2 -3
- data/lib/paper_trail/has_paper_trail.rb +32 -13
- data/lib/paper_trail/version_concern.rb +1 -1
- data/lib/paper_trail/version_number.rb +1 -1
- data/paper_trail.gemspec +1 -1
- data/spec/models/gadget_spec.rb +1 -1
- data/spec/models/kitchen/banana_spec.rb +14 -0
- data/spec/models/not_on_update_spec.rb +19 -0
- data/spec/models/skipper_spec.rb +17 -0
- data/spec/models/version_spec.rb +5 -8
- data/spec/models/widget_spec.rb +5 -8
- data/test/dummy/app/models/kitchen/banana.rb +5 -0
- data/test/dummy/app/models/not_on_update.rb +4 -0
- data/test/dummy/app/models/skipper.rb +6 -0
- data/test/dummy/app/versions/kitchen/banana_version.rb +5 -0
- data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +25 -0
- data/test/paper_trail_test.rb +7 -0
- metadata +18 -24
- data/test/dummy/public/404.html +0 -26
- data/test/dummy/public/422.html +0 -26
- data/test/dummy/public/500.html +0 -26
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/javascripts/application.js +0 -2
- data/test/dummy/public/javascripts/controls.js +0 -965
- data/test/dummy/public/javascripts/dragdrop.js +0 -974
- data/test/dummy/public/javascripts/effects.js +0 -1123
- data/test/dummy/public/javascripts/rails.js +0 -175
- data/test/dummy/public/stylesheets/.gitkeep +0 -0
@@ -1,5 +1,10 @@
|
|
1
1
|
class AddObjectChangesToVersions < ActiveRecord::Migration
|
2
|
+
|
3
|
+
# The largest text column available in all supported RDBMS.
|
4
|
+
# See `create_versions.rb` for details.
|
5
|
+
TEXT_BYTES = 1_073_741_823
|
6
|
+
|
2
7
|
def change
|
3
|
-
add_column :versions, :object_changes, :text
|
8
|
+
add_column :versions, :object_changes, :text, limit: TEXT_BYTES
|
4
9
|
end
|
5
10
|
end
|
@@ -1,11 +1,18 @@
|
|
1
1
|
class CreateVersions < ActiveRecord::Migration
|
2
|
+
|
3
|
+
# The largest text column available in all supported RDBMS is
|
4
|
+
# 1024^3 - 1 bytes, roughly one gibibyte. We specify a size
|
5
|
+
# so that MySQL will use `longtext` instead of `text`. Otherwise,
|
6
|
+
# when serializing very large objects, `text` might not be big enough.
|
7
|
+
TEXT_BYTES = 1_073_741_823
|
8
|
+
|
2
9
|
def change
|
3
10
|
create_table :versions do |t|
|
4
11
|
t.string :item_type, :null => false
|
5
12
|
t.integer :item_id, :null => false
|
6
13
|
t.string :event, :null => false
|
7
14
|
t.string :whodunnit
|
8
|
-
t.text :object
|
15
|
+
t.text :object, :limit => TEXT_BYTES
|
9
16
|
t.datetime :created_at
|
10
17
|
end
|
11
18
|
add_index :versions, [:item_type, :item_id]
|
data/lib/paper_trail.rb
CHANGED
@@ -104,7 +104,7 @@ module PaperTrail
|
|
104
104
|
end
|
105
105
|
|
106
106
|
def self.transaction?
|
107
|
-
ActiveRecord::Base.connection.open_transactions > 0
|
107
|
+
::ActiveRecord::Base.connection.open_transactions > 0
|
108
108
|
end
|
109
109
|
|
110
110
|
def self.transaction_id
|
@@ -149,7 +149,7 @@ end
|
|
149
149
|
|
150
150
|
# Require frameworks
|
151
151
|
require 'paper_trail/frameworks/sinatra'
|
152
|
-
if defined? Rails
|
152
|
+
if defined? ::Rails
|
153
153
|
require 'paper_trail/frameworks/rails'
|
154
154
|
else
|
155
155
|
require 'paper_trail/frameworks/active_record'
|
data/lib/paper_trail/config.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
require 'singleton'
|
2
|
+
require 'paper_trail/serializers/yaml'
|
2
3
|
|
3
4
|
module PaperTrail
|
4
5
|
class Config
|
5
6
|
include Singleton
|
6
|
-
attr_accessor :
|
7
|
+
attr_accessor :timestamp_field, :serializer, :version_limit
|
7
8
|
attr_reader :serialized_attributes
|
8
9
|
attr_writer :track_associations
|
9
10
|
|
10
11
|
def initialize
|
11
|
-
@enabled = true # Indicates whether PaperTrail is on or off.
|
12
12
|
@timestamp_field = :created_at
|
13
13
|
@serializer = PaperTrail::Serializers::YAML
|
14
14
|
|
@@ -22,9 +22,11 @@ module PaperTrail
|
|
22
22
|
|
23
23
|
def serialized_attributes=(value)
|
24
24
|
if ::ActiveRecord::VERSION::MAJOR >= 5
|
25
|
-
warn(
|
25
|
+
::ActiveSupport::Deprecation.warn(
|
26
|
+
"ActiveRecord 5.0 deprecated `serialized_attributes` " +
|
26
27
|
"without replacement, so this PaperTrail config setting does " +
|
27
|
-
"nothing with this version, and is always turned off"
|
28
|
+
"nothing with this version, and is always turned off"
|
29
|
+
)
|
28
30
|
end
|
29
31
|
@serialized_attributes = value
|
30
32
|
end
|
@@ -33,5 +35,14 @@ module PaperTrail
|
|
33
35
|
@track_associations ||= PaperTrail::VersionAssociation.table_exists?
|
34
36
|
end
|
35
37
|
alias_method :track_associations?, :track_associations
|
38
|
+
|
39
|
+
# Indicates whether PaperTrail is on or off.
|
40
|
+
def enabled
|
41
|
+
PaperTrail.paper_trail_store[:paper_trail_enabled].nil? || PaperTrail.paper_trail_store[:paper_trail_enabled]
|
42
|
+
end
|
43
|
+
|
44
|
+
def enabled= enable
|
45
|
+
PaperTrail.paper_trail_store[:paper_trail_enabled] = enable
|
46
|
+
end
|
36
47
|
end
|
37
48
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
# This file only needs to be loaded if the gem is being used outside of Rails, since otherwise
|
2
2
|
# the model(s) will get loaded in via the `Rails::Engine`
|
3
|
-
|
4
|
-
|
5
|
-
end
|
3
|
+
require "paper_trail/frameworks/active_record/models/paper_trail/version_association"
|
4
|
+
require "paper_trail/frameworks/active_record/models/paper_trail/version"
|
@@ -192,7 +192,7 @@ module PaperTrail
|
|
192
192
|
end
|
193
193
|
|
194
194
|
def originator
|
195
|
-
warn "
|
195
|
+
::ActiveSupport::Deprecation.warn "Use paper_trail_originator instead of originator."
|
196
196
|
self.paper_trail_originator
|
197
197
|
end
|
198
198
|
|
@@ -266,11 +266,14 @@ module PaperTrail
|
|
266
266
|
PaperTrail.whodunnit = current_whodunnit
|
267
267
|
end
|
268
268
|
|
269
|
-
#
|
269
|
+
# Mimics the `touch` method from `ActiveRecord::Persistence`, but also
|
270
|
+
# creates a version. A version is created regardless of options such as
|
271
|
+
# `:on`, `:if`, or `:unless`.
|
270
272
|
#
|
271
|
-
# TODO:
|
272
|
-
#
|
273
|
-
#
|
273
|
+
# TODO: look into leveraging the `after_touch` callback from
|
274
|
+
# `ActiveRecord` to allow the regular `touch` method go generate a version
|
275
|
+
# as normal. May make sense to switch the `record_update` method to
|
276
|
+
# leverage an `after_update` callback anyways (likely for v4.0.0)
|
274
277
|
def touch_with_version(name = nil)
|
275
278
|
raise ActiveRecordError, "can not touch on a new record object" unless persisted?
|
276
279
|
|
@@ -279,13 +282,20 @@ module PaperTrail
|
|
279
282
|
current_time = current_time_from_proper_timezone
|
280
283
|
|
281
284
|
attributes.each { |column| write_attribute(column, current_time) }
|
282
|
-
|
283
|
-
record_update(true)
|
285
|
+
|
286
|
+
record_update(true) unless will_record_after_update?
|
284
287
|
save!(:validate => false)
|
285
288
|
end
|
286
289
|
|
287
290
|
private
|
288
291
|
|
292
|
+
# Returns true if `save` will cause `record_update`
|
293
|
+
# to be called via the `after_update` callback.
|
294
|
+
def will_record_after_update?
|
295
|
+
on = paper_trail_options[:on]
|
296
|
+
on.nil? || on.include?(:update)
|
297
|
+
end
|
298
|
+
|
289
299
|
def source_version
|
290
300
|
send self.class.version_association_name
|
291
301
|
end
|
@@ -459,18 +469,27 @@ module PaperTrail
|
|
459
469
|
attrs
|
460
470
|
end
|
461
471
|
|
462
|
-
# This method
|
463
|
-
#
|
464
|
-
#
|
465
|
-
#
|
472
|
+
# This method determines whether it is appropriate to generate a new
|
473
|
+
# version instance. A timestamp-only update (e.g. only `updated_at`
|
474
|
+
# changed) is considered notable unless an ignored attribute was also
|
475
|
+
# changed.
|
466
476
|
def changed_notably?
|
467
|
-
if
|
468
|
-
|
477
|
+
if ignored_attr_has_changed?
|
478
|
+
timestamps = timestamp_attributes_for_update_in_model.map(&:to_s)
|
479
|
+
(notably_changed - timestamps).any?
|
469
480
|
else
|
470
481
|
notably_changed.any?
|
471
482
|
end
|
472
483
|
end
|
473
484
|
|
485
|
+
# An attributed is "ignored" if it is listed in the `:ignore` option
|
486
|
+
# and/or the `:skip` option. Returns true if an ignored attribute
|
487
|
+
# has changed.
|
488
|
+
def ignored_attr_has_changed?
|
489
|
+
ignored = self.paper_trail_options[:ignore] + self.paper_trail_options[:skip]
|
490
|
+
ignored.any? && (changed & ignored).any?
|
491
|
+
end
|
492
|
+
|
474
493
|
def notably_changed
|
475
494
|
only = self.paper_trail_options[:only].dup
|
476
495
|
# remove Hash arguments and then evaluate whether the attributes (the keys of the hash) should also get pushed into the collection
|
@@ -257,7 +257,7 @@ module PaperTrail
|
|
257
257
|
end
|
258
258
|
|
259
259
|
def originator
|
260
|
-
warn "
|
260
|
+
::ActiveSupport::Deprecation.warn "Use paper_trail_originator instead of originator."
|
261
261
|
self.paper_trail_originator
|
262
262
|
end
|
263
263
|
|
data/paper_trail.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
|
|
21
21
|
|
22
22
|
s.add_dependency 'activerecord', ['>= 3.0', '< 6.0']
|
23
23
|
s.add_dependency 'activesupport', ['>= 3.0', '< 6.0']
|
24
|
-
s.add_dependency 'request_store', '~> 1.1
|
24
|
+
s.add_dependency 'request_store', '~> 1.1'
|
25
25
|
|
26
26
|
s.add_development_dependency 'rake', '~> 10.1.1'
|
27
27
|
s.add_development_dependency 'shoulda', '~> 3.5'
|
data/spec/models/gadget_spec.rb
CHANGED
@@ -45,7 +45,7 @@ describe Gadget, :type => :model do
|
|
45
45
|
expect(subject.send(:changed_notably?)).to be true
|
46
46
|
end
|
47
47
|
|
48
|
-
it "should not acknowledge ignored
|
48
|
+
it "should not acknowledge ignored attr (brand)" do
|
49
49
|
subject.brand = 'Acme'
|
50
50
|
expect(subject.send(:changed_notably?)).to be false
|
51
51
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
module Kitchen
|
4
|
+
describe Banana, :type => :model do
|
5
|
+
it { is_expected.to be_versioned }
|
6
|
+
|
7
|
+
describe '#versions' do
|
8
|
+
it "returns instances of Kitchen::BananaVersion", :versioning => true do
|
9
|
+
banana = described_class.create!
|
10
|
+
expect(banana.versions.first).to be_a(Kitchen::BananaVersion)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe NotOnUpdate, :type => :model do
|
4
|
+
describe "#touch_with_version", :versioning => true do
|
5
|
+
let!(:record) { described_class.create! }
|
6
|
+
|
7
|
+
it "should create a version, regardless" do
|
8
|
+
expect { record.touch_with_version }.to change {
|
9
|
+
PaperTrail::Version.count
|
10
|
+
}.by(+1)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "increments the `:updated_at` timestamp" do
|
14
|
+
before = record.updated_at
|
15
|
+
record.touch_with_version
|
16
|
+
expect(record.updated_at).to be > before
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe Skipper, :type => :model do
|
4
|
+
describe "#update_attributes!", :versioning => true do
|
5
|
+
context "updating a skipped attribute" do
|
6
|
+
let(:t1) { Time.zone.local(2015, 7, 15, 20, 34, 0) }
|
7
|
+
let(:t2) { Time.zone.local(2015, 7, 15, 20, 34, 30) }
|
8
|
+
|
9
|
+
it "should not create a version" do
|
10
|
+
skipper = Skipper.create!(:another_timestamp => t1)
|
11
|
+
expect {
|
12
|
+
skipper.update_attributes!(:another_timestamp => t2)
|
13
|
+
}.to_not change { skipper.versions.length }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/spec/models/version_spec.rb
CHANGED
@@ -58,10 +58,10 @@ describe PaperTrail::Version, :type => :model do
|
|
58
58
|
|
59
59
|
context "Has previous version", :versioning => true do
|
60
60
|
let(:name) { Faker::Name.name }
|
61
|
-
let(:widget) { Widget.create!(name
|
61
|
+
let(:widget) { Widget.create!(:name => Faker::Name.name) }
|
62
62
|
before do
|
63
63
|
widget.versions.first.update_attributes!(:whodunnit => name)
|
64
|
-
widget.update_attributes!(name
|
64
|
+
widget.update_attributes!(:name => Faker::Name.first_name)
|
65
65
|
end
|
66
66
|
subject { widget.versions.last }
|
67
67
|
|
@@ -75,19 +75,16 @@ describe PaperTrail::Version, :type => :model do
|
|
75
75
|
|
76
76
|
describe "#originator" do
|
77
77
|
it { is_expected.to respond_to(:originator) }
|
78
|
-
let(:warning_msg) do
|
79
|
-
"DEPRECATED: use `paper_trail_originator` instead of `originator`." +
|
80
|
-
" Support for `originator` will be removed in PaperTrail 4.0"
|
81
|
-
end
|
82
78
|
|
83
79
|
it 'should set the invoke `paper_trail_originator`' do
|
84
|
-
|
80
|
+
allow(ActiveSupport::Deprecation).to receive(:warn)
|
85
81
|
is_expected.to receive(:paper_trail_originator)
|
86
82
|
subject.originator
|
87
83
|
end
|
88
84
|
|
89
85
|
it 'should display a deprecation warning' do
|
90
|
-
|
86
|
+
expect(ActiveSupport::Deprecation).to receive(:warn).
|
87
|
+
with(/Use paper_trail_originator instead of originator/)
|
91
88
|
subject.originator
|
92
89
|
end
|
93
90
|
end
|
data/spec/models/widget_spec.rb
CHANGED
@@ -177,19 +177,16 @@ describe Widget, :type => :model do
|
|
177
177
|
subject { widget }
|
178
178
|
|
179
179
|
it { is_expected.to respond_to(:originator) }
|
180
|
-
let(:warning_msg) do
|
181
|
-
"DEPRECATED: use `paper_trail_originator` instead of `originator`." +
|
182
|
-
" Support for `originator` will be removed in PaperTrail 4.0"
|
183
|
-
end
|
184
180
|
|
185
181
|
it 'should set the invoke `paper_trail_originator`' do
|
186
|
-
|
182
|
+
allow(::ActiveSupport::Deprecation).to receive(:warn)
|
187
183
|
is_expected.to receive(:paper_trail_originator)
|
188
184
|
subject.originator
|
189
185
|
end
|
190
186
|
|
191
187
|
it 'should display a deprecation warning' do
|
192
|
-
|
188
|
+
expect(::ActiveSupport::Deprecation).to receive(:warn).
|
189
|
+
with(/Use paper_trail_originator instead of originator/)
|
193
190
|
subject.originator
|
194
191
|
end
|
195
192
|
end
|
@@ -252,13 +249,13 @@ describe Widget, :type => :model do
|
|
252
249
|
describe '#touch_with_version' do
|
253
250
|
it { is_expected.to respond_to(:touch_with_version) }
|
254
251
|
|
255
|
-
it "
|
252
|
+
it "creates a version" do
|
256
253
|
count = widget.versions.size
|
257
254
|
widget.touch_with_version
|
258
255
|
expect(widget.versions.size).to eq(count + 1)
|
259
256
|
end
|
260
257
|
|
261
|
-
it "
|
258
|
+
it "increments the `:updated_at` timestamp" do
|
262
259
|
time_was = widget.updated_at
|
263
260
|
widget.touch_with_version
|
264
261
|
expect(widget.updated_at).to be > time_was
|
@@ -1,5 +1,10 @@
|
|
1
1
|
class SetUpTestTables < ActiveRecord::Migration
|
2
2
|
def self.up
|
3
|
+
create_table :skippers, :force => true do |t|
|
4
|
+
t.datetime :another_timestamp
|
5
|
+
t.timestamps :null => true
|
6
|
+
end
|
7
|
+
|
3
8
|
create_table :widgets, :force => true do |t|
|
4
9
|
t.string :name
|
5
10
|
t.text :a_text
|
@@ -73,6 +78,24 @@ class SetUpTestTables < ActiveRecord::Migration
|
|
73
78
|
add_index :json_versions, [:item_type, :item_id]
|
74
79
|
end
|
75
80
|
|
81
|
+
create_table :not_on_updates, :force => true do |t|
|
82
|
+
t.timestamps :null => true
|
83
|
+
end
|
84
|
+
|
85
|
+
create_table :bananas, :force => true do |t|
|
86
|
+
t.timestamps :null => true
|
87
|
+
end
|
88
|
+
|
89
|
+
create_table :banana_versions, :force => true do |t|
|
90
|
+
t.string :item_type, :null => false
|
91
|
+
t.integer :item_id, :null => false
|
92
|
+
t.string :event, :null => false
|
93
|
+
t.string :whodunnit
|
94
|
+
t.text :object
|
95
|
+
t.datetime :created_at
|
96
|
+
end
|
97
|
+
add_index :banana_versions, [:item_type, :item_id]
|
98
|
+
|
76
99
|
create_table :wotsits, :force => true do |t|
|
77
100
|
t.integer :widget_id
|
78
101
|
t.string :name
|
@@ -191,6 +214,8 @@ class SetUpTestTables < ActiveRecord::Migration
|
|
191
214
|
|
192
215
|
def self.down
|
193
216
|
drop_table :animals
|
217
|
+
drop_table :skippers
|
218
|
+
drop_table :not_on_updates
|
194
219
|
drop_table :posts
|
195
220
|
drop_table :songs
|
196
221
|
drop_table :editors
|
data/test/paper_trail_test.rb
CHANGED
@@ -8,6 +8,13 @@ class PaperTrailTest < ActiveSupport::TestCase
|
|
8
8
|
test 'Version Number' do
|
9
9
|
assert PaperTrail.const_defined?(:VERSION)
|
10
10
|
end
|
11
|
+
|
12
|
+
test 'enabled is thread-safe' do
|
13
|
+
Thread.new do
|
14
|
+
PaperTrail.enabled = false
|
15
|
+
end.join
|
16
|
+
assert PaperTrail.enabled?
|
17
|
+
end
|
11
18
|
|
12
19
|
test 'create with plain model class' do
|
13
20
|
widget = Widget.create
|