LoginSignup
1
1

More than 5 years have passed since last update.

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

Posted at

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

1
1
0

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
  3. You can use dark theme
What you can do with signing up
1
1