关于 jr-rails-classic
jr-rails-classic 是面向 Rails 开发者的编码风格技能,依据 37signals 惯例指导应用实现。它强调富领域模型、REST 资源设计,以及使用 Minitest 和 fixtures 测试。
主要功能
- 01按生成器优先的开发流程
- 02富领域模型与薄控制器
- 03以子资源映射状态变更
- 04Minitest 与 fixtures 测试
- 05数据库约束与作业设计建议
适用场景
- 01按 37signals 惯例构建 Rails 功能
- 02规划 REST 控制器与数据模型
- 03为 Rails 应用设计测试和后台作业
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ julianrubisch/ skills --skill jr-rails-classic安装后在 Claude Code 中输入/jr-rails-classic确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
reference/agentic-worktrees.mdanti-patterns.mdcoding-classic.mdcoding-phlex.mddevops.mdpatterns.mdreek.ymlreference/coding/api.mdreference/refactorings/000-template.md001-extract-scope-from-controller.md002-replace-conditional-with-polymorphism.md003-replace-conditional-with-null-object.md004-introduce-form-object.md005-replace-subclasses-with-strategies.md006-replace-mixin-with-composition.md007-extract-validator.md008-introduce-parameter-object.md009-replace-callback-with-method.md010-refactor-service-object-into-poro.mdextraction-signals.mdreference/shared/architecture.mdauthorization.mdcallbacks.mdcomponents.mdconcerns.mdconfiguration.mdcurrent_attributes.mdgraphql.mdhotwire.mdinstrumentation.md详细介绍
Rails 经典编码风格
按照 37signals 的约定编写 Rails 应用代码。丰富的领域模型、CRUD 控制器、所有内容都由数据库支撑,使用 Minitest 和 fixtures。
核心工作流
- 先使用生成器 —
rails g model、rails g controller、rails g migration。生成器一次性生成正确的文件结构、测试桩 和路由条目。 - 实现顺序 — 模型 → 控制器 → 视图 → 测试。
- 发布、验证、改进 — 从原型走向生产环境,从真实使用中学习。
- 让它崩溃 — 使用带 bang 的方法,让 Rails 通过 422 响应处理 RecordInvalid。
防护原则
- 不使用 service object — 使用命名空间位于
app/models/下的领域模型 - 不添加自定义控制器操作 — 改为创建新资源(REST 映射)
- 不使用 RSpec 或 factory_bot — 仅使用 Minitest 和 fixtures
- 不使用 Redis — 使用 Solid Queue、Solid Cache、Solid Cable
- 视图中不使用
strftime— 在初始化文件中自定义 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_many | has_many :through | 联结模型需要属性或回调 |
| 1:N | M:N(has_many :through) | 关系可能扩展 |
polymorphic: true | delegated_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
当前属性
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
视图辅助方法
通过初始化文件格式化日期(视图中绝不使用 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 和 fixtures,控制器使用集成测试
- 测试可观察到的行为,而非实现细节
- 能实际测试的内容就不要模拟
- 外部 API 使用 VCR
首选技术栈
| 关注点 | Gem |
|---|---|
| 前端 | turbo-rails、stimulus-rails、importmap-rails |
| 资源 | propshaft |
| 任务 | Solid Queue |
| 缓存/线缆 | Solid Cache、Solid Cable |
| 授权 | Pundit |
| 部署 | Kamal + Thruster |
Gem 选择
- 原生 Rails 能做到吗?
- 这是应用的核心关注点吗?如果是,就自己维护代码
- 它会增加基础设施吗?是否存在由数据库支撑的替代方案?
- 复杂度值得吗?
- 它来自你信任的人吗?
深入参考文件
当任务需要更深入的指导时,请按需阅读以下文件:
- 设计模式(表单对象、查询对象、策略等):reference/patterns.md
- 反模式和代码异味:reference/anti-patterns.md、reference/smells.md
- 重构方案:reference/refactorings/(002–010)
- 测试指南:reference/shared/testing.md
- Hotwire(Turbo + Stimulus):reference/shared/hotwire.md
- 后台任务 + Continuations:reference/shared/jobs.md
- 状态机(AASM):reference/shared/state_machines.md
- 回调评分和提取:reference/shared/callbacks.md
- 授权(Pundit):reference/shared/authorization.md
- 通知(Noticed):reference/shared/notifications.md
- 检测 / EventReporter:reference/shared/instrumentation.md
- 组件(Phlex):reference/shared/components.md
- 序列化器(API JSON):reference/shared/serializers.md
- 架构分层:reference/shared/architecture.md
- Gem 推荐:reference/toolbelt.md
对于前端模式(Stimulus 控制器、Turbo Frames/Streams),请同时调用相关的 hwc-* skill。
评论
支持 Markdown