mcpField
J
jr-rails-classic

jr-rails-classic

github.com/julianrubisch開発3046 回インストール4 週間前に更新
リポジトリ

jr-rails-classic について

jr-rails-classicは、37signalsの慣習に基づいてRails開発者を支援するコーディングスタイルスキルです。リッチなドメインモデル、RESTリソース設計、fixturesを使ったMinitestを重視します。

主な機能

  1. 01ジェネレーターを優先する開発手順
  2. 02リッチなドメインモデルと薄いコントローラー
  3. 03状態変更をサブリソースに割り当て
  4. 04Minitestとfixturesによるテスト
  5. 05データベース制約とジョブの設計指針

活用シーン

  1. 0137signalsの慣習に沿ったRails機能の実装
  2. 02RESTコントローラーとデータモデルの設計
  3. 03Railsアプリのテストとバックグラウンドジョブの設計

インストール

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

$npx skills add https://github.com/julianrubisch/skills --skill jr-rails-classic

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

同梱ファイル

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

reference/
agentic-worktrees.md
anti-patterns.md
coding-classic.md
coding-phlex.md
devops.md
patterns.md
reek.yml
reference/coding/
api.md
reference/refactorings/
000-template.md
001-extract-scope-from-controller.md
002-replace-conditional-with-polymorphism.md
003-replace-conditional-with-null-object.md
004-introduce-form-object.md
005-replace-subclasses-with-strategies.md
006-replace-mixin-with-composition.md
007-extract-validator.md
008-introduce-parameter-object.md
009-replace-callback-with-method.md
010-refactor-service-object-into-poro.md
extraction-signals.md
reference/shared/
architecture.md
authorization.md
callbacks.md
components.md
concerns.md
configuration.md
current_attributes.md
graphql.md
hotwire.md
instrumentation.md

詳細

Rails クラシックコーディングスタイル

37signals の規約に従って Rails アプリケーションのコードを書きます。リッチなドメインモデル、CRUD コントローラー、すべてデータベースベース、フィクスチャを使った Minitest。

コアワークフロー

  1. まずジェネレーターを使う — rails g model、rails g controller、 rails g migration。ジェネレーターは正しいファイル構造、テストスタブ、 ルートエントリを一度に生成します。
  2. 実装順序 — モデル → コントローラー → ビュー → テスト。
  3. Ship, Validate, Refine — プロトタイプから本番へ、実際の利用から学ぶ。
  4. Let it crash — bang メソッドを使い、RecordInvalid は Rails に 422 で処理させる。

ガードレール

  • サービスオブジェクトは使わない — app/models/ 以下に名前空間を切ったドメインモデルを使う
  • カスタムコントローラーアクションは作らない — 代わりに新しいリソースを作る(REST マッピング)
  • RSpec なし、factory_bot なし — Minitest とフィクスチャのみ
  • Redis なし — Solid Queue、Solid Cache、Solid Cable
  • ビューで strftime は使わない — initializer でカスタム DATE_FORMATS を定義
  • コールバックは派生データと非同期ディスパッチのみ — ビジネスロジックには決して使わない
  • 強い保証にはモデルのバリデーションよりデータベース制約
  • ジョブにはオブジェクトではなく ID を渡す

規約クイックリファレンス

命名
  • 状態変化には動詞: card.close、board.publish
  • レコードの存在に基づく述語: card.closed?、card.golden?
  • Concern は形容詞: Closeable、Publishable、Watchable
  • コントローラー は名詞: Cards::ClosuresController
  • スコープ はビジネス用語: scope :active、scope :chronologically
REST マッピング

カスタムアクションはなし。サブリソースを作成します:

アクションルート
カードを閉じるPOST /cards/:id/closure
カードをアーカイブするPOST /cards/:id/archival
ボードをウォッチするPOST /boards/:id/watching
モデル — 状態をレコードとして

ブール値の代わりに、状態レコードを作成します(タイムスタンプと作成者が自動で付きます):

class Card::Closure < ApplicationRecord
  belongs_to :card
  belongs_to :creator, class_name: "User"
end

module Closeable
  extend ActiveSupport::Concern

  included do
    has_one :closure, dependent: :destroy
  end

  def closed? = closure.present?
  def close(creator: Current.user) = create_closure!(creator: creator)
  def reopen = closure&.destroy
end

# Querying
Card.joins(:closure)          # closed
Card.where.missing(:closure)  # open
Concern — 横断的な振る舞い

自己完結型(アソシエーション + スコープ + メソッド)。50〜150 行。組織ではなく 機能にちなんで命名します。

class Card < ApplicationRecord
  include Assignable, Closeable, Golden, Watchable, Searchable
end
モデル名前空間下の PORO

Concern に収まらないビジネスロジック:

# app/models/event/description.rb
class Event::Description
  def initialize(event) = @event = event
  def to_s = # ...
end
バリデーション

モデルには最小限(データ整合性)、フォームオブジェクトには文脈依存(UI フロー):

# Model — minimal
class User < ApplicationRecord
  validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
  normalizes :email, with: ->(e) { e.strip.downcase }
end

# Migration — hard constraints
add_index :users, :email, unique: true
add_foreign_key :cards, :boards
アソシエーション設計
使わない優先する使うとき
has_and_belongs_to_manyhas_many :throughジョインモデルに属性やコールバックが必要な場合
1:NM:N (has_many :through)関係が拡大する可能性がある場合
polymorphic: truedelegated_type異なるスキーマを持つ型安全なバリアント

has_many/has_one には常に :dependent を設定します。Rails が推論できない場合は :inverse_of を使います。

Rails 7.1+ のパターン
# Delegated types
class Message < ApplicationRecord
  delegated_type :messageable, types: %w[Comment Reply Announcement]
end

# store_accessor for JSON/JSONB
class User < ApplicationRecord
  store_accessor :settings, :theme, :notifications_enabled
end
データベース
# UUIDs (UUIDv7 — time-sortable)
create_table :cards, id: :uuid do |t|
  t.references :board, type: :uuid, foreign_key: true
end

# Counter caches
class Comment < ApplicationRecord
  belongs_to :card, counter_cache: true
end

# Default values with Current
class Card < ApplicationRecord
  belongs_to :creator, class_name: "User", default: -> { Current.user }
end
コントローラー

薄く。共有の振る舞いには Concern を使います:

module CardScoped
  extend ActiveSupport::Concern
  included { before_action :set_card }

  private
    def set_card
      @card  = Card.find(params[:card_id])
      @board = @card.board
    end
    def render_card_replacement
      render turbo_stream: turbo_stream.replace(@card)
    end
end

class Cards::ClosuresController < ApplicationController
  include CardScoped
  def create  = @card.close   && render_card_replacement
  def destroy = @card.reopen  && render_card_replacement
end
Current Attributes
class Current < ActiveSupport::CurrentAttributes
  attribute :session, :user, :account, :request_id
  delegate :user, to: :session, allow_nil: true
end
バックグラウンドジョブ

薄いラッパー — ロジックはモデルに置きます:

module Watchable
  def notify_watchers_later = NotifyWatchersJob.perform_later(self)
  def notify_watchers
    watchers.each { |w| WatcherMailer.notification(w, self).deliver_later }
  end
end

class NotifyWatchersJob < ApplicationJob
  def perform(card) = card.notify_watchers
end
ビューヘルパー

日付フォーマットは initializer 経由(ビューで strftime は決して使わない):

# config/initializers/date_formats.rb
Date::DATE_FORMATS[:default]    = "%d.%m.%Y"
Time::DATE_FORMATS[:default]    = "%d.%m.%Y %H:%M"
Time::DATE_FORMATS[:time_only]  = "%H:%M"

ナビゲーションのアクティブ状態には active_link_to gem を使います。

テスト
  • Minitest と フィクスチャ、コントローラーには統合テスト
  • 実装ではなく観測可能な振る舞いをテストする
  • 実際にテストできるものはモックしない
  • 外部 API には VCR
推奨スタック
関心事Gem
フロントエンドturbo-rails、stimulus-rails、importmap-rails
アセットpropshaft
ジョブSolid Queue
キャッシュ/CableSolid Cache、Solid Cable
認可Pundit
デプロイKamal + Thruster
Gem の選定
  1. 素の Rails でできるか?
  2. アプリのコアな関心事か? はいならコードを自分で持つ
  3. インフラを追加するか? データベースベースの代替がある
  4. 複雑さに見合う価値があるか?
  5. 信頼できる人が作ったものか?

詳細リファレンスファイル

タスクでより深いガイダンスが必要な場合は、必要に応じてこれらを参照してください。

フロントエンドのパターン(Stimulus コントローラー、Turbo Frames/Streams)については、このスキルと併せて該当する hwc-* スキルを呼び出してください。

コメント