mcpField
D
dhh-coder

dhh-coder

github.com/majesticlabs-dev開発5067 回インストール7 週間前に更新
リポジトリ

dhh-coder について

dhh-coderは、DHHの方針に沿った実装や設計をまとめた、Ruby・Rails開発者向けのコーディングスタイルスキルです。RESTコントローラー、ファットモデル、命名、クエリ最適化、Hotwireのパターンを扱います。

主な機能

  1. 01RESTの7つのコントローラーアクションを使用
  2. 02ビジネスロジックをモデルに配置
  3. 03リクエストコンテキストにCurrentを使用
  4. 04データベースのクエリと書き込みを最適化
  5. 05RailsとHotwireのパターンを紹介

活用シーン

  1. 01DHHのスタイルでRails機能を実装
  2. 02コントローラーとモデルの責務を整理
  3. 03ActiveRecordクエリを最適化
  4. 04レコードによる状態管理とRESTルートを設計

インストール

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

$npx skills add https://github.com/majesticlabs-dev/majestic-marketplace --skill dhh-coder

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

同梱ファイル

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

references/
activerecord-tips.md
activestorage-tips.md
caching-strategies.md
concerns-organization.md
config-tips.md
controllers-tips.md
css-architecture.md
database-patterns.md
delegated-types.md
filter-objects.md
hotwire-tips.md
multi-tenancy.md
palkan-patterns.md
passwordless-auth.md
patterns.md
recording-pattern.md
resources.md
stimulus-catalog.md
structured-events.md
turbo-morphing.md
webhooks.md

詳細

DHH Ruby/Rails スタイルガイド

DHH の哲学に従って Ruby と Rails のコードを書きます。何よりも 巧妙さより明瞭さ、設定より規約、開発者の幸福 を重視します。

クイックリファレンス

コントローラーアクション
  • REST アクションは 7 つのみ:index、show、new、create、edit、update、destroy
  • 新しい振る舞いが必要ですか? 独自アクションではなく、新しいコントローラーを作成します
  • アクションの長さ:最大 1~5 行
  • 空のアクションでも問題ありません:レンダリングは Rails の規約に任せます
class MessagesController < ApplicationController
  before_action :set_message, only: %i[ show edit update destroy ]

  def index
    @messages = @room.messages.with_creator.last_page
    fresh_when @messages
  end

  def show
  end

  def create
    @message = @room.messages.create_with_attachment!(message_params)
    @message.broadcast_create
  end

  private
    def set_message
      @message = @room.messages.find(params[:id])
    end

    def message_params
      params.require(:message).permit(:body, :attachment)
    end
end
プライベートメソッドのインデント

private キーワードの下で、プライベートメソッドを 1 段階インデントします。

  private
    def set_message
      @message = Message.find(params[:id])
    end

    def message_params
      params.require(:message).permit(:body)
    end
モデル設計(ファットモデル)

モデルがビジネスロジック、認可、ブロードキャストを担います。

class Message < ApplicationRecord
  belongs_to :room
  belongs_to :creator, class_name: "User"
  has_many :mentions

  scope :with_creator, -> { includes(:creator) }
  scope :page_before, ->(cursor) { where("id < ?", cursor.id).order(id: :desc).limit(50) }

  def broadcast_create
    broadcast_append_to room, :messages, target: "messages"
  end

  def mentionees
    mentions.includes(:user).map(&:user)
  end
end

class User < ApplicationRecord
  def can_administer?(message)
    message.creator == self || admin?
  end
end
Current 属性

リクエストコンテキストには Current を使い、current_user をあちこちに渡すことは避けます。

class Current < ActiveSupport::CurrentAttributes
  attribute :user, :session
end

# Usage anywhere in app
Current.user.can_administer?(@message)
Ruby の構文に関する好み

DHH 独自のスタイルについては、一般的な Ruby スタイルを説明した ruby-coder スキルを参照してください。

# Symbol arrays with spaces inside brackets
before_action :set_message, only: %i[ show edit update destroy ]

# Expression-less case for cleaner conditionals
case
when params[:before].present?
  @room.messages.page_before(params[:before])
when params[:after].present?
  @room.messages.page_after(params[:after])
else
  @room.messages.last_page
end
クエリの最適化

map(&:name) より pluck(:name) を、messages.to_a.count より messages.count を優先します。処理はデータベースに任せます。

述語に StringInquirer を使う

文字列の enum には .inquiry を使い、読みやすい条件式にします。

class Event < ApplicationRecord
  def action
    super.inquiry
  end
end

# Clean predicate methods
event.action.completed?
event.action.pending?
event.action.failed?
コントローラーのレスポンスパターン

本文のない更新には head :no_content を、作成には head :created を使います。すぐに失敗させる場合は、バングメソッド(create!、update!)を使います。

現在のユーザーのリソースには My:: 名前空間を使う

Current.user にスコープされたリソースには、My:: 名前空間を使います。

# routes.rb
namespace :my do
  resource :profile, only: %i[ show edit update ]
  resources :notifications, only: %i[ index destroy ]
end

# app/controllers/my/profiles_controller.rb
class My::ProfilesController < ApplicationController
  def show
    @profile = Current.user
  end
end

リソースは Current.user から暗黙的に決まるため、ID を使った index や show は必要ありません。

書き込み時に計算する

表示時ではなく、保存時にデータを加工します。

# WRONG: Compute on read
def display_name
  "#{first_name} #{last_name}".titleize
end

# CORRECT: Compute on write
before_save :set_display_name

private
  def set_display_name
    self.display_name = "#{first_name} #{last_name}".titleize
  end

利点:ページネーションとキャッシュが可能になり、ビューの複雑さを軽減できます。

遅延読み込みには delegate を使う

関連を介した遅延読み込みを有効にするには、delegate を使います。

class Message < ApplicationRecord
  belongs_to :session
  delegate :user, to: :session
end

# Lazy loads user through session
message.user
命名規約
要素規約例
セッターメソッドset_ 接頭辞set_message、set_room
パラメータメソッド{model}_paramsmessage_params
関連名汎用的ではなく、意味のある名前user ではなく creator
スコープチェーン可能で説明的with_creator、page_before
述語? で終えるdirect?、can_administer?
現在のユーザーのリソースMy:: 名前空間My::ProfilesController
Hotwire/Turbo パターン

ブロードキャストはモデルの責務です。

# In model
def broadcast_create
  broadcast_append_to room, :messages, target: "messages"
end

Hotwire の詳しいパターンについては、hotwire-coder スキルを使ってください。

エラー処理

特定の例外を捕捉し、バングメソッドですぐに失敗させます。

def create
  @message = @room.messages.create_with_attachment!(message_params)
  @message.broadcast_create
rescue ActiveRecord::RecordNotFound
  render action: :room_not_found
end
状態はレコードで管理する(ブール値ではなく)

ブール値のカラムではなく、データベースのレコードで状態を管理します。

# WRONG: Boolean columns for state
class Card < ApplicationRecord
  # closed: boolean, gilded: boolean columns
end
card.update!(closed: true)
card.closed?  # Loses who/when/why

# CORRECT: State as separate records
class Card < ApplicationRecord
  has_one :closure
  has_one :gilding

  def close(by:)
    create_closure!(closed_by: by)
  end

  def closed?
    closure.present?
  end
end
card.close(by: Current.user)
card.closure.closed_by  # Full audit trail
REST URL の変換

独自アクションを、ネストされたリソースコントローラーに対応付けます。

独自アクションREST リソース
POST /cards/:id/closePOST /cards/:id/closure
DELETE /cards/:id/closeDELETE /cards/:id/closure
POST /cards/:id/gildPOST /cards/:id/gilding
POST /posts/:id/publishPOST /posts/:id/publication
DELETE /posts/:id/publishDELETE /posts/:id/publication
# routes.rb
resources :cards do
  resource :closure, only: %i[ create destroy ]
  resource :gilding, only: %i[ create destroy ]
end

# app/controllers/cards/closures_controller.rb
class Cards::ClosuresController < ApplicationController
  def create
    @card = Card.find(params[:card_id])
    @card.close(by: Current.user)
  end

  def destroy
    @card = Card.find(params[:card_id])
    @card.closure.destroy!
  end
end
アーキテクチャに関する好み
従来の方法DHH の方法
PostgreSQLSQLite(シングルテナントの場合)
Redis + SidekiqSolid Queue
Redis キャッシュSolid Cache
Kubernetes単一の Docker コンテナ
サービスオブジェクトファットモデル
ポリシーオブジェクト(Pundit)User モデルでの認可
FactoryBotフィクスチャ
ブール値の状態カラムレコードで状態を管理

詳細なリファレンス

包括的なパターンと例については、以下を参照してください。

コアパターン
  • references/patterns.md - 説明付きのコードパターン集
  • references/palkan-patterns.md - 名前空間付きモデルクラス、カウンターキャッシュ、モデルの整理順序、PostgreSQL の列挙型
  • references/concerns-organization.md - モデル固有の Concern と共通 Concern、ファサードパターン
  • references/delegated-types.md - STI の問題を避けるポリモーフィズム
  • references/recording-pattern.md - 多様なコンテンツタイプを統一する抽象化
  • references/filter-objects.md - PORO のフィルターオブジェクト、URL ベースの状態、テスト可能なクエリ構築
  • references/database-patterns.md - UUIDv7、ハード削除、レコードとしての状態、カウンターキャッシュ、インデックス
Rails コンポーネント
  • references/activerecord-tips.md - ActiveRecord のクエリパターン、バリデーション、関連付け
  • references/controllers-tips.md - コントローラーパターン、ルーティング、レート制限、フォームオブジェクト
  • references/activestorage-tips.md - ファイルアップロード、添付ファイル、Blob の処理
Hotwire
  • references/hotwire-tips.md - Turbo Frames、Turbo Streams、ViewComponents
  • references/turbo-morphing.md - モーフィングパターンを使った Turbo 8 のページ更新
  • references/stimulus-catalog.md - コピーしてすぐに使える Stimulus コントローラー(クリップボード、ダイアログ、ホットキーなど)
  • こちらも参照: 詳細なパターンについては、hotwire-coder、stimulus-coder、viewcomponent-coder のスキルをご覧ください
フロントエンド
  • references/css-architecture.md - ネイティブ CSS のパターン(レイヤー、OKLCH、ネスト、ダークモード)
認証とマルチテナンシー
  • references/passwordless-auth.md - マジックリンク認証、セッション、アイデンティティモデル
  • references/multi-tenancy.md - パスベースのテナンシー、Cookie のスコープ設定、テナント対応ジョブ
インフラストラクチャとインテグレーション
  • references/webhooks.md - 安全な Webhook 配信、SSRF 対策、リトライ戦略
  • references/caching-strategies.md - ロシア人形キャッシュ、Solid Cache、キャッシュ分析
  • references/config-tips.md - 設定、ログ記録、デプロイパターン
  • references/structured-events.md - 構造化された可観測性のための Rails 8.1 Rails.event API
  • references/resources.md - 参考資料と関連情報へのリンク

哲学のまとめ

  1. REST の純粋性: アクションは 7 つのみ。バリエーションには新しいコントローラーを作成
  2. ファットモデル: 認可、ブロードキャスト、ビジネスロジックをモデルに配置
  3. シンコントローラー: アクションは 1~5 行にし、複雑な処理は切り出す
  4. 設定より規約: 空のメソッド、暗黙のレンダリング
  5. 最小限の抽象化: 単純なケースではサービスオブジェクトを使わない
  6. Current attributes: あらゆる場所でスレッドローカルなリクエストコンテキストを使用
  7. Hotwire を優先: モデルレベルのブロードキャスト、Turbo Streams、Stimulus
  8. 読みやすいコード: 意味の明確な命名、小さなメソッド、コメント不要

成功の指標

次の条件を満たすコードは、DHH のスタイルに沿っています。

  • コントローラーが CRUD の動詞をリソースに対応付けている(独自アクションなし)
  • モデルが Concern を使って横断的な振る舞いを共有している
  • 状態をブール型カラムではなくレコードで管理している
  • 抽象化を最小限に保っている(不要なサービスオブジェクトなし)
  • データベースを活用して解決している(Redis ではなく Solid Queue/Cache)
  • すべてのインタラクティブな動作を Turbo/Stimulus で処理している
  • 認可が User モデルに定義されている(can_*? メソッド)
  • Current attributes がリクエストコンテキストを提供している
  • スコープが命名規約に従っている(chronologically、with_* など)
  • 属性の抽出に map ではなく pluck を使っている
  • 現在のユーザーのリソースに My:: 名前空間を使っている
  • データを表示時ではなく書き込み時に計算している

コメント