Skip to main content

rails

Use when building or maintaining a Ruby on Rails app — models, controllers, views, routes and migrations; ActiveRecord associations, scopes and query performance; Hotwire (Turbo and Stimulus) UI; Active Job or Solid Queue background work; and Rails tests. Covers N+1 queries, migration safety on live tables, and flaky system tests around Turbo Frames. NOT plain Ruby scripts, gems or CLIs with no Rails loaded (that is `ruby`).

ソース情報

リポジトリ
ericrisco/rsc-harness
ソースの最終更新活動
2026年7月29日 23:26
検出された SKILL.md の言語
英語
スター
142
フォーク
11

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

ファイルエクスプローラー
7 ファイル

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
rails
description
Use when building or maintaining a Ruby on Rails app — models, controllers, views, routes and migrations; ActiveRecord associations, scopes and query performance; Hotwire (Turbo and Stimulus) UI; Active Job or Solid Queue background work; and Rails tests. Covers N+1 queries, migration safety on live tables, and flaky system tests around Turbo Frames. NOT plain Ruby scripts, gems or CLIs with no Rails loaded (that is `ruby`).
tags
["rails","activerecord","hotwire","turbo","stimulus","solid-queue","minitest"]
recommends
["ruby","postgresdb","testing-web","deployment","db-migrations"]
profiles
[]
origin
risco
# Build Rails the omakase way Write Rails the way a core-adjacent senior would: fat-enough models, skinny controllers, zero N+1, strong parameters, migrations that are reversible and safe on a live table, and tests that exercise the request rather than the implementation. Lean into the Rails 8 "omakase" defaults instead of fighting them — the framework already made the boring choices for you. This is **Rails the framework**: the MVC request cycle, ActiveRecord, Hotwire, Active Job, the Rails test stack. The moment code stops loading Rails — a plain script, a gem, an `Enumerable` refactor with no `ActiveRecord` in sight — it is no longer this skill. That is `ruby` (see `../ruby/SKILL.md`). ## Target versions ```text Rails 8.1 (stable line, released 2025-10-22; 8.0 shipped 2024-11-07) Ruby 3.4+ (floor for current Rails) Jobs/queue Solid Queue (default Active Job backend — database-backed, no Redis) Cache Solid Cache (default cache store — database-backed) WebSockets Solid Cable (default Action Cable adapter — database-backed) Assets Propshaft (default; Sprockets is legacy) JS Importmaps (default; no Node build step required) Deploy Kamal 2 + Thruster Front-end Hotwire = Turbo 8 + Stimulus Auth `rails generate authentication` (built in; no Devise for basic auth) ``` A fresh Rails 8 app needs **no Redis and no Node**. Stop reaching for Sidekiq/Redis or a JS bundler by reflex — the Solid trifecta and Importmaps already cover the default case. Rails 8.1 adds Active Job Continuations (resumable long jobs), Structured Event Reporting, local CI via `config/ci.rb`, and Markdown rendering. ## When to use - Generating/editing anything under `app/` (models, controllers, views, jobs, mailers, channels), `config/routes.rb`, or `db/migrate/`. - ActiveRecord work: associations, scopes, validations, callbacks, `includes`/`preload`/ `eager_load`, query objects, migrations. - Hotwire UI: Turbo Frames/Streams, morphing, Stimulus controllers, broadcasts. - Background work via Active Job / Solid Queue; fragment caching via Solid Cache. - Rails testing: Minitest model/integration/system tests, fixtures, RSpec + Capybara, debugging flaky Turbo system tests. - Upgrading a Rails app or migrating off Redis/Sidekiq onto the Solid stack. ## When NOT to use - Plain Ruby with no Rails loaded — scripts, gems, CLIs, Bundler packaging → `ruby`. - PHP/Laravel → `laravel`. Python/Django → `django`. (Route by the language/framework the file actually uses.) - Pure engine-level schema/index/locking depth not expressed as an AR migration → `postgresdb` / `mysql` / `db-migrations`. - Host/container/CI provisioning (Kamal host setup, Docker, runners) → `deployment` / `docker` / `github-actions`. This skill writes the Rails-side glue, then hands off. ## The request cycle — where logic lives Route → controller → model → view. Keep each layer doing one job. - **Controllers stay skinny.** A controller action finds/builds objects, calls one domain method, and renders/redirects. Why: a controller fat with business rules is untestable except through the full HTTP stack and impossible to reuse. - **Domain logic lives on the model.** Validations, scopes, state transitions, and computed attributes belong on the AR class. Why: that is where the data and its invariants already are. - **Extract a PORO/service object only when** a method spans 2+ models or does external IO (payment, email, third-party API). Don't pre-extract — a one-model method is just a model method. Why: premature service objects scatter logic that AR would hold fine. ```ruby # Good: skinny controller, domain logic on the model. class PostsController < ApplicationController def create @post = Current.user.posts.build(post_params) if @post.publish # model method owns the rule redirect_to @post, notice: "Published" else render :new, status: :unprocessable_entity end end private def post_params params.expect(post: [:title, :body]) # Rails 8 strong params end end ``` ## ActiveRecord Associations, validations, and scopes are the vocabulary — use them before writing raw SQL or manual loops. ```ruby class Post < ApplicationRecord belongs_to :author, class_name: "User" has_many :comments, dependent: :destroy validates :title, presence: true, length: { maximum: 120 } scope :published, -> { where.not(published_at: nil) } def publish update(published_at: Time.current) end end ``` **Kill N+1 with eager loading.** The classic footgun is touching an association inside a loop, firing one query per row. ```ruby # Bad: 1 query for posts + 1 per post for its author (N+1). Post.published.each { |p| puts p.author.name } # Good: 2 queries total — posts, then all authors in one IN(...). Post.published.includes(:author).each { |p| puts p.author.name } ``` Gate it: add the `bullet` gem in development, or mark a hot association `strict_loading` so an accidental lazy load raises instead of silently degrading. **Migrations must be reversible and safe on a live table.** A migration that locks a large table blocks every reader and writer for its duration. ```ruby # Good: a Postgres index built without an exclusive table lock. class AddIndexToPostsOnAuthor < ActiveRecord::Migration[8.1] disable_ddl_transaction! # required — concurrent index can't run in a txn def change add_index :posts, :author_id, algorithm: :concurrently end end ``` Rules: never `add_column ..., default:` on a huge legacy table without checking your Rails/Postgres version handles it without a full rewrite; backfill data in batches with `find_each` or `update_all`, not one giant `UPDATE` in the migration; add the `strong_migrations` gem so unsafe operations fail in review, not in production. Deeper AR — query objects, `strict_loading`, counter caches, composite primary keys, advisory locks, per-operation safe-migration recipes (add column, rename, backfill in batches) — is in `references/activerecord.md`. ## Hotwire Hotwire is the default front-end: Turbo for navigation/updates, Stimulus for behavior. Reach for the lightest tool that does the job. | Need | Use | Why | |------|-----|-----| | Faster full-page navigation, no code | Turbo Drive (on by default) | Free; intercepts links/forms | | Replace one bounded region after an action | Turbo **Frame** | Scoped DOM swap, no JS | | Update several elements / push from server | Turbo **Stream** | Targeted append/replace/remove + broadcasts | | Re-render a whole page and keep scroll/focus | **Morphing** (Turbo 8) | Diffs the DOM; kills most custom Stream choreography | | Client behavior (toggle, copy, debounce) | Stimulus controller | Sprinkle, not a SPA | Default to Turbo Drive and morphing; only hand-write a Turbo Stream when you need to touch elements that morphing can't infer (e.g. prepend to a list from a broadcast). ```javascript // app/javascript/controllers/clipboard_controller.js import { Controller } from "@hotwired/stimulus" export default class extends Controller { static targets = ["source"] static values = { successText: { type: String, default: "Copied!" } } copy() { navigator.clipboard.writeText(this.sourceTarget.value) } } ``` Worked Frame/Stream/morph examples, model broadcasting (`broadcasts_to`), Stimulus values/targets/outlets, and how to system-test Turbo are in `references/hotwire.md`. ## Background jobs & caching Use Active Job over **Solid Queue** (the Rails 8 default). Two non-negotiable rules: - **Jobs take serializable arguments — pass IDs, not records.** Why: a serialized AR object goes stale between enqueue and run; an ID is re-fetched fresh. - **Jobs must be idempotent.** Why: retries and at-least-once delivery mean a job can run twice; design so the second run is a no-op. ```ruby class CommentNotificationJob < ApplicationJob queue_as :default def perform(comment_id) comment = Comment.find_by(id: comment_id) return unless comment # idempotent: nothing to do if it's gone CommentMailer.posted(comment).deliver_now end end ``` For genuinely long work (large backfills, bulk exports) use **Active Job Continuations** (Rails 8.1) so a job can checkpoint and resume instead of restarting from zero. Cache expensive view fragments with the `cache` helper backed by Solid Cache: ```erb <% cache @post do %> <%= render @post.comments %> <% end %> ``` Do **not** add Sidekiq/Redis to a Rails 8 app unless a real, stated throughput or latency requirement justifies the extra moving part. Solid Queue covers the default case on the database you already run. ## Testing Minitest + fixtures is the framework default; parallel execution is on by default. RSpec-rails (8.x) is the popular alternative. Pick one and exercise behavior through the request, never private methods. | Test type | Base class | Tests | |-----------|-----------|-------| | Model / unit | `ActiveSupport::TestCase` | validations, scopes, domain methods | | Integration | `ActionDispatch::IntegrationTest` | a request → response, status, redirects | | System (browser) | `ApplicationSystemTestCase` | end-to-end UI incl. Turbo/Stimulus | | Job | `ActiveJob::TestCase` | enqueue + `perform` behavior | Fixtures (default) are fast and load once; reach for factories (FactoryBot) only when per-test object variation gets unwieldy. **Flaky Turbo system test?** It's almost always a timing race: never `sleep`. Use Capybara's auto-waiting matchers (`assert_selector`, `assert_text`, `assert_no_selector`) which retry until the DOM settles, and swap the default Selenium driver for **Cuprite or Playwright** to cut Turbo-timing flake. Minitest vs RSpec layout, fixtures gotchas, driver config, parallelization, and request-spec / broadcast-testing patterns are in `references/testing.md`. ## Anti-patterns | Anti-pattern | Why it bites | Do instead | |--------------|--------------|------------| | Business logic in the controller | Untestable except through HTTP; not reusable | Push it onto the model; service object only for cross-model/IO | | Callback soup (`after_save` chains for business rules) | Hidden order-dependent side effects, brutal to test | Explicit method called from the action; reserve callbacks for data hygiene | | Touching an association in a loop | N+1 — one query per row, silent in dev | `includes`/`preload`/`eager_load`; gate with `bullet`/`strict_loading` | | Blocking DDL on a big live table | Locks readers + writers for the whole migration | `disable_ddl_transaction!` + `algorithm: :concurrently`; `strong_migrations` | | Passing AR records into jobs | Stale serialized state; fails on retry | Pass IDs, re-`find` inside `perform` | | Adding Sidekiq/Redis to a Rails 8 app by reflex | A new daemon + dependency for nothing | Solid Queue is the default; add Sidekiq only for a stated need | | Queries or business logic in views | N+1 + logic you can't test | Prepare in controller/model; views only render | | Testing private methods | Locks tests to internals; refactors break green tests | Test observable behavior through the request | | Hand-rolled `fetch`/JS for live updates | Reinvents what Turbo gives free | Turbo Stream or morphing | ## Verify Run `scripts/verify.sh` inside a Rails app for a read-only static gate: it runs RuboCop (if rubocop-rails is configured) and `bin/rails zeitwerk:check`, and greps for the statically-detectable anti-patterns above (Sidekiq/Redis in a default app, `.all.each` mass loads, a concurrent `add_index` missing `disable_ddl_transaction!`). It skips any check whose tooling is absent and exits non-zero only on a hard violation.
GitHubで見る