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.
Files changed (149) hide show
  1. checksums.yaml +4 -4
  2. data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +28 -9
  3. data/.github/ISSUE_TEMPLATE.md +13 -0
  4. data/.gitignore +2 -1
  5. data/.rubocop.yml +100 -0
  6. data/.rubocop_todo.yml +14 -0
  7. data/.travis.yml +8 -9
  8. data/Appraisals +41 -0
  9. data/CHANGELOG.md +49 -9
  10. data/Gemfile +1 -1
  11. data/README.md +130 -109
  12. data/Rakefile +19 -19
  13. data/doc/bug_report_template.rb +20 -14
  14. data/gemfiles/ar3.gemfile +10 -53
  15. data/gemfiles/ar4.gemfile +7 -0
  16. data/gemfiles/ar5.gemfile +13 -0
  17. data/lib/generators/paper_trail/install_generator.rb +26 -18
  18. data/lib/generators/paper_trail/templates/add_object_changes_to_versions.rb +4 -2
  19. data/lib/generators/paper_trail/templates/add_transaction_id_column_to_versions.rb +2 -0
  20. data/lib/generators/paper_trail/templates/create_version_associations.rb +9 -4
  21. data/lib/generators/paper_trail/templates/create_versions.rb +39 -5
  22. data/lib/paper_trail.rb +169 -146
  23. data/lib/paper_trail/attributes_serialization.rb +89 -17
  24. data/lib/paper_trail/cleaner.rb +15 -9
  25. data/lib/paper_trail/config.rb +28 -11
  26. data/lib/paper_trail/frameworks/active_record.rb +4 -0
  27. data/lib/paper_trail/frameworks/active_record/models/paper_trail/version.rb +5 -1
  28. data/lib/paper_trail/frameworks/active_record/models/paper_trail/version_association.rb +6 -2
  29. data/lib/paper_trail/frameworks/cucumber.rb +1 -0
  30. data/lib/paper_trail/frameworks/rails.rb +2 -7
  31. data/lib/paper_trail/frameworks/rails/controller.rb +29 -9
  32. data/lib/paper_trail/frameworks/rails/engine.rb +7 -1
  33. data/lib/paper_trail/frameworks/rspec.rb +5 -5
  34. data/lib/paper_trail/frameworks/rspec/helpers.rb +3 -1
  35. data/lib/paper_trail/frameworks/sinatra.rb +6 -4
  36. data/lib/paper_trail/has_paper_trail.rb +199 -106
  37. data/lib/paper_trail/record_history.rb +1 -3
  38. data/lib/paper_trail/reifier.rb +297 -118
  39. data/lib/paper_trail/serializers/json.rb +3 -3
  40. data/lib/paper_trail/serializers/yaml.rb +27 -8
  41. data/lib/paper_trail/version_association_concern.rb +3 -1
  42. data/lib/paper_trail/version_concern.rb +75 -35
  43. data/lib/paper_trail/version_number.rb +6 -9
  44. data/paper_trail.gemspec +44 -51
  45. data/spec/generators/install_generator_spec.rb +24 -25
  46. data/spec/generators/paper_trail/templates/create_versions_spec.rb +51 -0
  47. data/spec/models/animal_spec.rb +12 -12
  48. data/spec/models/boolit_spec.rb +8 -8
  49. data/spec/models/callback_modifier_spec.rb +47 -47
  50. data/spec/models/car_spec.rb +13 -0
  51. data/spec/models/fluxor_spec.rb +3 -3
  52. data/spec/models/gadget_spec.rb +19 -19
  53. data/spec/models/joined_version_spec.rb +3 -3
  54. data/spec/models/json_version_spec.rb +23 -24
  55. data/spec/models/kitchen/banana_spec.rb +3 -3
  56. data/spec/models/not_on_update_spec.rb +7 -4
  57. data/spec/models/post_with_status_spec.rb +13 -3
  58. data/spec/models/skipper_spec.rb +10 -10
  59. data/spec/models/thing_spec.rb +4 -4
  60. data/spec/models/truck_spec.rb +5 -0
  61. data/spec/models/vehicle_spec.rb +5 -0
  62. data/spec/models/version_spec.rb +103 -59
  63. data/spec/models/widget_spec.rb +82 -52
  64. data/spec/modules/paper_trail_spec.rb +2 -2
  65. data/spec/modules/version_concern_spec.rb +11 -12
  66. data/spec/modules/version_number_spec.rb +2 -4
  67. data/spec/paper_trail/config_spec.rb +10 -29
  68. data/spec/paper_trail_spec.rb +16 -14
  69. data/spec/rails_helper.rb +10 -9
  70. data/spec/requests/articles_spec.rb +11 -7
  71. data/spec/spec_helper.rb +41 -22
  72. data/spec/support/alt_db_init.rb +8 -13
  73. data/test/custom_json_serializer.rb +3 -3
  74. data/test/dummy/Rakefile +2 -2
  75. data/test/dummy/app/controllers/application_controller.rb +21 -8
  76. data/test/dummy/app/controllers/articles_controller.rb +11 -8
  77. data/test/dummy/app/controllers/widgets_controller.rb +13 -12
  78. data/test/dummy/app/models/animal.rb +1 -1
  79. data/test/dummy/app/models/article.rb +19 -11
  80. data/test/dummy/app/models/authorship.rb +1 -1
  81. data/test/dummy/app/models/bar_habtm.rb +4 -0
  82. data/test/dummy/app/models/book.rb +4 -4
  83. data/test/dummy/app/models/boolit.rb +1 -1
  84. data/test/dummy/app/models/callback_modifier.rb +6 -6
  85. data/test/dummy/app/models/car.rb +3 -0
  86. data/test/dummy/app/models/chapter.rb +4 -4
  87. data/test/dummy/app/models/customer.rb +1 -1
  88. data/test/dummy/app/models/document.rb +2 -2
  89. data/test/dummy/app/models/editor.rb +1 -1
  90. data/test/dummy/app/models/foo_habtm.rb +4 -0
  91. data/test/dummy/app/models/fruit.rb +2 -2
  92. data/test/dummy/app/models/gadget.rb +1 -1
  93. data/test/dummy/app/models/kitchen/banana.rb +1 -1
  94. data/test/dummy/app/models/legacy_widget.rb +2 -2
  95. data/test/dummy/app/models/line_item.rb +1 -1
  96. data/test/dummy/app/models/not_on_update.rb +1 -1
  97. data/test/dummy/app/models/person.rb +6 -6
  98. data/test/dummy/app/models/post.rb +1 -1
  99. data/test/dummy/app/models/post_with_status.rb +1 -1
  100. data/test/dummy/app/models/quotation.rb +1 -1
  101. data/test/dummy/app/models/section.rb +1 -1
  102. data/test/dummy/app/models/skipper.rb +2 -2
  103. data/test/dummy/app/models/song.rb +13 -4
  104. data/test/dummy/app/models/thing.rb +2 -2
  105. data/test/dummy/app/models/translation.rb +2 -2
  106. data/test/dummy/app/models/truck.rb +4 -0
  107. data/test/dummy/app/models/vehicle.rb +4 -0
  108. data/test/dummy/app/models/whatchamajigger.rb +1 -1
  109. data/test/dummy/app/models/widget.rb +7 -6
  110. data/test/dummy/app/versions/joined_version.rb +4 -3
  111. data/test/dummy/app/versions/json_version.rb +1 -1
  112. data/test/dummy/app/versions/kitchen/banana_version.rb +1 -1
  113. data/test/dummy/app/versions/post_version.rb +2 -2
  114. data/test/dummy/config.ru +1 -1
  115. data/test/dummy/config/application.rb +20 -9
  116. data/test/dummy/config/boot.rb +5 -5
  117. data/test/dummy/config/environment.rb +1 -1
  118. data/test/dummy/config/environments/development.rb +4 -3
  119. data/test/dummy/config/environments/production.rb +3 -2
  120. data/test/dummy/config/environments/test.rb +15 -5
  121. data/test/dummy/config/initializers/backtrace_silencers.rb +4 -2
  122. data/test/dummy/config/initializers/paper_trail.rb +1 -2
  123. data/test/dummy/config/initializers/secret_token.rb +3 -1
  124. data/test/dummy/config/initializers/session_store.rb +1 -1
  125. data/test/dummy/config/routes.rb +2 -2
  126. data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +120 -74
  127. data/test/dummy/db/schema.rb +29 -6
  128. data/test/dummy/script/rails +6 -4
  129. data/test/functional/controller_test.rb +34 -35
  130. data/test/functional/enabled_for_controller_test.rb +6 -7
  131. data/test/functional/modular_sinatra_test.rb +43 -38
  132. data/test/functional/sinatra_test.rb +49 -40
  133. data/test/functional/thread_safety_test.rb +4 -6
  134. data/test/paper_trail_test.rb +15 -14
  135. data/test/test_helper.rb +68 -44
  136. data/test/time_travel_helper.rb +1 -15
  137. data/test/unit/associations_test.rb +517 -251
  138. data/test/unit/cleaner_test.rb +66 -60
  139. data/test/unit/inheritance_column_test.rb +17 -17
  140. data/test/unit/model_test.rb +611 -504
  141. data/test/unit/protected_attrs_test.rb +16 -12
  142. data/test/unit/serializer_test.rb +44 -43
  143. data/test/unit/serializers/json_test.rb +17 -18
  144. data/test/unit/serializers/mixin_json_test.rb +15 -14
  145. data/test/unit/serializers/mixin_yaml_test.rb +20 -16
  146. data/test/unit/serializers/yaml_test.rb +12 -13
  147. data/test/unit/timestamp_test.rb +10 -12
  148. data/test/unit/version_test.rb +7 -7
  149. metadata +92 -40
data/Rakefile CHANGED
@@ -1,30 +1,30 @@
1
- require 'bundler'
1
+ require "bundler"
2
2
  Bundler::GemHelper.install_tasks
3
3
 
4
- desc 'Set a relevant database.yml for testing'
4
+ desc "Set a relevant database.yml for testing"
5
5
  task :prepare do
6
6
  ENV["DB"] ||= "sqlite"
7
- if RUBY_VERSION >= '1.9'
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
- require 'rake/testtask'
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 << 'lib'
20
- t.libs << 'test'
21
- t.pattern = 'test/**/*_test.rb'
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 'rspec/core/rake_task'
26
- desc 'Run tests on PaperTrail with RSpec'
27
- RSpec::Core::RakeTask.new(:spec)
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 'Default: run all available test suites'
30
- task :default => [:prepare, :test, :spec]
29
+ desc "Default: run all available test suites"
30
+ task default: [:rubocop, :prepare, :test, :spec]
@@ -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 'bundler/inline'
5
+ require "bundler/inline"
6
6
  rescue LoadError => e
7
- $stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler'
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 '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'
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 'active_record'
21
- require 'minitest/autorun'
22
- require 'logger'
23
- ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
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: 'index_version_associations_on_foreign_key'
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) {
@@ -1,61 +1,18 @@
1
- source 'https://rubygems.org'
1
+ # This file was generated by Appraisal
2
2
 
3
- gem 'activerecord', '3.2.22.2'
4
- gem 'request_store', '~> 1.1.0'
3
+ source "https://rubygems.org"
5
4
 
6
- # i18n 0.7 requires ruby >= 1.9.3, but we still support 1.8.7
7
- gem 'i18n', '< 0.7'
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 'rake', '~> 10.1.1'
17
- gem 'shoulda', '~> 3.5'
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 'sqlite3', '~> 1.2'
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,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.2"
6
+
7
+ 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 'rails/generators'
2
- require 'rails/generators/active_record'
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('../templates', __FILE__)
9
- class_option :with_changes, :type => :boolean, :default => false,
10
- :desc => "Store changeset (diff) with each version"
11
- class_option :with_associations, :type => :boolean, :default => false,
12
- :desc => "Store transactional IDs to support association restoration"
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 'Generates (but does not run) a migration to add a versions table.'
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('create_versions')
18
- add_paper_trail_migration('add_object_changes_to_versions') if options.with_changes?
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('create_version_associations')
21
- add_paper_trail_migration('add_transaction_id_column_to_versions')
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
- unless self.class.migration_exists?(migration_dir, template)
34
- migration_template "#{template}.rb", "db/migrate/#{template}.rb"
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
- warn("ALERT: Migration already exists named '#{template}'." +
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, :limit => TEXT_BYTES
10
+ add_column :versions, :object_changes, :text, limit: TEXT_BYTES
9
11
  end
10
12
  end
@@ -1,3 +1,5 @@
1
+ # This migration and CreateVersionAssociations provide the necessary
2
+ # schema for tracking associations.
1
3
  class AddTransactionIdColumnToVersions < ActiveRecord::Migration
2
4
  def self.up
3
5
  add_column :versions, :transaction_id, :integer
@@ -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, :null => false
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, [:foreign_key_name, :foreign_key_id], :name => 'index_version_associations_on_foreign_key'
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, :name => 'index_version_associations_on_foreign_key'
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, :null => false
12
- t.integer :item_id, :null => false
13
- t.string :event, :null => false
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, :limit => TEXT_BYTES
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
@@ -1,162 +1,185 @@
1
- require 'request_store'
2
-
3
- # Require files in lib/paper_trail, but not its subdirectories.
4
- Dir[File.join(File.dirname(__FILE__), 'paper_trail', '*.rb')].each do |file|
5
- require File.join('paper_trail', File.basename(file, '.rb'))
6
- end
7
-
8
- # Require serializers
9
- Dir[File.join(File.dirname(__FILE__), 'paper_trail', 'serializers', '*.rb')].each do |file|
10
- require File.join('paper_trail', 'serializers', File.basename(file, '.rb'))
11
- end
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
- alias_method :configure, :config
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
- # Ensure `ProtectedAttributes` gem gets required if it is available before the
142
- # `Version` class gets loaded in.
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 'protected_attributes'
147
- rescue LoadError
148
- # In case `ProtectedAttributes` gem is not available.
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 'paper_trail/frameworks/sinatra'
158
- if defined?(::Rails) && ActiveRecord::VERSION::STRING >= '3.2'
159
- require 'paper_trail/frameworks/rails'
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 'paper_trail/frameworks/active_record'
184
+ require "paper_trail/frameworks/active_record"
162
185
  end