From e166f8c6fc5bf4db3e76b7bc6846981c053a8e19 Mon Sep 17 00:00:00 2001 From: Alexey Fedorov Date: Tue, 27 Jan 2015 18:32:59 +0100 Subject: [PATCH 1/2] Ignore legacy migrations :: add configuration option to ignore migrations based on version as lambda/proc --- lib/mysql_online_migrations.rb | 5 +++ spec/lib/migration/version_ignore_spec.rb | 32 +++++++++++++++ spec/support/helpers.rb | 11 ++++- spec/support/shared_examples/migration.rb | 49 +++++++++++++++++++---- 4 files changed, 88 insertions(+), 9 deletions(-) create mode 100644 spec/lib/migration/version_ignore_spec.rb diff --git a/lib/mysql_online_migrations.rb b/lib/mysql_online_migrations.rb index 92bff3a..f7fff88 100644 --- a/lib/mysql_online_migrations.rb +++ b/lib/mysql_online_migrations.rb @@ -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 diff --git a/spec/lib/migration/version_ignore_spec.rb b/spec/lib/migration/version_ignore_spec.rb new file mode 100644 index 0000000..0789b4e --- /dev/null +++ b/spec/lib/migration/version_ignore_spec.rb @@ -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 diff --git a/spec/support/helpers.rb b/spec/support/helpers.rb index d895ec3..2844505 100644 --- a/spec/support/helpers.rb +++ b/spec/support/helpers.rb @@ -1,8 +1,10 @@ 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) @@ -10,6 +12,7 @@ def build_migration(method_name, args, &block) migration.define_singleton_method(:change) do public_send(@test_method_name, *@test_args, &@test_block) end + migration.version = version migration end @@ -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 @@ -112,4 +119,4 @@ def insert_version(version) def clear_version @adapter_without_lock.execute("TRUNCATE schema_migrations") end -end \ No newline at end of file +end diff --git a/spec/support/shared_examples/migration.rb b/spec/support/shared_examples/migration.rb index 87acfa3..1b841bf 100644 --- a/spec/support/shared_examples/migration.rb +++ b/spec/support/shared_examples/migration.rb @@ -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) @@ -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 @@ -42,11 +42,46 @@ 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 @@ -54,11 +89,11 @@ def staged_for_travis 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 @@ -68,4 +103,4 @@ def staged_for_travis end end end -end \ No newline at end of file +end From 74094399587b67708bc61174c7aeeaddfa3b1aae Mon Sep 17 00:00:00 2001 From: Alexey Fedorov Date: Tue, 27 Jan 2015 18:37:25 +0100 Subject: [PATCH 2/2] Ignore legacy migrations :: add usage note to readme --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 886877c..6cc3f0a 100644 --- a/README.md +++ b/README.md @@ -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.