LoginSignup
6

More than 1 year has passed since last update.

posted at

updated at

migrateを辞めてridgepoleを導入する

導入のきっかけ

どれだけ緻密にテーブル設計をしても、途中仕様変更や追加要望が入って、初期の見積もりから大幅にテーブル構造が変更されることは目に見えてる。
Railsの場合、スキーマを変更するにはスキーマファイルを追加していかないといけない。
でもスキーマの変更履歴は git で追えるし、ぶっちゃけ最新のスキーマが見られればそれで十分なんだよね🤔

ということで無駄にスキーマファイルが増えるのを嫌い、ridgepole導入を決意しました。
めちゃ簡単だよ🙆‍♂️

動作環境

ruby 3.0.2
Rails 6.1.4
MySQL 8.0.25

導入手順

1. ridgepoleをインストール

Gemfile
gem 'ridgepole'
terminal
% bundle install

Gemfileに rigepole を追記し bundle install
これで面倒な migrate ではなく ridgepole が使用できるようになりました。はやっ。

2. 試しにauthorsテーブルを作ってみる

ridgepoleに関わるファイルは1つの階層に纏めておきたいので、dbディレクトリ配下にschemasディレクトリを作成します。
(自分が分かりやすければ、名前はschemasじゃなくてもOK)
次に Schemafile という名前のファイルをschemasディレクトリに作成。
そして同じ階層にauthorsテーブルのスキーマを定義したスキーマファイルを作成します。

terminal
% mkdir -p db/schemas
% touch db/schemas/Schemafile
% touch db/schemas/authors.schema.rb

authorsテーブルのスキーマは仮に下記のように定義しておくとしましょう。

db/schemas/authors.schema.rb
create_table :authors, force: :cascade, charset: 'utf8mb4', collation: 'utf8mb4_0900_ai_ci', options: 'ENGINE=InnoDB ROW_FORMAT=DYNAMIC' do |t|
  t.string  :name, null: false, default: ''
  t.integer :age,  null: false, default: 20

  t.index :name, name: 'index_authors_on_name'
end

authorsテーブルのスキーマファイルが出来たら、それを Schemafile で読み込んで。

Schemafile
require 'authors.schema.rb'

そして最後にridgepoleコマンドを実行!

terminal
% bundle exec ridgepole --config config/database.yml --env development --file db/schemas/Schemafile --apply

やや長ったらしいコマンドで分かりづらいので、少しだけridgepoleコマンドのオプションに関して説明します☝️
※ 詳しくは公式ドキュメントのHelp参照

オプション 省略形 引数 役割
--config -c config/database.yml DBに関するconfigファイルの指定
--env -E development, test, production 実行環境の指定
--file -f db/schemas/Schemafile スキーマファイルの指定
--apply -a なし apply(実行)

つまり先程のコマンドは、

・ DBに関するconfigファイルは config/database.yml
・ 実行環境は development
・ スキーマファイルは db/schemas/Schemafile

以上の設定でridgepoleコマンドをapply(実行)します、という意味でした🤗
意外とシンプル笑

さて、本題に戻ります↩️
先程のコマンドを実行すると、下記のような実行結果が表示されてると思います。

terminal
Apply `db/schemas/Schemafile`
-- create_table("authors", {:charset=>"utf8mb4", :collation=>"utf8mb4_0900_ai_ci", :options=>"ENGINE=InnoDB ROW_FORMAT=DYNAMIC"})
   -> 0.0501s
-- add_index("authors", ["name"], {:name=>"index_authors_on_name"})
   -> 0.0101s

表示結果を見る限り、authorsテーブルが作られindexも貼られているようです。
では実際にDBにログインしてテーブルが正しく作成されているか確認しましょう。

terminal
% mysql db名

ログイン出来たらテーブル一覧を表示し、authorsテーブルがあればテーブル構造を確認します。

mysql
mysql> show tables;
+-------------------------------+
| Tables_in_db_name             |
+-------------------------------+
| ar_internal_metadata          |
| authors                       |
| schema_migrations             |
+-------------------------------+
3 rows in set (0.00 sec)

mysql> desc authors;
+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | bigint       | NO   | PRI | NULL    | auto_increment |
| name  | varchar(255) | NO   | MUL |         |                |
| age   | int          | NO   |     | 20      |                |
+-------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

うんうん、無事に作られていたようですね!とても簡単◎

3. スキーマを変更したい場合

先程のauthorsテーブルですが、実はとっても大切なカラムを忘れていました。
そう、timestampsです。

timestampsとは、どのテーブルにも当たり前のようにあるcreated_atupdated_atのことです。
レコードの作成日時と更新日時を保持する大事なカラムですね。

Railsデフォルトのmigrateを使用していた場合、rails g migrate 〜コマンドでスキーマファイルを追加する必要がありましたが、ridgepoleは違います。
なんと先程作ったauthorsスキーマを書き換え、再度ridgepoleコマンドを実行するだけでいいのです!簡単✨

ということで早速編集。

db/schemas/authors.schema.rb
create_table :authors, force: :cascade, charset: 'utf8mb4', collation: 'utf8mb4_0900_ai_ci', options: 'ENGINE=InnoDB ROW_FORMAT=DYNAMIC' do |t|
  t.string  :name, null: false, default: ''
  t.integer :age,  null: false, default: 20
+ 
+ t.timestamps

  t.index :name, name: 'index_authors_on_name'
end

そしてridgepoleコマンド実行!

terminal
% bundle exec ridgepole --config config/database.yml -env development --file db/schemas/Schemafile --apply

以下、実行結果。

terminal
Apply `db/schemas/Schemafile`
-- add_column("authors", "created_at", :datetime, {:null=>false, :after=>"age"})
   -> 0.0169s
-- add_column("authors", "updated_at", :datetime, {:null=>false, :after=>"created_at"})
   -> 0.0145s

うん、無事に追加出来たようですね!
先程の手順と同様、DBにログインしてテーブル構造を確認してみます。

terminal
% mysql db名
mysql
mysql> desc authors;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | bigint       | NO   | PRI | NULL    | auto_increment |
| name       | varchar(255) | NO   | MUL |         |                |
| age        | int          | NO   |     | 20      |                |
| created_at | datetime     | NO   |     | NULL    |                |
| updated_at | datetime     | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

しっかり追加されていました!万歳🙌

4. おまけ

最後にridgepoleコマンドを短くする方法を紹介します。
これをすると下記のように短いコマンドで同じことを出来るようになります。

terminal
# 通常のridgepoleコマンド
% bundle exec ridgepole --config config/database.yml -env development --file db/schemas/Schemafile --apply

# rakeタスクを使ったridgepoleコマンド
% bundle exec rails ridgepole:apply

では実際の作業手順を説明していきます。

ridgepoleはとても便利だけどコマンドが少々長く覚えづらいのがデメリットです。
しかもconfigファイルやスキーマファイルを毎回コマンド実行のたびに指定しなきゃいけないのはちょっと無駄な気がする。。。

しかし、こんなデメリットはrakeタスクを作ると解消できますよ🙆‍♂️
今回はridgepoleという名前のタスクにします。

terminal
% bundle exec rails g task ridgepole

するとlib/tasksディレクトリの配下にridgepole.rakeというファイルが作成されます。
このファイルを以下のように編集してください。

lib/tasks/ridgepole.rake
namespace :ridgepole do
  desc 'Apply schema to database'
  task apply: :environment do
    run('--apply')
  end

  private

    def run(*options)
      config_file = 'config/database.yml'
      schema_file = 'db/schemas/Schemafile'
      rails_env   = ENV['RAILS_ENV'] || Rails.env

      base_command = "bundle exec ridgepole --config #{config_file} --env #{rails_env} --file #{schema_file}"
      full_command = [base_command, *options].join(' ')

      puts '=== run ridgepole... ==='
      puts "[Running] #{full_command}"

      system full_command
    end
end

これでrakeタスクを作ることが出来ました!簡単でしょ?
ちなみにridgepoleコマンドのオプションを省略形で書かないのは、誰が見ても分かるように(もしくは分かりやすく)するため。
今後ridgepoleコマンドを実行したいときは下記のコマンドで実行できます🎉

terminal
% bundle exec rails ridgepole:apply

5. さらにおまけ

ridgepoleはスキーマをDBに反映させる以外に、DBの内容をSchemafileに反映することもできます。
その機能も使う場合にはrakeタスクを下記のように変更しましょう。

lib/tasks/ridgepole.rake
namespace :ridgepole do
  desc 'Apply schema to database'
  task apply: :environment do
    run('--apply', '--file')
  end

  desc 'Export schema to Schemafile'
  task export: :environment do
    run('--export', '--split', '--output')
  end

  private

    def run(*options)
      config_file = 'config/database.yml'
      schema_file = 'db/schemas/Schemafile'
      rails_env   = ENV['RAILS_ENV'] || Rails.env

      base_command = "bundle exec ridgepole --config #{config_file} --env #{rails_env}"
      full_command = [base_command, *options, schema_file].join(' ')

      puts '=== run ridgepole... ==='
      puts "[Running] #{full_command}"

      system full_command
    end
end

使い方は以下の通り。

terminal
% bundle exec ridgepole:export

Enjoy ridgepole's life 👍

参考記事

Ridgepole - Github
Rakeタスクの実装

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
6