mirror of
https://github.com/paper-trail-gem/paper_trail.git
synced 2022-11-09 11:33:19 -05:00
Merge pull request #950 from airblade/fix_949
Improvements to migrations
This commit is contained in:
commit
362ad6dc3c
9 changed files with 116 additions and 136 deletions
|
@ -11,11 +11,12 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/).
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- None
|
- Generate cleaner migrations for databases other than MySQL
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- None
|
- [#949](https://github.com/airblade/paper_trail/issues/949) - Inherit from the
|
||||||
|
new versioned migration class, e.g. `ActiveRecord::Migration[5.1]`
|
||||||
|
|
||||||
## 7.0.0 (2017-04-01)
|
## 7.0.0 (2017-04-01)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,14 @@ module PaperTrail
|
||||||
class InstallGenerator < ::Rails::Generators::Base
|
class InstallGenerator < ::Rails::Generators::Base
|
||||||
include ::Rails::Generators::Migration
|
include ::Rails::Generators::Migration
|
||||||
|
|
||||||
|
# Class names of MySQL adapters.
|
||||||
|
# - `MysqlAdapter` - Used by gems: `mysql`, `activerecord-jdbcmysql-adapter`.
|
||||||
|
# - `Mysql2Adapter` - Used by `mysql2` gem.
|
||||||
|
MYSQL_ADAPTERS = [
|
||||||
|
"ActiveRecord::ConnectionAdapters::MysqlAdapter",
|
||||||
|
"ActiveRecord::ConnectionAdapters::Mysql2Adapter"
|
||||||
|
].freeze
|
||||||
|
|
||||||
source_root File.expand_path("../templates", __FILE__)
|
source_root File.expand_path("../templates", __FILE__)
|
||||||
class_option(
|
class_option(
|
||||||
:with_changes,
|
:with_changes,
|
||||||
|
@ -50,7 +58,57 @@ module PaperTrail
|
||||||
if self.class.migration_exists?(migration_dir, template)
|
if self.class.migration_exists?(migration_dir, template)
|
||||||
::Kernel.warn "Migration already exists: #{template}"
|
::Kernel.warn "Migration already exists: #{template}"
|
||||||
else
|
else
|
||||||
migration_template "#{template}.rb", "db/migrate/#{template}.rb"
|
migration_template(
|
||||||
|
"#{template}.rb.erb",
|
||||||
|
"db/migrate/#{template}.rb",
|
||||||
|
item_type_options: item_type_options,
|
||||||
|
migration_version: migration_version,
|
||||||
|
versions_table_options: versions_table_options
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# MySQL 5.6 utf8mb4 limit is 191 chars for keys used in indexes.
|
||||||
|
# See https://github.com/airblade/paper_trail/issues/651
|
||||||
|
def item_type_options
|
||||||
|
opt = { null: false }
|
||||||
|
opt[:limit] = 191 if mysql?
|
||||||
|
", #{opt}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def migration_version
|
||||||
|
major = ActiveRecord::VERSION::MAJOR
|
||||||
|
if major >= 5
|
||||||
|
"[#{major}.#{ActiveRecord::VERSION::MINOR}]"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def mysql?
|
||||||
|
MYSQL_ADAPTERS.include?(ActiveRecord::Base.connection.class.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Even modern versions of MySQL still use `latin1` as the default character
|
||||||
|
# encoding. Many users are not aware of this, and run into trouble when they
|
||||||
|
# try to use PaperTrail in apps that otherwise tend to use UTF-8. Postgres, by
|
||||||
|
# comparison, uses UTF-8 except in the unusual case where the OS is configured
|
||||||
|
# with a custom locale.
|
||||||
|
#
|
||||||
|
# - https://dev.mysql.com/doc/refman/5.7/en/charset-applications.html
|
||||||
|
# - http://www.postgresql.org/docs/9.4/static/multibyte.html
|
||||||
|
#
|
||||||
|
# Furthermore, MySQL's original implementation of UTF-8 was flawed, and had
|
||||||
|
# to be fixed later by introducing a new charset, `utf8mb4`.
|
||||||
|
#
|
||||||
|
# - https://mathiasbynens.be/notes/mysql-utf8mb4
|
||||||
|
# - https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html
|
||||||
|
#
|
||||||
|
def versions_table_options
|
||||||
|
if mysql?
|
||||||
|
', { options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci" }'
|
||||||
|
else
|
||||||
|
""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
# This migration creates the `versions` table, the only schema PT requires.
|
|
||||||
# All other migrations PT provides are optional.
|
|
||||||
class CreateVersions < ActiveRecord::Migration
|
|
||||||
# Class names of MySQL adapters.
|
|
||||||
# - `MysqlAdapter` - Used by gems: `mysql`, `activerecord-jdbcmysql-adapter`.
|
|
||||||
# - `Mysql2Adapter` - Used by `mysql2` gem.
|
|
||||||
MYSQL_ADAPTERS = [
|
|
||||||
"ActiveRecord::ConnectionAdapters::MysqlAdapter",
|
|
||||||
"ActiveRecord::ConnectionAdapters::Mysql2Adapter"
|
|
||||||
].freeze
|
|
||||||
|
|
||||||
# The largest text column available in all supported RDBMS is
|
|
||||||
# 1024^3 - 1 bytes, roughly one gibibyte. We specify a size
|
|
||||||
# so that MySQL will use `longtext` instead of `text`. Otherwise,
|
|
||||||
# when serializing very large objects, `text` might not be big enough.
|
|
||||||
TEXT_BYTES = 1_073_741_823
|
|
||||||
|
|
||||||
def change
|
|
||||||
create_table :versions, versions_table_options do |t|
|
|
||||||
t.string :item_type, item_type_options
|
|
||||||
t.integer :item_id, null: false
|
|
||||||
t.string :event, null: false
|
|
||||||
t.string :whodunnit
|
|
||||||
t.text :object, limit: TEXT_BYTES
|
|
||||||
|
|
||||||
# Known issue in MySQL: fractional second precision
|
|
||||||
# -------------------------------------------------
|
|
||||||
#
|
|
||||||
# MySQL timestamp columns do not support fractional seconds unless
|
|
||||||
# defined with "fractional seconds precision". MySQL users should manually
|
|
||||||
# add fractional seconds precision to this migration, specifically, to
|
|
||||||
# the `created_at` column.
|
|
||||||
# (https://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html)
|
|
||||||
#
|
|
||||||
# MySQL users should also upgrade to rails 4.2, which is the first
|
|
||||||
# version of ActiveRecord with support for fractional seconds in MySQL.
|
|
||||||
# (https://github.com/rails/rails/pull/14359)
|
|
||||||
#
|
|
||||||
t.datetime :created_at
|
|
||||||
end
|
|
||||||
add_index :versions, %i(item_type item_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# MySQL 5.6 utf8mb4 limit is 191 chars for keys used in indexes.
|
|
||||||
# See https://github.com/airblade/paper_trail/issues/651
|
|
||||||
def item_type_options
|
|
||||||
opt = { null: false }
|
|
||||||
opt[:limit] = 191 if mysql?
|
|
||||||
opt
|
|
||||||
end
|
|
||||||
|
|
||||||
def mysql?
|
|
||||||
MYSQL_ADAPTERS.include?(connection.class.name)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Even modern versions of MySQL still use `latin1` as the default character
|
|
||||||
# encoding. Many users are not aware of this, and run into trouble when they
|
|
||||||
# try to use PaperTrail in apps that otherwise tend to use UTF-8. Postgres, by
|
|
||||||
# comparison, uses UTF-8 except in the unusual case where the OS is configured
|
|
||||||
# with a custom locale.
|
|
||||||
#
|
|
||||||
# - https://dev.mysql.com/doc/refman/5.7/en/charset-applications.html
|
|
||||||
# - http://www.postgresql.org/docs/9.4/static/multibyte.html
|
|
||||||
#
|
|
||||||
# Furthermore, MySQL's original implementation of UTF-8 was flawed, and had
|
|
||||||
# to be fixed later by introducing a new charset, `utf8mb4`.
|
|
||||||
#
|
|
||||||
# - https://mathiasbynens.be/notes/mysql-utf8mb4
|
|
||||||
# - https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html
|
|
||||||
#
|
|
||||||
def versions_table_options
|
|
||||||
if mysql?
|
|
||||||
{ options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci" }
|
|
||||||
else
|
|
||||||
{}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
36
lib/generators/paper_trail/templates/create_versions.rb.erb
Normal file
36
lib/generators/paper_trail/templates/create_versions.rb.erb
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# This migration creates the `versions` table, the only schema PT requires.
|
||||||
|
# All other migrations PT provides are optional.
|
||||||
|
class CreateVersions < ActiveRecord::Migration<%= migration_version %>
|
||||||
|
|
||||||
|
# The largest text column available in all supported RDBMS is
|
||||||
|
# 1024^3 - 1 bytes, roughly one gibibyte. We specify a size
|
||||||
|
# so that MySQL will use `longtext` instead of `text`. Otherwise,
|
||||||
|
# when serializing very large objects, `text` might not be big enough.
|
||||||
|
TEXT_BYTES = 1_073_741_823
|
||||||
|
|
||||||
|
def change
|
||||||
|
create_table :versions<%= versions_table_options %> do |t|
|
||||||
|
t.string :item_type<%= item_type_options %>
|
||||||
|
t.integer :item_id, null: false
|
||||||
|
t.string :event, null: false
|
||||||
|
t.string :whodunnit
|
||||||
|
t.text :object, limit: TEXT_BYTES
|
||||||
|
|
||||||
|
# Known issue in MySQL: fractional second precision
|
||||||
|
# -------------------------------------------------
|
||||||
|
#
|
||||||
|
# MySQL timestamp columns do not support fractional seconds unless
|
||||||
|
# defined with "fractional seconds precision". MySQL users should manually
|
||||||
|
# add fractional seconds precision to this migration, specifically, to
|
||||||
|
# the `created_at` column.
|
||||||
|
# (https://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html)
|
||||||
|
#
|
||||||
|
# MySQL users should also upgrade to rails 4.2, which is the first
|
||||||
|
# version of ActiveRecord with support for fractional seconds in MySQL.
|
||||||
|
# (https://github.com/rails/rails/pull/14359)
|
||||||
|
#
|
||||||
|
t.datetime :created_at
|
||||||
|
end
|
||||||
|
add_index :versions, %i(item_type item_id)
|
||||||
|
end
|
||||||
|
end
|
|
@ -15,14 +15,30 @@ RSpec.describe PaperTrail::InstallGenerator, type: :generator do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "generates a migration for creating the 'versions' table" do
|
it "generates a migration for creating the 'versions' table" do
|
||||||
|
expected_parent_class = lambda {
|
||||||
|
old_school = "ActiveRecord::Migration"
|
||||||
|
ar_version = ActiveRecord::VERSION
|
||||||
|
if ar_version::MAJOR >= 5
|
||||||
|
format("%s[%d.%d]", old_school, ar_version::MAJOR, ar_version::MINOR)
|
||||||
|
else
|
||||||
|
old_school
|
||||||
|
end
|
||||||
|
}.call
|
||||||
|
expected_create_table_options = lambda {
|
||||||
|
if described_class::MYSQL_ADAPTERS.include?(ActiveRecord::Base.connection.class.name)
|
||||||
|
', { options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci" }'
|
||||||
|
else
|
||||||
|
""
|
||||||
|
end
|
||||||
|
}.call
|
||||||
expect(destination_root).to(
|
expect(destination_root).to(
|
||||||
have_structure {
|
have_structure {
|
||||||
directory("db") {
|
directory("db") {
|
||||||
directory("migrate") {
|
directory("migrate") {
|
||||||
migration("create_versions") {
|
migration("create_versions") {
|
||||||
contains "class CreateVersions"
|
contains("class CreateVersions < " + expected_parent_class)
|
||||||
contains "def change"
|
contains "def change"
|
||||||
contains "create_table :versions"
|
contains "create_table :versions#{expected_create_table_options}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
require "rails_helper"
|
|
||||||
require "generators/paper_trail/templates/create_versions"
|
|
||||||
|
|
||||||
RSpec.describe CreateVersions do
|
|
||||||
describe "#change", verify_stubs: false do
|
|
||||||
let(:migration) { described_class.new }
|
|
||||||
|
|
||||||
before do
|
|
||||||
allow(migration).to receive(:add_index)
|
|
||||||
allow(migration).to receive(:create_table)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "creates the versions table" do
|
|
||||||
migration.change
|
|
||||||
expect(migration).to have_received(:create_table) do |arg1|
|
|
||||||
expect(arg1).to eq(:versions)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
case ENV["DB"]
|
|
||||||
when "mysql"
|
|
||||||
it "uses InnoDB engine" do
|
|
||||||
migration.change
|
|
||||||
expect(migration).to have_received(:create_table) do |_, arg2|
|
|
||||||
expect(arg2[:options]).to match(/ENGINE=InnoDB/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it "uses utf8mb4 character set" do
|
|
||||||
migration.change
|
|
||||||
expect(migration).to have_received(:create_table) do |_, arg2|
|
|
||||||
expect(arg2[:options]).to match(/DEFAULT CHARSET=utf8mb4/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it "uses utf8mb4_col collation" do
|
|
||||||
migration.change
|
|
||||||
expect(migration).to have_received(:create_table) do |_, arg2|
|
|
||||||
expect(arg2[:options]).to match(/COLLATE=utf8mb4_general_ci/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
it "passes an empty options hash to create_table" do
|
|
||||||
migration.change
|
|
||||||
expect(migration).to have_received(:create_table) do |_, arg2|
|
|
||||||
expect(arg2).to eq({})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in a new issue