paper_trail 4.2.0 → 5.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/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +28 -9
- data/.github/ISSUE_TEMPLATE.md +13 -0
- data/.gitignore +2 -1
- data/.rubocop.yml +100 -0
- data/.rubocop_todo.yml +14 -0
- data/.travis.yml +8 -9
- data/Appraisals +41 -0
- data/CHANGELOG.md +49 -9
- data/Gemfile +1 -1
- data/README.md +130 -109
- data/Rakefile +19 -19
- data/doc/bug_report_template.rb +20 -14
- data/gemfiles/ar3.gemfile +10 -53
- data/gemfiles/ar4.gemfile +7 -0
- data/gemfiles/ar5.gemfile +13 -0
- data/lib/generators/paper_trail/install_generator.rb +26 -18
- data/lib/generators/paper_trail/templates/add_object_changes_to_versions.rb +4 -2
- data/lib/generators/paper_trail/templates/add_transaction_id_column_to_versions.rb +2 -0
- data/lib/generators/paper_trail/templates/create_version_associations.rb +9 -4
- data/lib/generators/paper_trail/templates/create_versions.rb +39 -5
- data/lib/paper_trail.rb +169 -146
- data/lib/paper_trail/attributes_serialization.rb +89 -17
- data/lib/paper_trail/cleaner.rb +15 -9
- data/lib/paper_trail/config.rb +28 -11
- data/lib/paper_trail/frameworks/active_record.rb +4 -0
- data/lib/paper_trail/frameworks/active_record/models/paper_trail/version.rb +5 -1
- data/lib/paper_trail/frameworks/active_record/models/paper_trail/version_association.rb +6 -2
- data/lib/paper_trail/frameworks/cucumber.rb +1 -0
- data/lib/paper_trail/frameworks/rails.rb +2 -7
- data/lib/paper_trail/frameworks/rails/controller.rb +29 -9
- data/lib/paper_trail/frameworks/rails/engine.rb +7 -1
- data/lib/paper_trail/frameworks/rspec.rb +5 -5
- data/lib/paper_trail/frameworks/rspec/helpers.rb +3 -1
- data/lib/paper_trail/frameworks/sinatra.rb +6 -4
- data/lib/paper_trail/has_paper_trail.rb +199 -106
- data/lib/paper_trail/record_history.rb +1 -3
- data/lib/paper_trail/reifier.rb +297 -118
- data/lib/paper_trail/serializers/json.rb +3 -3
- data/lib/paper_trail/serializers/yaml.rb +27 -8
- data/lib/paper_trail/version_association_concern.rb +3 -1
- data/lib/paper_trail/version_concern.rb +75 -35
- data/lib/paper_trail/version_number.rb +6 -9
- data/paper_trail.gemspec +44 -51
- data/spec/generators/install_generator_spec.rb +24 -25
- data/spec/generators/paper_trail/templates/create_versions_spec.rb +51 -0
- data/spec/models/animal_spec.rb +12 -12
- data/spec/models/boolit_spec.rb +8 -8
- data/spec/models/callback_modifier_spec.rb +47 -47
- data/spec/models/car_spec.rb +13 -0
- data/spec/models/fluxor_spec.rb +3 -3
- data/spec/models/gadget_spec.rb +19 -19
- data/spec/models/joined_version_spec.rb +3 -3
- data/spec/models/json_version_spec.rb +23 -24
- data/spec/models/kitchen/banana_spec.rb +3 -3
- data/spec/models/not_on_update_spec.rb +7 -4
- data/spec/models/post_with_status_spec.rb +13 -3
- data/spec/models/skipper_spec.rb +10 -10
- data/spec/models/thing_spec.rb +4 -4
- data/spec/models/truck_spec.rb +5 -0
- data/spec/models/vehicle_spec.rb +5 -0
- data/spec/models/version_spec.rb +103 -59
- data/spec/models/widget_spec.rb +82 -52
- data/spec/modules/paper_trail_spec.rb +2 -2
- data/spec/modules/version_concern_spec.rb +11 -12
- data/spec/modules/version_number_spec.rb +2 -4
- data/spec/paper_trail/config_spec.rb +10 -29
- data/spec/paper_trail_spec.rb +16 -14
- data/spec/rails_helper.rb +10 -9
- data/spec/requests/articles_spec.rb +11 -7
- data/spec/spec_helper.rb +41 -22
- data/spec/support/alt_db_init.rb +8 -13
- data/test/custom_json_serializer.rb +3 -3
- data/test/dummy/Rakefile +2 -2
- data/test/dummy/app/controllers/application_controller.rb +21 -8
- data/test/dummy/app/controllers/articles_controller.rb +11 -8
- data/test/dummy/app/controllers/widgets_controller.rb +13 -12
- data/test/dummy/app/models/animal.rb +1 -1
- data/test/dummy/app/models/article.rb +19 -11
- data/test/dummy/app/models/authorship.rb +1 -1
- data/test/dummy/app/models/bar_habtm.rb +4 -0
- data/test/dummy/app/models/book.rb +4 -4
- data/test/dummy/app/models/boolit.rb +1 -1
- data/test/dummy/app/models/callback_modifier.rb +6 -6
- data/test/dummy/app/models/car.rb +3 -0
- data/test/dummy/app/models/chapter.rb +4 -4
- data/test/dummy/app/models/customer.rb +1 -1
- data/test/dummy/app/models/document.rb +2 -2
- data/test/dummy/app/models/editor.rb +1 -1
- data/test/dummy/app/models/foo_habtm.rb +4 -0
- data/test/dummy/app/models/fruit.rb +2 -2
- data/test/dummy/app/models/gadget.rb +1 -1
- data/test/dummy/app/models/kitchen/banana.rb +1 -1
- data/test/dummy/app/models/legacy_widget.rb +2 -2
- data/test/dummy/app/models/line_item.rb +1 -1
- data/test/dummy/app/models/not_on_update.rb +1 -1
- data/test/dummy/app/models/person.rb +6 -6
- data/test/dummy/app/models/post.rb +1 -1
- data/test/dummy/app/models/post_with_status.rb +1 -1
- data/test/dummy/app/models/quotation.rb +1 -1
- data/test/dummy/app/models/section.rb +1 -1
- data/test/dummy/app/models/skipper.rb +2 -2
- data/test/dummy/app/models/song.rb +13 -4
- data/test/dummy/app/models/thing.rb +2 -2
- data/test/dummy/app/models/translation.rb +2 -2
- data/test/dummy/app/models/truck.rb +4 -0
- data/test/dummy/app/models/vehicle.rb +4 -0
- data/test/dummy/app/models/whatchamajigger.rb +1 -1
- data/test/dummy/app/models/widget.rb +7 -6
- data/test/dummy/app/versions/joined_version.rb +4 -3
- data/test/dummy/app/versions/json_version.rb +1 -1
- data/test/dummy/app/versions/kitchen/banana_version.rb +1 -1
- data/test/dummy/app/versions/post_version.rb +2 -2
- data/test/dummy/config.ru +1 -1
- data/test/dummy/config/application.rb +20 -9
- data/test/dummy/config/boot.rb +5 -5
- data/test/dummy/config/environment.rb +1 -1
- data/test/dummy/config/environments/development.rb +4 -3
- data/test/dummy/config/environments/production.rb +3 -2
- data/test/dummy/config/environments/test.rb +15 -5
- data/test/dummy/config/initializers/backtrace_silencers.rb +4 -2
- data/test/dummy/config/initializers/paper_trail.rb +1 -2
- data/test/dummy/config/initializers/secret_token.rb +3 -1
- data/test/dummy/config/initializers/session_store.rb +1 -1
- data/test/dummy/config/routes.rb +2 -2
- data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +120 -74
- data/test/dummy/db/schema.rb +29 -6
- data/test/dummy/script/rails +6 -4
- data/test/functional/controller_test.rb +34 -35
- data/test/functional/enabled_for_controller_test.rb +6 -7
- data/test/functional/modular_sinatra_test.rb +43 -38
- data/test/functional/sinatra_test.rb +49 -40
- data/test/functional/thread_safety_test.rb +4 -6
- data/test/paper_trail_test.rb +15 -14
- data/test/test_helper.rb +68 -44
- data/test/time_travel_helper.rb +1 -15
- data/test/unit/associations_test.rb +517 -251
- data/test/unit/cleaner_test.rb +66 -60
- data/test/unit/inheritance_column_test.rb +17 -17
- data/test/unit/model_test.rb +611 -504
- data/test/unit/protected_attrs_test.rb +16 -12
- data/test/unit/serializer_test.rb +44 -43
- data/test/unit/serializers/json_test.rb +17 -18
- data/test/unit/serializers/mixin_json_test.rb +15 -14
- data/test/unit/serializers/mixin_yaml_test.rb +20 -16
- data/test/unit/serializers/yaml_test.rb +12 -13
- data/test/unit/timestamp_test.rb +10 -12
- data/test/unit/version_test.rb +7 -7
- metadata +92 -40
data/Rakefile
CHANGED
@@ -1,30 +1,30 @@
|
|
1
|
-
require
|
1
|
+
require "bundler"
|
2
2
|
Bundler::GemHelper.install_tasks
|
3
3
|
|
4
|
-
desc
|
4
|
+
desc "Set a relevant database.yml for testing"
|
5
5
|
task :prepare do
|
6
6
|
ENV["DB"] ||= "sqlite"
|
7
|
-
|
8
|
-
FileUtils.cp "test/dummy/config/database.#{ENV["DB"]}.yml", "test/dummy/config/database.yml"
|
9
|
-
else
|
10
|
-
require 'ftools'
|
11
|
-
File.cp "test/dummy/config/database.#{ENV["DB"]}.yml", "test/dummy/config/database.yml"
|
12
|
-
end
|
7
|
+
FileUtils.cp "test/dummy/config/database.#{ENV['DB']}.yml", "test/dummy/config/database.yml"
|
13
8
|
end
|
14
9
|
|
15
|
-
|
16
|
-
|
17
|
-
desc 'Run tests on PaperTrail with Test::Unit.'
|
10
|
+
require "rake/testtask"
|
11
|
+
desc "Run tests on PaperTrail with Test::Unit."
|
18
12
|
Rake::TestTask.new(:test) do |t|
|
19
|
-
t.libs <<
|
20
|
-
t.libs <<
|
21
|
-
t.pattern =
|
13
|
+
t.libs << "lib"
|
14
|
+
t.libs << "test"
|
15
|
+
t.pattern = "test/**/*_test.rb"
|
22
16
|
t.verbose = false
|
23
17
|
end
|
24
18
|
|
25
|
-
require
|
26
|
-
desc
|
27
|
-
|
19
|
+
require "rspec/core/rake_task"
|
20
|
+
desc "Run tests on PaperTrail with RSpec"
|
21
|
+
task(:spec).clear
|
22
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
23
|
+
t.verbose = false # hide list of specs bit.ly/1nVq3Jn
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubocop/rake_task"
|
27
|
+
RuboCop::RakeTask.new
|
28
28
|
|
29
|
-
desc
|
30
|
-
task :
|
29
|
+
desc "Default: run all available test suites"
|
30
|
+
task default: [:rubocop, :prepare, :test, :spec]
|
data/doc/bug_report_template.rb
CHANGED
@@ -2,27 +2,26 @@
|
|
2
2
|
# It is based on the ActiveRecord template.
|
3
3
|
# https://github.com/rails/rails/blob/master/guides/bug_report_templates/active_record_gem.rb
|
4
4
|
begin
|
5
|
-
require
|
5
|
+
require "bundler/inline"
|
6
6
|
rescue LoadError => e
|
7
|
-
$stderr.puts
|
7
|
+
$stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
|
8
8
|
raise e
|
9
9
|
end
|
10
10
|
|
11
11
|
gemfile(true) do
|
12
|
-
ruby
|
13
|
-
source
|
14
|
-
gem
|
15
|
-
gem
|
16
|
-
gem
|
17
|
-
gem
|
12
|
+
ruby "2.2.3"
|
13
|
+
source "https://rubygems.org"
|
14
|
+
gem "activerecord", "4.2.0"
|
15
|
+
gem "minitest", "5.8.3"
|
16
|
+
gem "paper_trail", "4.0.0", require: false
|
17
|
+
gem "sqlite3"
|
18
18
|
end
|
19
19
|
|
20
|
-
require
|
21
|
-
require
|
22
|
-
require
|
23
|
-
ActiveRecord::Base.establish_connection(adapter:
|
20
|
+
require "active_record"
|
21
|
+
require "minitest/autorun"
|
22
|
+
require "logger"
|
23
|
+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
24
24
|
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
25
|
-
require 'paper_trail'
|
26
25
|
|
27
26
|
ActiveRecord::Schema.define do
|
28
27
|
create_table :users, force: true do |t|
|
@@ -49,13 +48,20 @@ ActiveRecord::Schema.define do
|
|
49
48
|
end
|
50
49
|
add_index :version_associations, [:version_id]
|
51
50
|
add_index :version_associations, [:foreign_key_name, :foreign_key_id],
|
52
|
-
name:
|
51
|
+
name: "index_version_associations_on_foreign_key"
|
53
52
|
end
|
54
53
|
|
54
|
+
# Require `paper_trail.rb` after the `version_associations` table
|
55
|
+
# exists so that PT will track associations.
|
56
|
+
require "paper_trail"
|
57
|
+
|
58
|
+
# Include your models here. Please only include the minimum code necessary to
|
59
|
+
# reproduce your issue.
|
55
60
|
class User < ActiveRecord::Base
|
56
61
|
has_paper_trail
|
57
62
|
end
|
58
63
|
|
64
|
+
# Please write a test that demonstrates your issue by failing.
|
59
65
|
class BugTest < ActiveSupport::TestCase
|
60
66
|
def test_1
|
61
67
|
assert_difference(-> { PaperTrail::Version.count }, +1) {
|
data/gemfiles/ar3.gemfile
CHANGED
@@ -1,61 +1,18 @@
|
|
1
|
-
|
1
|
+
# This file was generated by Appraisal
|
2
2
|
|
3
|
-
|
4
|
-
gem 'request_store', '~> 1.1.0'
|
3
|
+
source "https://rubygems.org"
|
5
4
|
|
6
|
-
|
7
|
-
gem
|
8
|
-
|
9
|
-
# actionpack 3 depends on rack-cache ~> 1.2, but bundler seems to ignore that.
|
10
|
-
# Also rack-cache 1.3 dropped support for ruby 1.8.7. The simplest thing for now
|
11
|
-
# was to specify rack-cache ~> 1.2 here, though it should be unnecessary given
|
12
|
-
# the actionpack 3 dependency.
|
13
|
-
gem 'rack-cache', '~> 1.2.0'
|
5
|
+
gem "activerecord", "~> 3.2.22"
|
6
|
+
gem "i18n", "~> 0.6.11"
|
7
|
+
gem "request_store", "~> 1.1.0"
|
14
8
|
|
15
9
|
group :development, :test do
|
16
|
-
gem
|
17
|
-
gem
|
18
|
-
gem 'ffaker', '<= 1.31.0'
|
19
|
-
|
20
|
-
# Testing of Sinatra
|
21
|
-
gem 'sinatra', '~> 1.1.4'
|
22
|
-
|
23
|
-
# RSpec testing
|
24
|
-
gem 'rspec-rails', '~> 3.4.2'
|
25
|
-
gem 'generator_spec'
|
26
|
-
|
27
|
-
# To do proper transactional testing with ActiveSupport::TestCase on MySQL
|
28
|
-
gem 'database_cleaner', '~> 1.2.0'
|
29
|
-
|
30
|
-
# Allow time travel in testing. timecop is only supported after 1.9.2 but does a better cleanup at 'return'
|
31
|
-
if RUBY_VERSION < "1.9.2"
|
32
|
-
gem 'delorean'
|
33
|
-
else
|
34
|
-
gem 'timecop'
|
35
|
-
end
|
10
|
+
gem "railties", "~> 3.2.22"
|
11
|
+
gem "test-unit", "~> 3.1.5"
|
36
12
|
|
37
13
|
platforms :ruby do
|
38
|
-
gem
|
39
|
-
|
40
|
-
# We would prefer to only constrain mysql2 to '~> 0.3',
|
41
|
-
# but a rails bug (https://github.com/rails/rails/issues/21544)
|
42
|
-
# requires us to constrain to '~> 0.3.20' for now.
|
43
|
-
gem 'mysql2', '~> 0.3.20'
|
44
|
-
|
45
|
-
gem 'pg', '~> 0.17.1'
|
46
|
-
end
|
47
|
-
|
48
|
-
platforms :jruby, :ruby_18 do
|
49
|
-
# shoulda-matchers > 2.0 is not compatible with Ruby18.
|
50
|
-
# Since we can't specify difference between JRuby 18/19, we need to use shoulda-matchers 1.5 for all JRuby testing.
|
51
|
-
gem 'shoulda-matchers', '~> 1.5'
|
52
|
-
end
|
53
|
-
|
54
|
-
platforms :jruby do
|
55
|
-
# Use jRuby's sqlite3 adapter for jRuby
|
56
|
-
gem 'activerecord-jdbcsqlite3-adapter', '~> 1.3'
|
57
|
-
gem 'activerecord-jdbcpostgresql-adapter', '~> 1.3'
|
58
|
-
gem 'activerecord-jdbcmysql-adapter', '~> 1.3'
|
59
|
-
gem 'activerecord-jdbc-adapter', '1.3.15'
|
14
|
+
gem "mysql2", "~> 0.3.20"
|
60
15
|
end
|
61
16
|
end
|
17
|
+
|
18
|
+
gemspec :path => "../"
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "activerecord", "5.0.0.beta3"
|
6
|
+
gem "activemodel", "5.0.0.beta3"
|
7
|
+
gem "actionpack", "5.0.0.beta3"
|
8
|
+
gem "railties", "5.0.0.beta3"
|
9
|
+
gem "rspec-rails", "3.5.0.beta3"
|
10
|
+
gem "rails-controller-testing"
|
11
|
+
gem "sinatra", :github => "sinatra/sinatra"
|
12
|
+
|
13
|
+
gemspec :path => "../"
|
@@ -1,24 +1,33 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "rails/generators"
|
2
|
+
require "rails/generators/active_record"
|
3
3
|
|
4
4
|
module PaperTrail
|
5
|
+
# Installs PaperTrail in a rails app.
|
5
6
|
class InstallGenerator < ::Rails::Generators::Base
|
6
7
|
include ::Rails::Generators::Migration
|
7
8
|
|
8
|
-
source_root File.expand_path(
|
9
|
-
class_option
|
10
|
-
:
|
11
|
-
|
12
|
-
:
|
9
|
+
source_root File.expand_path("../templates", __FILE__)
|
10
|
+
class_option(
|
11
|
+
:with_changes,
|
12
|
+
type: :boolean,
|
13
|
+
default: false,
|
14
|
+
desc: "Store changeset (diff) with each version"
|
15
|
+
)
|
16
|
+
class_option(
|
17
|
+
:with_associations,
|
18
|
+
type: :boolean,
|
19
|
+
default: false,
|
20
|
+
desc: "Store transactional IDs to support association restoration"
|
21
|
+
)
|
13
22
|
|
14
|
-
desc
|
23
|
+
desc "Generates (but does not run) a migration to add a versions table."
|
15
24
|
|
16
25
|
def create_migration_file
|
17
|
-
add_paper_trail_migration(
|
18
|
-
add_paper_trail_migration(
|
26
|
+
add_paper_trail_migration("create_versions")
|
27
|
+
add_paper_trail_migration("add_object_changes_to_versions") if options.with_changes?
|
19
28
|
if options.with_associations?
|
20
|
-
add_paper_trail_migration(
|
21
|
-
add_paper_trail_migration(
|
29
|
+
add_paper_trail_migration("create_version_associations")
|
30
|
+
add_paper_trail_migration("add_transaction_id_column_to_versions")
|
22
31
|
end
|
23
32
|
end
|
24
33
|
|
@@ -27,14 +36,13 @@ module PaperTrail
|
|
27
36
|
end
|
28
37
|
|
29
38
|
protected
|
30
|
-
def add_paper_trail_migration(template)
|
31
|
-
migration_dir = File.expand_path('db/migrate')
|
32
39
|
|
33
|
-
|
34
|
-
|
40
|
+
def add_paper_trail_migration(template)
|
41
|
+
migration_dir = File.expand_path("db/migrate")
|
42
|
+
if self.class.migration_exists?(migration_dir, template)
|
43
|
+
warn "Migration already exists: #{template}"
|
35
44
|
else
|
36
|
-
|
37
|
-
" Please check your migrations directory before re-running")
|
45
|
+
migration_template "#{template}.rb", "db/migrate/#{template}.rb"
|
38
46
|
end
|
39
47
|
end
|
40
48
|
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# This migration adds the optional `object_changes` column, in which PaperTrail
|
2
|
+
# will store the `changes` diff for each update event. See the readme for
|
3
|
+
# details.
|
1
4
|
class AddObjectChangesToVersions < ActiveRecord::Migration
|
2
|
-
|
3
5
|
# The largest text column available in all supported RDBMS.
|
4
6
|
# See `create_versions.rb` for details.
|
5
7
|
TEXT_BYTES = 1_073_741_823
|
6
8
|
|
7
9
|
def change
|
8
|
-
add_column :versions, :object_changes, :text, :
|
10
|
+
add_column :versions, :object_changes, :text, limit: TEXT_BYTES
|
9
11
|
end
|
10
12
|
end
|
@@ -1,17 +1,22 @@
|
|
1
|
+
# This migration and AddTransactionIdColumnToVersions provide the necessary
|
2
|
+
# schema for tracking associations.
|
1
3
|
class CreateVersionAssociations < ActiveRecord::Migration
|
2
4
|
def self.up
|
3
5
|
create_table :version_associations do |t|
|
4
6
|
t.integer :version_id
|
5
|
-
t.string :foreign_key_name, :
|
7
|
+
t.string :foreign_key_name, null: false
|
6
8
|
t.integer :foreign_key_id
|
7
9
|
end
|
8
10
|
add_index :version_associations, [:version_id]
|
9
|
-
add_index :version_associations,
|
11
|
+
add_index :version_associations,
|
12
|
+
[:foreign_key_name, :foreign_key_id],
|
13
|
+
name: "index_version_associations_on_foreign_key"
|
10
14
|
end
|
11
15
|
|
12
16
|
def self.down
|
13
17
|
remove_index :version_associations, [:version_id]
|
14
|
-
remove_index :version_associations,
|
18
|
+
remove_index :version_associations,
|
19
|
+
name: "index_version_associations_on_foreign_key"
|
15
20
|
drop_table :version_associations
|
16
21
|
end
|
17
|
-
end
|
22
|
+
end
|
@@ -1,4 +1,13 @@
|
|
1
|
+
# This migration creates the `versions` table, the only schema PT requires.
|
2
|
+
# All other migrations PT provides are optional.
|
1
3
|
class CreateVersions < ActiveRecord::Migration
|
4
|
+
# Class names of MySQL adapters.
|
5
|
+
# - `MysqlAdapter` - Used by gems: `mysql`, `activerecord-jdbcmysql-adapter`.
|
6
|
+
# - `Mysql2Adapter` - Used by `mysql2` gem.
|
7
|
+
MYSQL_ADAPTERS = [
|
8
|
+
"ActiveRecord::ConnectionAdapters::MysqlAdapter",
|
9
|
+
"ActiveRecord::ConnectionAdapters::Mysql2Adapter"
|
10
|
+
].freeze
|
2
11
|
|
3
12
|
# The largest text column available in all supported RDBMS is
|
4
13
|
# 1024^3 - 1 bytes, roughly one gibibyte. We specify a size
|
@@ -7,12 +16,12 @@ class CreateVersions < ActiveRecord::Migration
|
|
7
16
|
TEXT_BYTES = 1_073_741_823
|
8
17
|
|
9
18
|
def change
|
10
|
-
create_table :versions do |t|
|
11
|
-
t.string :item_type, :
|
12
|
-
t.integer :item_id, :
|
13
|
-
t.string :event, :
|
19
|
+
create_table :versions, versions_table_options do |t|
|
20
|
+
t.string :item_type, null: false
|
21
|
+
t.integer :item_id, null: false
|
22
|
+
t.string :event, null: false
|
14
23
|
t.string :whodunnit
|
15
|
-
t.text :object,
|
24
|
+
t.text :object, limit: TEXT_BYTES
|
16
25
|
|
17
26
|
# Known issue in MySQL: fractional second precision
|
18
27
|
# -------------------------------------------------
|
@@ -31,4 +40,29 @@ class CreateVersions < ActiveRecord::Migration
|
|
31
40
|
end
|
32
41
|
add_index :versions, [:item_type, :item_id]
|
33
42
|
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Even modern versions of MySQL still use `latin1` as the default character
|
47
|
+
# encoding. Many users are not aware of this, and run into trouble when they
|
48
|
+
# try to use PaperTrail in apps that otherwise tend to use UTF-8. Postgres, by
|
49
|
+
# comparison, uses UTF-8 except in the unusual case where the OS is configured
|
50
|
+
# with a custom locale.
|
51
|
+
#
|
52
|
+
# - https://dev.mysql.com/doc/refman/5.7/en/charset-applications.html
|
53
|
+
# - http://www.postgresql.org/docs/9.4/static/multibyte.html
|
54
|
+
#
|
55
|
+
# Furthermore, MySQL's original implementation of UTF-8 was flawed, and had
|
56
|
+
# to be fixed later by introducing a new charset, `utf8mb4`.
|
57
|
+
#
|
58
|
+
# - https://mathiasbynens.be/notes/mysql-utf8mb4
|
59
|
+
# - https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html
|
60
|
+
#
|
61
|
+
def versions_table_options
|
62
|
+
if MYSQL_ADAPTERS.include?(connection.class.name)
|
63
|
+
{ options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci" }
|
64
|
+
else
|
65
|
+
{}
|
66
|
+
end
|
67
|
+
end
|
34
68
|
end
|
data/lib/paper_trail.rb
CHANGED
@@ -1,162 +1,185 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
1
|
+
require "request_store"
|
2
|
+
require "paper_trail/attributes_serialization"
|
3
|
+
require "paper_trail/cleaner"
|
4
|
+
require "paper_trail/config"
|
5
|
+
require "paper_trail/has_paper_trail"
|
6
|
+
require "paper_trail/record_history"
|
7
|
+
require "paper_trail/reifier"
|
8
|
+
require "paper_trail/version_association_concern"
|
9
|
+
require "paper_trail/version_concern"
|
10
|
+
require "paper_trail/version_number"
|
11
|
+
require "paper_trail/serializers/json"
|
12
|
+
require "paper_trail/serializers/yaml"
|
13
|
+
|
14
|
+
# An ActiveRecord extension that tracks changes to your models, for auditing or
|
15
|
+
# versioning.
|
13
16
|
module PaperTrail
|
14
17
|
extend PaperTrail::Cleaner
|
15
18
|
|
16
|
-
# Switches PaperTrail on or off.
|
17
|
-
def self.enabled=(value)
|
18
|
-
PaperTrail.config.enabled = value
|
19
|
-
end
|
20
|
-
|
21
|
-
# Returns `true` if PaperTrail is on, `false` otherwise.
|
22
|
-
# PaperTrail is enabled by default.
|
23
|
-
def self.enabled?
|
24
|
-
!!PaperTrail.config.enabled
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.serialized_attributes?
|
28
|
-
ActiveSupport::Deprecation.warn(
|
29
|
-
"PaperTrail.serialized_attributes? is deprecated without replacement " +
|
30
|
-
"and always returns false."
|
31
|
-
)
|
32
|
-
false
|
33
|
-
end
|
34
|
-
|
35
|
-
# Sets whether PaperTrail is enabled or disabled for the current request.
|
36
|
-
def self.enabled_for_controller=(value)
|
37
|
-
paper_trail_store[:request_enabled_for_controller] = value
|
38
|
-
end
|
39
|
-
|
40
|
-
# Returns `true` if PaperTrail is enabled for the request, `false` otherwise.
|
41
|
-
#
|
42
|
-
# See `PaperTrail::Rails::Controller#paper_trail_enabled_for_controller`.
|
43
|
-
def self.enabled_for_controller?
|
44
|
-
!!paper_trail_store[:request_enabled_for_controller]
|
45
|
-
end
|
46
|
-
|
47
|
-
# Sets whether PaperTrail is enabled or disabled for this model in the
|
48
|
-
# current request.
|
49
|
-
def self.enabled_for_model(model, value)
|
50
|
-
paper_trail_store[:"enabled_for_#{model}"] = value
|
51
|
-
end
|
52
|
-
|
53
|
-
# Returns `true` if PaperTrail is enabled for this model in the current
|
54
|
-
# request, `false` otherwise.
|
55
|
-
def self.enabled_for_model?(model)
|
56
|
-
!!paper_trail_store.fetch(:"enabled_for_#{model}", true)
|
57
|
-
end
|
58
|
-
|
59
|
-
# Set the field which records when a version was created.
|
60
|
-
def self.timestamp_field=(field_name)
|
61
|
-
PaperTrail.config.timestamp_field = field_name
|
62
|
-
end
|
63
|
-
|
64
|
-
# Returns the field which records when a version was created.
|
65
|
-
def self.timestamp_field
|
66
|
-
PaperTrail.config.timestamp_field
|
67
|
-
end
|
68
|
-
|
69
|
-
# Sets who is responsible for any changes that occur. You would normally use
|
70
|
-
# this in a migration or on the console, when working with models directly.
|
71
|
-
# In a controller it is set automatically to the `current_user`.
|
72
|
-
def self.whodunnit=(value)
|
73
|
-
paper_trail_store[:whodunnit] = value
|
74
|
-
end
|
75
|
-
|
76
|
-
# Returns who is reponsible for any changes that occur.
|
77
|
-
def self.whodunnit
|
78
|
-
paper_trail_store[:whodunnit]
|
79
|
-
end
|
80
|
-
|
81
|
-
# Sets any information from the controller that you want PaperTrail to
|
82
|
-
# store. By default this is set automatically by a before filter.
|
83
|
-
def self.controller_info=(value)
|
84
|
-
paper_trail_store[:controller_info] = value
|
85
|
-
end
|
86
|
-
|
87
|
-
# Returns any information from the controller that you want
|
88
|
-
# PaperTrail to store.
|
89
|
-
#
|
90
|
-
# See `PaperTrail::Rails::Controller#info_for_paper_trail`.
|
91
|
-
def self.controller_info
|
92
|
-
paper_trail_store[:controller_info]
|
93
|
-
end
|
94
|
-
|
95
|
-
# Getter and Setter for PaperTrail Serializer
|
96
|
-
def self.serializer=(value)
|
97
|
-
PaperTrail.config.serializer = value
|
98
|
-
end
|
99
|
-
|
100
|
-
def self.serializer
|
101
|
-
PaperTrail.config.serializer
|
102
|
-
end
|
103
|
-
|
104
|
-
def self.active_record_protected_attributes?
|
105
|
-
@active_record_protected_attributes ||= ::ActiveRecord::VERSION::MAJOR < 4 ||
|
106
|
-
!!defined?(ProtectedAttributes)
|
107
|
-
end
|
108
|
-
|
109
|
-
def self.transaction?
|
110
|
-
::ActiveRecord::Base.connection.open_transactions > 0
|
111
|
-
end
|
112
|
-
|
113
|
-
def self.transaction_id
|
114
|
-
paper_trail_store[:transaction_id]
|
115
|
-
end
|
116
|
-
|
117
|
-
def self.transaction_id=(id)
|
118
|
-
paper_trail_store[:transaction_id] = id
|
119
|
-
end
|
120
|
-
|
121
|
-
private
|
122
|
-
|
123
|
-
# Thread-safe hash to hold PaperTrail's data. Initializing with needed
|
124
|
-
# default values.
|
125
|
-
def self.paper_trail_store
|
126
|
-
RequestStore.store[:paper_trail] ||= { :request_enabled_for_controller => true }
|
127
|
-
end
|
128
|
-
|
129
|
-
# Returns PaperTrail's configuration object.
|
130
|
-
def self.config
|
131
|
-
@config ||= PaperTrail::Config.instance
|
132
|
-
yield @config if block_given?
|
133
|
-
@config
|
134
|
-
end
|
135
|
-
|
136
19
|
class << self
|
137
|
-
|
20
|
+
# Switches PaperTrail on or off.
|
21
|
+
# @api public
|
22
|
+
def enabled=(value)
|
23
|
+
PaperTrail.config.enabled = value
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns `true` if PaperTrail is on, `false` otherwise.
|
27
|
+
# PaperTrail is enabled by default.
|
28
|
+
# @api public
|
29
|
+
def enabled?
|
30
|
+
!!PaperTrail.config.enabled
|
31
|
+
end
|
32
|
+
|
33
|
+
def serialized_attributes?
|
34
|
+
ActiveSupport::Deprecation.warn(
|
35
|
+
"PaperTrail.serialized_attributes? is deprecated without replacement " +
|
36
|
+
"and always returns false."
|
37
|
+
)
|
38
|
+
false
|
39
|
+
end
|
40
|
+
|
41
|
+
# Sets whether PaperTrail is enabled or disabled for the current request.
|
42
|
+
# @api public
|
43
|
+
def enabled_for_controller=(value)
|
44
|
+
paper_trail_store[:request_enabled_for_controller] = value
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns `true` if PaperTrail is enabled for the request, `false` otherwise.
|
48
|
+
#
|
49
|
+
# See `PaperTrail::Rails::Controller#paper_trail_enabled_for_controller`.
|
50
|
+
# @api public
|
51
|
+
def enabled_for_controller?
|
52
|
+
!!paper_trail_store[:request_enabled_for_controller]
|
53
|
+
end
|
54
|
+
|
55
|
+
# Sets whether PaperTrail is enabled or disabled for this model in the
|
56
|
+
# current request.
|
57
|
+
# @api public
|
58
|
+
def enabled_for_model(model, value)
|
59
|
+
paper_trail_store[:"enabled_for_#{model}"] = value
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns `true` if PaperTrail is enabled for this model in the current
|
63
|
+
# request, `false` otherwise.
|
64
|
+
# @api public
|
65
|
+
def enabled_for_model?(model)
|
66
|
+
!!paper_trail_store.fetch(:"enabled_for_#{model}", true)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Set the field which records when a version was created.
|
70
|
+
# @api public
|
71
|
+
def timestamp_field=(field_name)
|
72
|
+
PaperTrail.config.timestamp_field = field_name
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns the field which records when a version was created.
|
76
|
+
# @api public
|
77
|
+
def timestamp_field
|
78
|
+
PaperTrail.config.timestamp_field
|
79
|
+
end
|
80
|
+
|
81
|
+
# Sets who is responsible for any changes that occur. You would normally use
|
82
|
+
# this in a migration or on the console, when working with models directly.
|
83
|
+
# In a controller it is set automatically to the `current_user`.
|
84
|
+
# @api public
|
85
|
+
def whodunnit=(value)
|
86
|
+
paper_trail_store[:whodunnit] = value
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns who is reponsible for any changes that occur.
|
90
|
+
# @api public
|
91
|
+
def whodunnit
|
92
|
+
paper_trail_store[:whodunnit]
|
93
|
+
end
|
94
|
+
|
95
|
+
# Sets any information from the controller that you want PaperTrail to
|
96
|
+
# store. By default this is set automatically by a before filter.
|
97
|
+
# @api public
|
98
|
+
def controller_info=(value)
|
99
|
+
paper_trail_store[:controller_info] = value
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns any information from the controller that you want
|
103
|
+
# PaperTrail to store.
|
104
|
+
#
|
105
|
+
# See `PaperTrail::Rails::Controller#info_for_paper_trail`.
|
106
|
+
# @api public
|
107
|
+
def controller_info
|
108
|
+
paper_trail_store[:controller_info]
|
109
|
+
end
|
110
|
+
|
111
|
+
# Getter and Setter for PaperTrail Serializer
|
112
|
+
# @api public
|
113
|
+
def serializer=(value)
|
114
|
+
PaperTrail.config.serializer = value
|
115
|
+
end
|
116
|
+
|
117
|
+
# @api public
|
118
|
+
def serializer
|
119
|
+
PaperTrail.config.serializer
|
120
|
+
end
|
121
|
+
|
122
|
+
# Returns a boolean indicating whether "protected attibutes" should be
|
123
|
+
# configured, e.g. attr_accessible, mass_assignment_sanitizer,
|
124
|
+
# whitelist_attributes, etc.
|
125
|
+
# @api public
|
126
|
+
def active_record_protected_attributes?
|
127
|
+
@active_record_protected_attributes ||= ::ActiveRecord::VERSION::MAJOR < 4 ||
|
128
|
+
!!defined?(ProtectedAttributes)
|
129
|
+
end
|
130
|
+
|
131
|
+
# @api public
|
132
|
+
def transaction?
|
133
|
+
::ActiveRecord::Base.connection.open_transactions > 0
|
134
|
+
end
|
135
|
+
|
136
|
+
# @api public
|
137
|
+
def transaction_id
|
138
|
+
paper_trail_store[:transaction_id]
|
139
|
+
end
|
140
|
+
|
141
|
+
# @api public
|
142
|
+
def transaction_id=(id)
|
143
|
+
paper_trail_store[:transaction_id] = id
|
144
|
+
end
|
145
|
+
|
146
|
+
# Thread-safe hash to hold PaperTrail's data. Initializing with needed
|
147
|
+
# default values.
|
148
|
+
# @api private
|
149
|
+
def paper_trail_store
|
150
|
+
RequestStore.store[:paper_trail] ||= { request_enabled_for_controller: true }
|
151
|
+
end
|
152
|
+
|
153
|
+
# Returns PaperTrail's configuration object.
|
154
|
+
# @api private
|
155
|
+
def config
|
156
|
+
@config ||= PaperTrail::Config.instance
|
157
|
+
yield @config if block_given?
|
158
|
+
@config
|
159
|
+
end
|
160
|
+
alias configure config
|
161
|
+
|
162
|
+
def version
|
163
|
+
VERSION::STRING
|
164
|
+
end
|
138
165
|
end
|
139
166
|
end
|
140
167
|
|
141
|
-
#
|
142
|
-
# `Version` class
|
168
|
+
# If available, ensure that the `protected_attributes` gem is loaded
|
169
|
+
# before the `Version` class.
|
143
170
|
unless PaperTrail.active_record_protected_attributes?
|
144
171
|
PaperTrail.send(:remove_instance_variable, :@active_record_protected_attributes)
|
145
172
|
begin
|
146
|
-
require
|
147
|
-
rescue LoadError
|
148
|
-
# In case `
|
173
|
+
require "protected_attributes"
|
174
|
+
rescue LoadError # rubocop:disable Lint/HandleExceptions
|
175
|
+
# In case `protected_attributes` gem is not available.
|
149
176
|
end
|
150
177
|
end
|
151
178
|
|
152
|
-
ActiveSupport.on_load(:active_record) do
|
153
|
-
include PaperTrail::Model
|
154
|
-
end
|
155
|
-
|
156
179
|
# Require frameworks
|
157
|
-
require
|
158
|
-
if defined?(::Rails) && ActiveRecord::VERSION::STRING >=
|
159
|
-
require
|
180
|
+
require "paper_trail/frameworks/sinatra"
|
181
|
+
if defined?(::Rails) && ActiveRecord::VERSION::STRING >= "3.2"
|
182
|
+
require "paper_trail/frameworks/rails"
|
160
183
|
else
|
161
|
-
require
|
184
|
+
require "paper_trail/frameworks/active_record"
|
162
185
|
end
|