paper_trail 4.2.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
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