rails-dev について
Rails 開発向けのスキルで、コーディングエージェントにプロジェクト規約を示し、領域ごとの作業前に対応するリファレンスを読むよう求めます。モデル、CRUD、テスト、データベース制約、Ruby のスタイルを扱います。
主な機能
- 01リッチモデルへのドメインロジック集約
- 02CRUD を軸にしたリソース設計
- 03データベース制約による不変条件の保証
- 04領域別リファレンスの参照手順
- 05Ruby のスタイルとエラー処理規約
活用シーン
- 01Rails のモデルやコントローラーの設計
- 02マイグレーションとルートの規約策定
- 03テストやエラー処理のレビュー
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ tech-leads-club/ agent-skills --skill rails-devインストール後、Claude Code で/rails-devを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/api.mdasync.mdauth.mdcaching.mdconcerns.mdcrud.mderror-handling.mdevents.mdhttp-client.mdjobs.mdlogging.mdmailer.mdmigration.mdmodel.mdmulti-tenant.mdreview.mdroutes.mdstate-records.mdstimulus.mdtest.mdturbo.mdvalidator.mdview.mdwebhooks.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 Queue | references/jobs.md |
| 並行処理、ファイバー、Async、外部 I/O | references/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、Faraday | references/http-client.md |
| ロギング、ログメッセージ、Rails.logger | references/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
参照元
これらの規約は、以下の参照アプリを参考にしていますが、異なる場合があります。