MySQL: Specify utf8mb4 charset for versions table

Fixes #651
This commit is contained in:
Jared Beck 2015-12-24 01:50:14 -05:00
parent 402a78e3e5
commit d138b955af
4 changed files with 98 additions and 6 deletions

View File

@ -1,5 +1,13 @@
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"
]
# 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,
@ -7,7 +15,7 @@ class CreateVersions < ActiveRecord::Migration
TEXT_BYTES = 1_073_741_823
def change
create_table :versions do |t|
create_table :versions, versions_table_options do |t|
t.string :item_type, :null => false
t.integer :item_id, :null => false
t.string :event, :null => false
@ -31,4 +39,29 @@ class CreateVersions < ActiveRecord::Migration
end
add_index :versions, [:item_type, :item_id]
end
private
# 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_ADAPTERS.include?(connection.class.name)
{ options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_col' }
else
{}
end
end
end

View File

@ -13,7 +13,7 @@ describe PaperTrail::InstallGenerator, :type => :generator do
prepare_destination
run_generator
end
it "generates a migration for creating the 'versions' table" do
expect(destination_root).to have_structure {
directory 'db' do
@ -21,7 +21,7 @@ describe PaperTrail::InstallGenerator, :type => :generator do
migration 'create_versions' do
contains 'class CreateVersions'
contains 'def change'
contains 'create_table :versions do |t|'
contains 'create_table :versions'
end
end
end
@ -42,7 +42,7 @@ describe PaperTrail::InstallGenerator, :type => :generator do
migration 'create_versions' do
contains 'class CreateVersions'
contains 'def change'
contains 'create_table :versions do |t|'
contains 'create_table :versions'
end
end
end
@ -63,5 +63,4 @@ describe PaperTrail::InstallGenerator, :type => :generator do
}
end
end
end

View File

@ -0,0 +1,51 @@
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_col/)
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

View File

@ -44,7 +44,16 @@ RSpec.configure do |config|
mocks.verify_partial_doubles = true
end
# The settings below are suggested to provide a good initial experience
# Support for disabling `verify_partial_doubles` on specific examples.
config.around(:each, verify_stubs: false) do |ex|
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = false
ex.run
mocks.verify_partial_doubles = true
end
end
# The settings below are suggested to provide a good initial experience
# with RSpec, but feel free to customize to your heart's content.
=begin
# These two settings work together to allow you to limit a spec run