🔖

Rails7.13 generatorコマンドを使用してファイルを自動生成

2024/03/05に公開

はじめに

Rails generatorコマンドを使用して、model、service、rspec関連ファイルを効率的に生成する方法をご紹介します。

実行環境

  • M2 mac mini
  • Rails 7.1.3
  • Ruby 3.2.3
  • Mysql 8.0
  • Docker 25.0.2
  • vscodeエディタ

環境構築が済んでいない方はこちらを参考に環境構築を行なってください。
https://zenn.dev/kei1232/articles/0fac51829570c1

Schemafileの導入がまだの方はこちらから設定してください。
https://zenn.dev/kei1232/articles/5c31cc58453453

RSpecの導入 
https://zenn.dev/kei1232/articles/941b465240bc1d

Grapeとgrape-entityの導入 
https://zenn.dev/kei1232/articles/3a9218761eff35

フォルダ構成

app/
  api/
    v1/
      posts.rb
  entitys/
    post.rb
  models/
    post.rb
  services/
    posts.rb
db/
  Schemafile
lib/
  generators/
    apis/
      apis_generator.rb
      USAGE
      template/
        controller.rb.erb
        entity.rb.erb
        service.rb.erb
        test.rb.erb
spec/
  models/
    post_spec.rb
  factories/
    posts.rb
  services/
    post_service_spec.rb

serviceフォルダの作成

mkdir app/services && mkdir spec/services

generatorの作成

ターミナルから以下のコマンドでrailsのgeneratorにapisを追加
※apiの場合、a_p_iとなってしまうため、apisで作成。

docker-compose exec web rails g generator apis
  • create lib/generators/apis
  • create lib/generators/apis/apis_generator.rb
  • create lib/generators/apis/USAGE
  • create lib/generators/apis/templates
  • invoke rspec
    以下のフォルダとファイルを削除(使用しないため)
  • create spec/generator/apis_generator_spec.rb

作成したgeneratorを確認

docker-compose exec web rails g -h
Apis:
  apis

生成する雛形ファイルを生成

touch lib/generators/apis/templates/{controller.rb.erb,entity.rb.erb,service.rb.erb,test.rb.erb}

雛形ファイルを修正

controller雛形修正

lib/generators/apis/templates/controller.rb.erb
module V1
  class <%= class_name.pluralize.camelize %> < Grape::API

=begin
    <%- class_name.camelize.constantize.columns.each do |col| -%>
    optional :<%= col.name %>, type: <%=
    col.type.to_s == 'datetime' ? 'DateTime':
    col.type.to_s == 'text' ? 'String':
    col.type.to_s == 'boolean' ? 'Boolean':
    col.type.to_s.camelize %>, description:''
    <%- end -%>
=end

    resource :<%= class_name.pluralize.underscore %> do

      desc '一覧取得', { success: { model: Entitys::<%= class_name.camelize %> }, is_array:true }
      params do
        use :pagination
      end
      get '' do
        <%= class_name.underscore %> = <%= class_name.camelize %>.all.per(params[:per_page])
        present <%= class_name.underscore %>, with: Entitys::<%= class_name.camelize %>
      end

      desc '詳細取得', { success: { model: Entitys::<%= class_name.camelize %> } }
      params do
        requires :id, type: Integer, description:'ID'
      end
      get '/:id' do
        <%= class_name.underscore %> = <%= class_name %>.find(params)
        present <%= class_name.underscore %>, with: Entitys::<%= class_name.camelize %>
      end

      desc '新規作成', { success: { model: Entitys::<%= class_name.camelize %> } }
      params do
      end
      post '' do
        <%= class_name.underscore %> = <%= class_name %>.create!(params)
        present <%= class_name.underscore %>, with: Entitys::<%= class_name.camelize %>
      end

      desc '更新', { success: { model: Entitys::<%= class_name.camelize %> } }
      params do
        requires :id, type: Integer, description:'ID'
      end
      put '/:id' do
        <%= class_name.underscore %> = <%= class_name %>.find(params[:id])
        <%= class_name.underscore %>.save!
        present <%= class_name.underscore %>, with: Entitys::<%= class_name.camelize %>
      end

      desc '削除', { success: { model: Entitys::<%= class_name.camelize %> } }
      params do
        requires :id, type: Integer, description:'ID'
      end
      delete '/:id' do
        <%= class_name.underscore %> = <%= class_name %>.find(params[:id])
        <%= class_name.underscore %>.delete
        present <%= class_name.underscore %>, with: Entitys::<%= class_name.camelize %>
      end
    end
  end
end

entity雛形修正

lib/generators/apis/templates/entity.rb.erb
module Entitys
  class <%= class_name.camelize %> < Grape::Entity
    <%- class_name.camelize.constantize.columns.each do |col| -%>
    expose :<%= col.name %>, documentation: { type: '<%=
    col.type.to_s == 'datetime' ? 'DateTime':
    col.type.to_s == 'text' ? 'String':
    col.type.to_s == 'boolean' ? 'boolean':
    col.type.to_s.camelize %>' }
    <%- end -%>
  end
end

service雛形修正

lib/generators/apis/templates/service.rb.erb
module Service
  class <%= class_name.pluralize.camelize %>
  end
end

test雛形修正

lib/generators/apis/templates/test.rb.erb
require 'rails_helper'

RSpec.describe <%= class_name.camelize %> do


end

apis_generator.rb

lib/generators/apis/apis_generator.rb
class ApisGenerator < Rails::Generators::NamedBase
  source_root File.expand_path("templates", __dir__)

  # APIのコントローラーを作成する
  def create_controller_file
    destination = Rails.root.join("app/api/v1/#{class_name.pluralize.underscore}.rb")
    template('controller.rb.erb', destination)
  end

  # APIのEntityを作成する
  def create_entity_file
    destination = Rails.root.join("app/api/entitys/#{class_name.underscore}.rb")
    template('entity.rb.erb', destination)
  end

  # サービスクラスを作成する
  def create_service_file
    destination = Rails.root.join("app/services/service/#{class_name.pluralize.underscore}.rb")
    template('service.rb.erb', destination)
  end

  # サービスクラスのspecテストクラスを作成する
  def create_test_file
    destination = Rails.root.join("spec/services/#{class_name.underscore}_service_spec.rb")
    template('test.rb.erb', destination)
  end
end

ファイルの生成

以下の手順で作成し、コマンドを実行
※ model、DBにマイグレーションしていない場合、生成に失敗します。
1.Schemafileにテーブルを定義
2.テーブルをDBにマイグレーション
3.モデルの作成(マイグレーションをスキップ)
4.generatorコマンドでファイルを生成

Schemafileにpostsテーブルを適当に定義

Schemafile
options = "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED"

create_table "posts", force: :cascade, options: options do |t|
  t.string :title, null: false
end

テーブルをDBにマイグレーション

docker-compose exec web rake ridgepole:apply RAILS_ENV=development

モデルの作成

docker-compose exec web rails g model Post --skip-migration

entity、service、rspec関連ファイルを生成

docker-compose exec web rails g apis Post

まとめ

Rails generatorを活用すれば、開発時間を大幅に短縮することができます。ぜひ本記事を参考に、挑戦してみてください。

Discussion