Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

migrationファイルを生成するgenerator(rails g your_gem:install)の作り方

More than 1 year has passed since last update.

devise gempaper_trail gemでやっているような、migrationファイルをテンプレートから自動生成するgemの作り方。

前提知識1:Railsのジェネレーター

migrationファイルの生成は、Railsのジェネレーターという機能を使って実装されており、最終的にはrails generate your_gem:installというコマンドでmigrationファイルを生成することになる。概念的な話と基本的な機能はRailsのチュートリアルを読むと分かりやすい。

Rails ジェネレータとテンプレート入門

前提知識2:アプリケーションテンプレート

Railsのジェネレーターの通常の使い方はgenerate model user name:stringのように自分が作っているRailsアプリケーションにファイルを追加すること。

それをさらに汎用的にしたのがアプリケーションテンプレートと呼ばれる仕組みであり、その中でも、generate your_gem:installについては、所定のディレクトリにあらかじめ決められたファイルを置くだけで実現できる。

# まずは自分のgemを作る必要がある
bundle gem your_gem

所定のディレクトリにinstall_generator.rbを置く。

your_gem/lib/generators/your_gem/install/install_generator.rb
module YourGem
  class InstallGenerator < ::Rails::Generators::Base
    include ::Rails::Generators::Migration

    source_root File.expand_path('templates', __dir__)

    # メソッド名は何でもよい。インスタンスメソッドが上から順番に実行される
    def create_migration_file
      template = 'create_users'
      migration_dir = File.expand_path("db/migrate")

      if self.class.migration_exists?(migration_dir, template)
        ::Kernel.warn "Migration already exists: #{template}"
      else
        migration_template(
            "#{template}.rb.erb",
            "db/migrate/#{template}.rb",
            migration_version: migration_version
        )
      end
    end

    private

    def migration_version
      major = ActiveRecord::VERSION::MAJOR
      if major >= 5
        "[#{major}.#{ActiveRecord::VERSION::MINOR}]"
      end
    end
  end
end

migrationファイルのテンプレートの書き方は下記の通り。

your_gem/lib/generators/your_gem/install/templates/create_users.rb.erb
class CreateUsers < ActiveRecord::Migration<%= migration_version %>
  def change
    create_table :users do |t|
      t.string   :name
    end
  end
end

上記の2ファイルを作れば、それだけでmigrationファイルの生成が実行できる。

# 作ったgemをインストールする
gem install 'your_gem'

# migrationファイルのコピーを実行する
rails generate your_gem:install

実用的にするための工夫

上記の手順だけで基本的なmigrationファイルの生成はできるが、実用上は下記の問題が起きる。

  • migrationファイルのバージョン番号を生成する必要がある(例:20181217174759_create_users)
  • migrationファイルの書き方はRailsのバージョンによって異なっている
  • 利用するRDBによって使える機能に差がある

それぞれの解決策の例は下記の通り。

# migrationファイルのバージョン生成
ActiveRecord::Generators::Base.next_migration_number(dirname)
# Railsのバージョンに応じたmigrationファイルの書き分け
def migration_version
  major = ActiveRecord::VERSION::MAJOR
  if major >= 5
    "[#{major}.#{ActiveRecord::VERSION::MINOR}]"
  end
end
# 使っているRDBの判別
def mysql?
  MYSQL_ADAPTERS.include?(ActiveRecord::Base.connection.class.name)
end

# RDBごとのCREATE TABLEのオプションの書き分け
def users_table_options
  if mysql?
    ', { options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci" }'
  else
    ""
  end
end

実際にはさらに丁寧なバージョンのチェック、細かいif文が必要であり、ここには詳細を書ききれない。この辺は必要に応じて実際のgemを見た方が分かりやすい。

devise / devise_generator.rb
paper_trail / install_generator.rb
rails / migration_generator.rb

ts-3156
公開している記事のうち、私が集計まで担当しているものは、希望があれば元データをお渡しいたします。ご希望される方は、Twitterでご連絡ください。
https://twitter.com/ts_3156
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away