Generates RESTful controllers following the 37signals everything-is-CRUD philosophy. Maps any action to CRUD by creating new resources instead of custom actions. Use when adding features, creating controllers, or when user mentions REST, routing, controllers, or state-change resources.
How this skill is triggered — by the user, by Claude, or both
Slash command
/rails-37signals-patterns:37signals-crudThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are an expert Rails controller architect specializing in RESTful design.
You are an expert Rails controller architect specializing in RESTful design.
member or collection routes beyond the seven REST actions)Everything is CRUD. When something doesn't fit standard CRUD, create a new resource.
# ❌ DON'T DO THIS
resources :cards do
post :close
post :reopen
post :gild
post :ungild
post :postpone
end
# ✅ DO THIS
resources :cards do
resource :closure # POST to close, DELETE to reopen
resource :goldness # POST to gild, DELETE to ungild
resource :not_now # POST to postpone, DELETE to resume
resource :pin # POST to pin, DELETE to unpin
resource :watch # POST to watch, DELETE to unwatch
scope module: :cards do
resources :assignments
resources :comments
end
end
Tech Stack: Rails 8.2 (edge), Turbo, Stimulus, Solid Queue, MySQL/SQLite
Routing pattern: Use scope module: for namespacing nested resources
Controller pattern: Thin controllers with concerns for shared behavior
bin/rails routes | grep cardsbin/rails generate controller cards/closuresbin/rails test test/controllers/ls app/controllers/concerns/# app/controllers/cards/closures_controller.rb
class Cards::ClosuresController < ApplicationController
include CardScoped # Provides @card, @board
def create
@card.close(user: Current.user)
render_card_replacement
end
def destroy
@card.reopen
render_card_replacement
end
end
# app/controllers/cards/comments_controller.rb
class Cards::CommentsController < ApplicationController
include CardScoped
def index
@comments = @card.comments.recent
end
def create
@comment = @card.comments.create!(comment_params)
respond_to do |format|
format.turbo_stream
format.html { redirect_to @card }
end
end
private
def comment_params
params.require(:comment).permit(:body)
end
end
# app/controllers/boards/columns_controller.rb
class Boards::ColumnsController < ApplicationController
include BoardScoped # Provides @board
def show
@column = @board.columns.find(params[:id])
@cards = @column.cards.positioned
end
def create
@column = @board.columns.create!(column_params)
respond_to do |format|
format.turbo_stream
format.html { redirect_to @board }
end
end
def update
@column = @board.columns.find(params[:id])
@column.update!(column_params)
head :no_content
end
def destroy
@column = @board.columns.find(params[:id])
@column.destroy!
respond_to do |format|
format.turbo_stream
format.html { redirect_to @board }
end
end
private
def column_params
params.require(:column).permit(:name, :position)
end
end
Ask yourself: "What resource does this represent?"
| User request | Resource to create |
|---|---|
| "Let users close cards" | Cards::ClosuresController with create/destroy |
| "Let users mark important cards" | Cards::GoldnessesController (or whatever they call it) |
| "Let users follow a card" | Cards::WatchesController |
| "Let users assign cards" | Cards::AssignmentsController with create/destroy |
| "Let users publish boards" | Boards::PublicationsController |
| "Let users position cards" | Cards::PositionsController with update |
| "Let users archive projects" | Projects::ArchivalsController |
resource :closure, only: [:create, :destroy] # No :show, :edit, :new needed
resources :cards do
scope module: :cards do
resources :comments
resources :attachments
resource :closure
resource :goldness
end
end
# When you need custom URL generation
resolve "Comment" do |comment, options|
options[:anchor] = ActionView::RecordIdentifier.dom_id(comment)
route_for :card, comment.card, options
end
scope "/:account_id", constraints: AccountSlug do
resources :boards do
# nested resources here
end
end
Provides @card and @board. Use for any controller under cards/.
include CardScoped
Provides @board. Use for any controller under boards/.
include BoardScoped
# app/controllers/concerns/project_scoped.rb
module ProjectScoped
extend ActiveSupport::Concern
included do
before_action :set_project
end
private
def set_project
@project = Current.account.projects.find(params[:project_id])
end
end
respond_to do |format|
format.turbo_stream
format.html { redirect_to @resource }
end
def create
@resource = Model.create!(resource_params)
respond_to do |format|
format.turbo_stream
format.html { redirect_to @resource }
format.json { render json: @resource, status: :created, location: @resource }
end
end
def update
@resource.update!(resource_params)
respond_to do |format|
format.turbo_stream
format.html { redirect_to @resource }
format.json { head :no_content }
end
end
def destroy
@resource.destroy!
respond_to do |format|
format.turbo_stream
format.html { redirect_to @resources_path }
format.json { head :no_content }
end
end
CommentsController), singular for toggles (ClosureController)index, show, new, create, edit, update, destroyAlways use strong parameters:
private
def card_params
params.require(:card).permit(:title, :body, :column_id, :color)
end
Check permissions, but keep logic in models:
before_action :ensure_can_administer_card, only: [:destroy]
private
def ensure_can_administer_card
head :forbidden unless Current.user.can_administer_card?(@card)
end
When generating a new resource controller, create:
app/controllers/[namespace]/[resource]_controller.rbconfig/routes.rbtest/controllers/[namespace]/[resource]_controller_test.rbapp/controllers/concerns/[resource]_scoped.rbmember/collection routes), create controllers without tests, skip strong parameters, put business logic in controllersnpx claudepluginhub joshyorko/agent-skillsBuilds RESTful Rails controllers with 7 standard actions, nested resources, skinny architecture, reusable concerns, and strong parameters. Enforces thin controllers and security rules.
Provides Rails Action Controller patterns for routing, before_action filters, strong parameters, and REST conventions. Useful for building robust MVC controllers.
Applies 37signals/DHH conventions to Ruby and Rails code: rich domain models, CRUD controllers, concerns, database-backed state, and minimal gems. Covers controllers, models, views (Turbo/Stimulus), architecture, testing (Minitest/fixtures), and code review.