前置き
モバイルアプリのバックエンドを学習を兼ねて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を追記します
Gemfile
source '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]
1. 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`するよう求められました。無事に利用可能になりました:thumbsup:
```
# 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) =====================
1. 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を再構築してみます。
```bash
$ 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を修正するマイグレーションファイルなどは集約されスッキリしてよかったです。