前置き
モバイルアプリのバックエンドを学習を兼ねてRailsで開発しています。「マイグレーションファイルを統合出来ないかな」と思っていたところ、こちらの書籍にてSquasherが紹介されていたので、試してみました。
まずは現状を確認する
dbディレクトリに以下のマイグレーションファイルがあります。
$ tree
.
├── migrate
│ ├── 20181129152052_create_users.rb
│ ├── 20181129152325_add_column_to_users.rb
│ ├── 20181230120205_rename_column_device_token.rb
│ ├── 20181231084154_add_index_to_user.rb
│ ├── 20181231142632_create_devices.rb
│ ├── 20181231151529_add_reference_to_users.rb
│ ├── 20181231151946_add_reference_to_devices.rb
│ ├── 20190101175529_create_rooms.rb
│ ├── 20190101182417_create_talks.rb
│ ├── 20190101183525_add_reference_to_talks.rb
│ ├── 20190101183740_add_reference_to_rooms.rb
│ ├── 20190101184243_add_reference_talk_to_users.rb
│ ├── 20190101184438_add_reference_user_to_talks.rb
│ ├── 20190102101406_create_user_room_mappings.rb
│ ├── 20190102102554_add_index_to_user_room_mappings.rb
│ ├── 20190102133103_add_column_status_to_user_rooms_mappings.rb
│ ├── 20190102134153_rename_table_user_rooms_mappings_to_residence.rb
│ ├── 20190103102845_rename_table_residence_to_residences.rb
│ ├── 20190103140215_add_reference_rooms_to_user.rb
│ ├── 20190103140817_add_reference_users_to_room.rb
│ ├── 20190103144721_rename_column_user_id_to_users.rb
│ ├── 20190109141322_rename_column_capacity_posts_par_user_to_rooms.rb
│ ├── 20190119135319_rename_column_capacity_users_to_rooms.rb
│ ├── 20190119135359_rename_column_capacity_post_per_user_to_rooms.rb
│ ├── 20190119143121_add_column_lifetime_to_rooms.rb
│ ├── 20190119143654_del_column_name_to_rooms.rb
│ ├── 20190119145901_del_reference_from_users.rb
│ └── 20190119150920_del_reference_to_rooms.rb
├── schema.rb
└── seeds.rb
インストールする
基本的には公式に沿って進めていきます。
-
GemfileにSquasherを追記します
Gemfilesource 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } ruby '2.5.1' gem 'rails', '~> 5.2.2' gem 'mysql2', '>= 0.4.4', '< 0.6.0' gem 'puma', '~> 3.11' gem 'bootsnap', '>= 1.1.0', require: false gem 'config', '~> 1.7.0' gem 'jwt', '~> 2.1.0' group :development, :test do gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'rspec-rails' gem 'factory_bot_rails' end group :development do gem 'listen', '>= 3.0.5', '< 3.2' gem 'spring' gem 'spring-watcher-listen', '~> 2.0.0' gem 'rubocop', '~> 0.60.0', require: false end # --- Squasherに関するGemを追加 ここから group :tools do gem 'squasher', '>= 0.6.0' gem 'capistrano' # gem 'rubocop' 当プロジェクトでは既に導入しているので除外 end # --- Squasherに関するGemを追加 ここまで gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
-
Gemをインストールします
# bundle install Fetching gem metadata from https://rubygems.org/......... Fetching gem metadata from https://rubygems.org/. Resolving dependencies... : Fetching squasher 0.6.2 Installing squasher 0.6.2 Bundle complete! 16 Gemfile dependencies, 87 gems now installed. Bundled gems are installed into `./vendor/bundle`
-
Squacherを導入します
# bundle binstub squasher # bin/squasher Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.
環境の違いからか
bundle binstubs bundler --force
するよう求められました。無事に利用可能になりました# bundle binstubs bundler --force # bin/squasher Squasher 0.6.2
実行する
-
squasherコマンドの実行します
「○○より前のマイグレーションファイルを集約する」という指示の仕方になります。今までのマイグレーションファイルを全て集約することにしますので2020年を指定します。Rails 5.2を利用しているのでmオプションの引数に5.2を指定します。# bin/squasher 2020 -m 5.2 Squasher is creating a tmp database Dropped database 'squasher' Created database 'squasher' Squasher is applying migrations on a tmp database == 20181129152052 CreateUsers: migrating ====================================== -- create_table(:users) -> 0.0317s == 20181129152052 CreateUsers: migrated (0.0319s) ============================= : == 20190119150920 DelReferenceToRooms: migrating ============================== -- remove_reference(:rooms, :talk, {:foreign_key=>true}) -> 0.0932s == 20190119150920 DelReferenceToRooms: migrated (0.0934s) =====================
-
SquasherのDBの削除やクリーンの可否を選択します
Squasher's created the `squasher` database for its needs. It might be useful to keep it if any of your deleted migrations inserts data or you squash migrations in a few steps (look at -r option). Keep it (yes / no)? yes # デフォルト
Do you want to clean your database from the old schema migration records(yes/no)? no # デフォルト
実行後のマイグレーションファイルを確認する
集約されたことがわかります。
$ tree
.
├── migrate
│ └── 20190119150920_init_schema.rb
├── schema.rb
└── seeds.rb
再構築してみる
集約されたマイグレーションファイルでDBを再構築してみます。
$ bundle exec rails db:drop
Dropped database 'app_development'
Dropped database 'app_test'
$ bundle exec rails db:create
Created database 'app_development'
Created database 'app_test'
$ bundle exec rails db:migrate
== 20190119150920 InitSchema: migrating =======================================
-- create_table("devices", {:options=>"ENGINE=InnoDB DEFAULT CHARSET=utf8"})
-> 0.0399s
:
-- add_foreign_key("talks", "users")
-> 0.0749s
== 20190119150920 InitSchema: migrated (0.4857s) ==============================
補足(失敗談)
Rails5.2では公式の手順をコピペしてmオプションに5.0を指定すると再構築でエラーが発生します。
# bin/squasher 2020 -m 5.0
Rails5.0ではデフォルトで作成されるidカラムはint型で、Rails5.1以降で作成されるidカラムはbigint型です。Rails5.2で作成された主キーと外部キーの関係はbigint型が前提となっているので、このようなエラーが発生します。
$ bundle exec rails db:migrate
== 20190119150920 InitSchema: migrating =======================================
-- adapter_name()
-> 0.0000s
:
-- add_foreign_key("devices", "users")
rails aborted!
StandardError: An error has occurred, all later migrations canceled:
Column `user_id` on table `devices` has a type of `bigint(20)`.
This does not match column `id` on `users`, which has type `int(11)`.
To resolve this issue, change the type of the `user_id` column on `devices` to be :integer. (For example `t.integer user_id`).
:
bin/rails:4:in `<main>'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)
mオプションで継承するActiveRecord::Migrationのバージョンが変わるようですね。
mオプション | 継承元 |
---|---|
5.0 | class InitSchema < ActiveRecord::Migration[5.0] |
5.2 | class InitSchema < ActiveRecord::Migration[5.2] |
終わりに
先述の失敗談で少しハマりましたが、目的を達成することは出来ました。特にカラム名のtypoを修正するマイグレーションファイルなどは集約されスッキリしてよかったです。