mcpField
R
rails-dev

rails-dev

github.com/tech-leads-club開発7.1k66 回インストール2 日前に更新
リポジトリ

rails-dev について

Rails 開発向けのスキルで、コーディングエージェントにプロジェクト規約を示し、領域ごとの作業前に対応するリファレンスを読むよう求めます。モデル、CRUD、テスト、データベース制約、Ruby のスタイルを扱います。

主な機能

  1. 01リッチモデルへのドメインロジック集約
  2. 02CRUD を軸にしたリソース設計
  3. 03データベース制約による不変条件の保証
  4. 04領域別リファレンスの参照手順
  5. 05Ruby のスタイルとエラー処理規約

活用シーン

  1. 01Rails のモデルやコントローラーの設計
  2. 02マイグレーションとルートの規約策定
  3. 03テストやエラー処理のレビュー

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/tech-leads-club/agent-skills --skill rails-dev

インストール後、Claude Code で/rails-devを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
api.md
async.md
auth.md
caching.md
concerns.md
crud.md
error-handling.md
events.md
http-client.md
jobs.md
logging.md
mailer.md
migration.md
model.md
multi-tenant.md
review.md
routes.md
state-records.md
stimulus.md
test.md
turbo.md
validator.md
view.md
webhooks.md

詳細

Rails の規約

基本理念

  • 豊富なモデル - ビジネスロジックはサービスオブジェクトではなく、モデルに置きます
  • すべては CRUD - 新しいアクションではなく、新しいリソースを追加します(post :close ではなく resource :closure)
  • 状態はレコードで表現 - closed: boolean ではなく Closure モデルを使います
  • 合成には Concern を使う - Closeable、Watchable、Commentable
  • 巧妙さより明示性 - 繰り返しが実際に発生するまではインラインで記述します。抽象化は必要性があって初めて導入し、憶測に基づいて作りません
  • 小さなインターフェース - 呼び出し元のないパブリックメソッドは作りません
  • クラッシュさせる - バングメソッド(create!)を使い、失敗は事前のガード(references/error-handling.md)で防ぐのではなく、境界で処理します
  • 不変条件はスキーマに置く - 必須、ユニーク、範囲などの厳格なルールは NOT NULL/unique/check 制約にします。バリデーションはユーザー向けのメッセージに使い、信頼できる情報源にはしません。参照はソフトなままにします。外部キーは使わず、整合性はモデル層で担保します
  • 依存関係は最小限に - gem を導入する前に自分で実装します。Devise、Pundit、RSpec、FactoryBot、ViewComponent、サービス/フォームオブジェクト、デコレーターは使いません
  • データベースを活用 - Redis は使わず、Solid Queue/Cache/Cable を使います
  • テストカバレッジ - テストの比率をおおむね 1:1(コード 1 行につきテスト 1 行)に保ちます

参照先の選び方

この表は索引であり、内容そのものではありません。規約はこの表やコードベース、一般的な Rails の知識ではなく、参照ファイルに記載されています。タスクに該当する行を確認し、コードを設計、記述、レビュー、分析する前に、該当するファイルを最初から最後まで読んでください。表の行を読むことは参照ファイルを読むことではありません。コードベースから推測すると、誤った規約をリリースすることになります。

タスク参照先
モデル、バリデーション、アソシエーション、ビジネスロジックreferences/model.md
カスタムバリデーター、複数モデルで再利用するバリデーションルールreferences/validator.md
エラー処理、rescue の境界、報告、再試行references/error-handling.md
コントローラー、CRUD アクションreferences/crud.md
ルート、config/routes.rb、リソースのマッピングreferences/routes.md
Concern、共有する振る舞いreferences/concerns.md
状態の追跡(ブール値ではなく)references/state-records.md
認証、認可、セッション、IDOR のスコープ制御references/auth.md
データベースマイグレーションreferences/migration.md
Minitest、フィクスチャ、テストreferences/test.md
ビュー、ERB、パーシャル、ヘルパー、プレゼンテーションロジックreferences/view.md
Turbo Frames、Turbo Streams、リアルタイム処理references/turbo.md
Stimulus コントローラー、JavaScript の小規模な追加references/stimulus.md
バックグラウンドジョブ、Solid Queuereferences/jobs.md
並行処理、ファイバー、Async、外部 I/Oreferences/async.md
メーラー、メール通知references/mailer.md
フラグメントキャッシュ、HTTP キャッシュreferences/caching.md
REST API、JSON レスポンスreferences/api.md
マルチテナンシー、アカウントのスコープ制御references/multi-tenant.md
イベント追跡、アクティビティログreferences/events.md
Webhook(受信/送信)、受信箱、冪等性references/webhooks.md
コードレビュー、一貫性チェックreferences/review.md
HTTP クライアント、外部 API、Faradayreferences/http-client.md
ロギング、ログメッセージ、Rails.loggerreferences/logging.md

コーディングスタイル

以下は横断的なルールであり、分野にかかわらずすべてのファイルに適用されます。参照先の代わりにはなりません。分野固有の内容(モデル、コントローラー、ジョブ、ビュー、テストなど)については、コードを記述またはレビューする前に、上の表にある該当する参照先を必ず読み込んでください。

条件分岐

値を返すガード節より、展開した if/else を使います。メソッドの先頭にあるガード節は、本体が単純でない場合は問題ありません。

# Don't: value-returning guard clause for a simple branch
def status_label
  return "closed" if closed?
  "open"
end

# Do: expanded if/else
def status_label
  if closed?
    "closed"
  else
    "open"
  end
end
メソッドの順序

クラスメソッド、パブリックメソッド(initialize を先頭にする)、プライベートメソッドの順にします。メソッドは呼び出し順に縦に並べ、呼び出し元を呼び出し先より上に配置します。

# Don't: callee above its caller, public after private
class Signup
  def create_member = Member.create!(email:)
  def call = create_member
end

# Do: class method, then the caller, then its callees
class Signup
  def self.call(...) = new(...).call

  def call = create_member

  private
    def create_member = Member.create!(email:)
end
可視性

private の後に空行を入れず、その下にメソッドをインデントします。プライベートメソッドのみのモジュールでは、先頭に private を置き、その後に空行を入れます。インデントはしません。

# Don't: blank line after private, methods not indented
class Card
  private

  def closure_exists? = closure.present?
end

# Do: no blank line, indented under private
class Card
  private
    def closure_exists? = closure.present?
end
Bang メソッド

! は、対応する bang なしのメソッドが存在する場合(save/save! など)にのみ使用します。破壊的な操作や重要な操作であることを示すためだけに ! を付けてはいけません。

# Don't: unpaired bang used to signal "this is destructive"
def revoke! = update!(revoked_at: Time.current)

# Do: plain domain verb; the ! belongs to the paired persistence call
def revoke = update!(revoked_at: Time.current)
メソッドの命名

公開メソッドには、ドメインの動詞を使います。動詞がスコープ、述語、またはコアメソッド(Kernel#fail)と衝突する場合は、mark_ を前に付けます(bang は付けません)。クラスや概念の命名については、references/model.md を参照してください。

# Don't: mark_ prefix when the plain verb is free
def mark_published = update!(published_at: Time.current)

# Do: plain verb; reserve mark_ for a real collision (fail -> Kernel#fail)
def publish = update!(published_at: Time.current)
def mark_failed = update!(failed_at: Time.current)
コメント

原則としてコメントは付けません。ほとんどのコメントはノイズです。意味はコードと命名で伝えるようにします。

コードだけでは伝えられない場合に限り、コメントを追加します。

  • パスやコードだけでは問題を明確にできない場合。
  • モデルの言葉の意味が曖昧で、ドメイン内での位置付けを補足する必要がある場合。

次のことは避けてください。

  • コードがすでに示している内容を繰り返す。
  • コードをセクション分けする(テスト内の # ----- some_method ----- のようなバナーなど)。

コメントを付ける場合は、次のようにします。

  • 常に平易な日本語で書く。
  • 簡潔にする。
  • LLM らしい、ありきたりな語彙を使わない。
  • 説明文よりリストのほうが読みやすい場合は、リストを使う。
# Don't: restates what the code already says; section banner
# increment the attempts counter
attempts += 1

# ----- private helpers -----

# Do: explains the non-obvious why
# Circle returns 422 on duplicate emails, so we reconcile instead of recreating.
reconcile_member(email)
エラー

エラーを握りつぶしてはいけません。通常は、ログに記録して false を返すのではなく、返却するオブジェクトに記録するなどして、呼び出し元が失敗にアクセスできるようにします。エラー処理の規約(記録するか再送出するか、報告、ラップ、リトライ)については、references/error-handling.md を参照してください。

# Don't: swallow the error; the caller can't tell it failed
def provision_circle
  circle_client.create_community_member(...)
rescue Circle::AdminClient::ApiError
  false
end

# Do: record the failure on the returned object
def provision_circle
  circle_client.create_community_member(...)
  circle_access.mark_successful(external_id: response.dig(:community_member, :id))
rescue Circle::AdminClient::ApiError => e
  circle_access.mark_failed(error: e.message)   # caller checks access.failed?
end

参照元

これらの規約は、以下の参照アプリを参考にしていますが、異なる場合があります。

コメント