rails-conventions について
Rails アプリケーションの開発・保守向けに、コードとテストの規約をまとめています。シンプルでテストしやすい構成を重視し、コントローラー、サービスオブジェクト、Turbo Streams、JavaScript の読み込み、性能を扱います。
主な機能
- 01コントローラーとモデルの責務指針
- 02サービスオブジェクトの抽出ルール
- 03インライン Turbo Streams のパターン
- 04Minitest とフィクスチャによるテスト
- 05importmap による JavaScript 読み込み管理
- 06性能と命名の指針
活用シーン
- 01Rails のコントローラーと業務ロジックの整理
- 02サービスオブジェクトを抽出する判断
- 03フィクスチャと外部依存のスタブによる動作テスト
- 04公開用と管理用の JavaScript 依存関係の分離
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ majesticlabs-dev/ majestic-marketplace --skill rails-conventionsインストール後、Claude Code で/rails-conventionsを入力して読み込まれたか確認します。
詳細
Rails の規約
クリーンで保守しやすいコードを書くための、明確な方針に基づく Rails のパターンです。
基本理念
-
複雑さより重複を選ぶ:複雑な DRY 抽象化より、シンプルなコードの重複を選びます
- 「複雑なコントローラーが 3 つあるより、シンプルなコントローラーが 4 つあるほうがよいです」
-
テスト容易性 = 品質:テストが難しい場合は、構造をリファクタリングする必要があります
-
コントローラーを追加するのは問題ありません。コントローラーを複雑にするのは問題です。
Turbo Streams
シンプルな Turbo Streams は必ずコントローラー内に直接記述します。
# FAIL: Separate .turbo_stream.erb files for simple operations
render "posts/update"
# PASS: Inline array
render turbo_stream: [
turbo_stream.replace("post_#{@post.id}", partial: "posts/post", locals: { post: @post }),
turbo_stream.remove("flash")
]
コントローラーと Concerns
ビジネスロジックはコントローラーではなく、モデルまたは Concern に記述します。
# Concern structure
module Dispatchable
extend ActiveSupport::Concern
included do
scope :available, -> { where(status: "pending") }
end
class_methods do
def claim!(batch_size)
# class-level behavior
end
end
end
サービスへの切り出し
次の項目が複数当てはまる場合に切り出します:
- 複雑なビジネスルール(単に「長い」だけではない)
- 複数のモデルを連携させる
- 外部 API とのやり取り
- 複数のコントローラーで再利用するロジック
サービスの構成:
- 公開メソッドは 1 つ
- 責務ごとに名前空間を分ける(
Extraction::RegexExtractor) - コンストラクターで依存関係を受け取る
- ドメインオブジェクトではなく、データ構造を返す
モダンな Ruby のスタイル
# Hash shorthand
{ id:, slug:, doc_type: kind }
# Safe navigation
created_at&.iso8601
@setting ||= SlugSetting.active.find_by!(slug:)
# Keyword arguments
def extract(document_type:, subject:, filename:)
def process!(strategy: nil)
Enum のパターン
# Frozen arrays with validation
STATUSES = %w[processed needs_review].freeze
enum :status, STATUSES.index_by(&:itself), validate: true
スコープのパターン
# Guard with .present?, chainable design
scope :by_slug, ->(slug) { where(slug:) if slug.present? }
scope :from_date, ->(date) { where(created_at: Date.parse(date).beginning_of_day..) if date.present? }
def self.filtered(params)
all.by_slug(params[:slug]).by_kind(params[:kind])
rescue ArgumentError
all
end
エラーハンドリング
# Domain-specific errors
class InactiveSlug < StandardError; end
# Log with context, re-raise for upstream
def handle_exception!(error:)
log_error("Exception #{error.class}: #{error.message}", error:)
mark_failed!(error.message)
raise
end
テスト(Minitest + フィクスチャ)
test "describes expected behavior" do
email = emails(:two)
email.process
email.reload
assert_equal "finished", email.processing_status
end
原則:
- 振る舞い駆動:方法ではなく、何をするかをテストします
- フィクスチャベース:セットアップには
emails(:two)を使用します - 外部依存をモック化:S3、API、PDF をスタブします
- 状態を検証:操作後に
.reloadを実行します - ヘルパーメソッド:
build_valid_email、with_stubbed_download
命名(5 秒ルール)
5 秒で理解できない場合:
# FAIL
show_in_frame
process_stuff
# PASS
fact_check_modal
_fact_frame
JavaScript と Importmap
Rails 7+ では、JS の依存関係管理に Importmap を使用します。依存関係のスコープは、最も限定的なエントリーポイントにします。
複数のエントリーポイント
# config/importmap.rb
pin "application" # Public entrypoint
pin "@hotwired/turbo-rails", to: "turbo.min.js"
pin "@hotwired/stimulus", to: "stimulus.min.js"
# Admin-only dependencies - pinned but only imported in admin entrypoint
pin "chartkick", to: "chartkick.js"
pin "Chart.bundle", to: "Chart.bundle.js"
// app/javascript/application.js — public pages only
import "@hotwired/turbo-rails"
import "@hotwired/stimulus"
import "controllers"
// app/javascript/admin.js — imports public base + admin-only libs
import "application"
import "chartkick"
import "Chart.bundle"
<%# Admin layout %>
<%= javascript_importmap_tags("admin") %>
<%# All other layouts %>
<%= javascript_importmap_tags %>
新しい JS 依存関係の追加
- スコープを確認します:一般ユーザー向けか、管理者専用か?
config/importmap.rbで Pin します- 正しいエントリーポイントでインポートします
- 管理者専用ライブラリを
application.jsに追加してはいけません
パフォーマンス
- 規模が与える影響を考慮します
- 時期尚早なキャッシュは避けます
- KISS — シンプルに保ちます
- インデックスは書き込みを遅くするため、必要な場合にのみ追加します
コメント
Markdown 対応