Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add flexible configuration option that allows to ignore legacy migrations #12

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ end

The `with_lock` method will be useful when hitting the caveats of `LOCK=NONE`. Please read the 'Caveats' section.

### Turn it off for old migrations

Set up ignore based on version rule. Usually in your `config/application.rb`, something like that:

```ruby
config.active_record.mysql_online_migrations_ignore = -> (version) { version < "20141024152412" }
```

This will ignore all migrations before version `20141024152412`.

### Enable verbose output
To enable an 'ONLINE MIGRATION' debug statement whenever an online migration is
run, simply set the `MysqlOnlineMigrations.verbose` module variable to true.
Expand Down
5 changes: 5 additions & 0 deletions lib/mysql_online_migrations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ class << self; attr_accessor :verbose; end
def self.prepended(base)
ActiveRecord::Base.send(:class_attribute, :mysql_online_migrations, :instance_writer => false)
ActiveRecord::Base.send("mysql_online_migrations=", true)

ActiveRecord::Base.send(:class_attribute, :mysql_online_migrations_ignore, :instance_writer => false)
ActiveRecord::Base.send("mysql_online_migrations_ignore=", -> (version) { false })
end

def connection
original_connection = super
return original_connection if ActiveRecord::Base.mysql_online_migrations_ignore.call(version)

adapter_mode = original_connection.class.name == "ActiveRecord::ConnectionAdapters::Mysql2Adapter"

@original_adapter ||= if adapter_mode
Expand Down
32 changes: 32 additions & 0 deletions spec/lib/migration/version_ignore_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
require "spec_helper"

RSpec.describe ActiveRecord::Migration do
OLD_VERSION = "20120508133018"
NEW_VERSION = "20150119090054"

let(:comma_before_lock_none) { true }
let(:migration_arguments_with_lock) { [] }

context "#create_table" do
let(:method_name) { :create_table }
let(:migration_arguments) do
[
[:test5]
]
end

context "when version ignore rule is configured" do
before { set_ignore_setting -> (version) { version < NEW_VERSION } }

context "when version matches" do
it_behaves_like "a migration that does not modify statement", OLD_VERSION
it_behaves_like "a migration that succeeds in MySQL", OLD_VERSION
end

context "when version does not match" do
it_behaves_like "a migration that adds LOCK=NONE when needed", NEW_VERSION
it_behaves_like "a migration that succeeds in MySQL", NEW_VERSION
end
end
end
end
11 changes: 9 additions & 2 deletions spec/support/helpers.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
module Helpers
CATCH_STATEMENT_REGEX = /^(alter|create|drop|update|rename) /i
DDL_STATEMENT_REGEX = /^(alter|create (unique )? ?index|drop index) /i
DEFAULT_VERSION = "20110603153213"

def build_migration(method_name, args, &block)
def build_migration(method_name, args, version=nil, &block)
version ||= DEFAULT_VERSION
migration = ActiveRecord::Migration.new
migration.instance_variable_set(:@test_method_name, method_name)
migration.instance_variable_set(:@test_args, args)
migration.instance_variable_set(:@test_block, block)
migration.define_singleton_method(:change) do
public_send(@test_method_name, *@test_args, &@test_block)
end
migration.version = version
migration
end

Expand Down Expand Up @@ -99,6 +102,10 @@ def set_ar_setting(value)
allow(ActiveRecord::Base).to receive(:mysql_online_migrations).and_return(value)
end

def set_ignore_setting(value)
allow(ActiveRecord::Base).to receive(:mysql_online_migrations_ignore).and_return(value)
end

def teardown
@adapter.drop_table :testing rescue nil
@adapter.drop_table :test_rake rescue nil
Expand All @@ -112,4 +119,4 @@ def insert_version(version)
def clear_version
@adapter_without_lock.execute("TRUNCATE schema_migrations")
end
end
end
49 changes: 42 additions & 7 deletions spec/support/shared_examples/migration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def staged_for_travis
set_ar_setting(true) if ENV["TRAVIS"]
end

shared_examples_for "a migration that adds LOCK=NONE when needed" do
shared_examples_for "a migration that adds LOCK=NONE when needed" do |version=nil|
before(:each) do
stub_adapter_without_lock
stub_execute(@adapter, :execute, :regular_execute)
Expand All @@ -28,7 +28,7 @@ def staged_for_travis
end

begin
build_migration(method_name, migration_argument).migrate(:up)
build_migration(method_name, migration_argument, version).migrate(:up)
rescue => e
raise e unless @rescue_statement_when_stubbed
end
Expand All @@ -42,23 +42,58 @@ def staged_for_travis
end
end

shared_examples_for "a migration that succeeds in MySQL" do
shared_examples_for "a migration that does not modify statement" do |version=nil|
before(:each) do
stub_adapter_without_lock
stub_execute(@adapter, :execute, :regular_execute)
stub_execute(@adapter_without_lock, :original_execute, :execute_without_lock)
@migration_arguments = migration_arguments + migration_arguments_with_lock
end

it "executes the same query as the original adapter unmodified" do
@migration_arguments.each do |migration_argument|
reset_queries_collectors

begin
@adapter.public_send(method_name, *migration_argument)
rescue => e
raise e unless @rescue_statement_when_stubbed
end

begin
build_migration(method_name, migration_argument, version).migrate(:up)
rescue => e
raise e unless @rescue_statement_when_stubbed
end

expect(@queries_received_by_regular_adapter.length).to be > 0
expect(@queries_received_by_adapter_without_lock.length).to eq(0)

size = @queries_received_by_regular_adapter.count
@queries_received_by_regular_adapter.each_with_index do |query, index|
expect(@queries_received_by_regular_adapter[size - index - 1]).to eq(query)
end
end
end
end

shared_examples_for "a migration that succeeds in MySQL" do |version=nil|
it "succeeds without exception" do
staged_for_travis do
migration_arguments.each do |migration_argument|
migration = build_migration(method_name, migration_argument)
migration = build_migration(method_name, migration_argument, version)
migration.migrate(:up)
rebuild_table
end
end
end
end

shared_examples_for "a migration with a non-lockable statement" do
shared_examples_for "a migration with a non-lockable statement" do |version=nil|
it "raises a MySQL exception" do
staged_for_travis do
migration_arguments_with_lock.each do |migration_argument|
migration = build_migration(method_name, migration_argument)
migration = build_migration(method_name, migration_argument, version)
begin
migration.migrate(:up)
rescue ActiveRecord::StatementInvalid => e
Expand All @@ -68,4 +103,4 @@ def staged_for_travis
end
end
end
end
end