Rails
ActiveRecord
migration
Refactoring
revert

Migration の revert 機能の使い方

便利機能を見つけたので紹介します。

次のような処理があったとします。

カラム追加
change_table :users do |t|
  t.string :name
end

それを revert ブロックで囲めば逆の処理が走ります。

カラム削除
revert do
  change_table :users do |t|
    t.string :name
  end
end

既存の migtation を丸ごと revert したい場合はそのクラスを指定するだけでよいようです。

既存のmigration定義をrevertする
revert CreateUsers

以下は確認用コードです。

require "active_record"
ActiveRecord::VERSION::STRING   # => "5.1.4"
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")

class A < ActiveRecord::Migration[5.1]
  def change
    create_table :users do |t|
      t.string :name
    end
  end

  run self
end

class B < ActiveRecord::Migration[5.1]
  def change
    # users を drop
    revert A

    # 逆の逆の逆の逆なので users を create
    revert do
      revert do
        revert do
          revert A
        end
      end
    end

    # 再び users を drop
    revert A

    # users を create
    ActiveRecord::Migration.run A # A.migrate(:up) でも実行できる

    # 「nameカラム追加」の逆なので「nameカラムを削除」
    revert do
      change_table :users do |t|
        # 本当は string 型だけど嘘を書いてもいい
        t.integer :name
      end
    end

    # users を drop
    revert do
      create_table :users
    end
  end

  run self
end
# >> -- create_table(:users)
# >>    -> 0.0018s
# >> -- drop_table(:users)
# >>    -> 0.0001s
# >> -- create_table(:users)
# >>    -> 0.0003s
# >> -- drop_table(:users)
# >>    -> 0.0001s
# >> -- create_table(:users)
# >>    -> 0.0002s
# >> -- remove_column(:users, :name, :integer, {})
# >>    -> 0.0046s
# >> -- drop_table(:users)
# >>    -> 0.0001s

drop や remove 系メソッドに変換しなくてもいいのが楽ですね。