Agent Skillsdilolabs/nosia › crud-patterns

crud-patterns

GitHub

提供Rails中RESTful控制器设计模式,指导将非标准操作映射为CRUD资源,涵盖状态变更、嵌套资源及标准增删改查实现。

.opencode/skills/crud-patterns/SKILL.md dilolabs/nosia

Trigger Scenarios

需要创建新的RESTful控制器 设计路由以处理状态变更 添加新功能需映射到资源

Install

npx skills add dilolabs/nosia --skill crud-patterns -g -y
More Options

Non-standard path

npx skills add https://github.com/dilolabs/nosia/tree/main/.opencode/skills/crud-patterns -g -y

Use without installing

npx skills use dilolabs/nosia@crud-patterns

指定 Agent (Claude Code)

npx skills add dilolabs/nosia --skill crud-patterns -a claude-code -g -y

安装 repo 全部 skill

npx skills add dilolabs/nosia --all -g -y

预览 repo 内 skill

npx skills add dilolabs/nosia --list

SKILL.md

Frontmatter
{
    "name": "crud-patterns",
    "license": "MIT",
    "description": "Generates RESTful controllers mapping any action to CRUD by creating new resources instead of custom actions. Use when adding features, creating controllers, designing routes, or handling state changes via REST. WHEN NOT: Non-REST APIs (use api-patterns), view\/template work (use turbo-patterns), or model business logic (use model-patterns).",
    "compatibility": "Ruby 3.3+, Rails 8.0+"
}

CRUD Patterns (37signals)

Map any action to CRUD. When something doesn't fit standard CRUD, create a new resource.

Project knowledge

Tech Stack: Rails 8.2 (edge), Turbo, Stimulus, Solid Queue, MySQL/SQLite Routing: Use scope module: for namespacing nested resources Controllers: Thin with concerns for shared behavior

Commands:

bin/rails routes | grep cards              # Check routes
bin/rails generate controller cards/closures  # Generate controller
bin/rails test test/controllers/           # Run controller tests

Resource thinking

When asked to add functionality, ask: "What resource does this represent?"

User request Resource to create
"Let users close cards" Cards::ClosuresController (create/destroy)
"Let users mark important" Cards::GoldnessesController (create/destroy)
"Let users follow a card" Cards::WatchesController (create/destroy)
"Let users assign cards" Cards::AssignmentsController (create/destroy)
"Let users publish boards" Boards::PublicationsController (create/destroy)
"Let users position cards" Cards::PositionsController (update)
"Let users archive projects" Projects::ArchivalsController (create/destroy)

State change controllers (singular resources)

Toggle state via POST (create) and DELETE (destroy) on a singular resource:

# app/controllers/cards/closures_controller.rb
class Cards::ClosuresController < ApplicationController
  include CardScoped  # Provides @card, @board

  def create
    @card.close
    render_card_replacement
  end

  def destroy
    @card.reopen
    render_card_replacement
  end
end

Standard CRUD controllers (plural resources)

# 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

Nested resource controllers

# app/controllers/boards/columns_controller.rb
class Boards::ColumnsController < ApplicationController
  include BoardScoped

  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

Routing patterns

Singular resource for toggles

resource :closure, only: [:create, :destroy]  # No :show, :edit, :new

Module scoping for organization

resources :cards do
  scope module: :cards do
    resources :comments
    resources :attachments
    resource :closure
    resource :goldness
  end
end

Polymorphic routes with resolve

resolve "Comment" do |comment, options|
  options[:anchor] = ActionView::RecordIdentifier.dom_id(comment)
  route_for :card, comment.card, options
end

Scoping concerns

Provide parent resource lookup for nested controllers:

# app/controllers/concerns/card_scoped.rb
module CardScoped
  extend ActiveSupport::Concern

  included do
    before_action :set_card
    before_action :set_board
  end

  private

  def set_card
    @card = Current.account.cards.find(params[:card_id])
  end

  def set_board
    @board = @card.board
  end

  def render_card_replacement
    respond_to do |format|
      format.turbo_stream do
        render turbo_stream: turbo_stream.replace(
          dom_id(@card, :card_container),
          partial: "cards/container",
          locals: { card: @card.reload }
        )
      end
      format.html { redirect_to @card }
    end
  end
end

Create new scoping concerns as needed:

# 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

Response patterns

Turbo Stream responses (preferred)

respond_to do |format|
  format.turbo_stream
  format.html { redirect_to @resource }
end

Multi-format responses

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

Authorization pattern

Keep authorization checks thin, delegate logic to 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

Files to create for a new resource

  1. Controller: app/controllers/[namespace]/[resource]_controller.rb
  2. Route entry: Add to config/routes.rb
  3. Test: test/controllers/[namespace]/[resource]_controller_test.rb
  4. Concern (if needed): app/controllers/concerns/[resource]_scoped.rb

Boundaries

  • Always: Map actions to CRUD, create new resources for state changes, use concerns for scoping, only 7 REST actions, use strong parameters
  • Ask first: Before adding custom actions, before creating non-REST routes, before modifying routing constraints
  • Never: Add member/collection routes, create controllers without tests, put business logic in controllers

Version History

  • edd76af Current 2026-08-20 14:24

Same Skill Collection

.claude/skills/api-patterns/SKILL.md
.claude/skills/auth-setup/SKILL.md
.claude/skills/caching-patterns/SKILL.md
.claude/skills/concern-patterns/SKILL.md
.claude/skills/crud-patterns/SKILL.md
.claude/skills/event-tracking/SKILL.md
.claude/skills/job-patterns/SKILL.md
.claude/skills/mailer-patterns/SKILL.md
.claude/skills/migration-patterns/SKILL.md
.claude/skills/model-patterns/SKILL.md
.claude/skills/multi-tenant-setup/SKILL.md
.claude/skills/state-records/SKILL.md
.claude/skills/stimulus-patterns/SKILL.md
.claude/skills/testing-patterns/SKILL.md
.claude/skills/turbo-patterns/SKILL.md
.opencode/skills/api-patterns/SKILL.md
.opencode/skills/auth-setup/SKILL.md
.opencode/skills/caching-patterns/SKILL.md
.opencode/skills/concern-patterns/SKILL.md
.opencode/skills/event-tracking/SKILL.md
.opencode/skills/job-patterns/SKILL.md
.opencode/skills/mailer-patterns/SKILL.md
.opencode/skills/migration-patterns/SKILL.md
.opencode/skills/model-patterns/SKILL.md
.opencode/skills/multi-tenant-setup/SKILL.md
.opencode/skills/state-records/SKILL.md
.opencode/skills/stimulus-patterns/SKILL.md
.opencode/skills/testing-patterns/SKILL.md
.opencode/skills/turbo-patterns/SKILL.md
.vibe/skills/api-patterns/SKILL.md
.vibe/skills/auth-setup/SKILL.md
.vibe/skills/caching-patterns/SKILL.md
.vibe/skills/concern-patterns/SKILL.md
.vibe/skills/crud-patterns/SKILL.md
.vibe/skills/event-tracking/SKILL.md
.vibe/skills/job-patterns/SKILL.md
.vibe/skills/mailer-patterns/SKILL.md
.vibe/skills/migration-patterns/SKILL.md
.vibe/skills/model-patterns/SKILL.md
.vibe/skills/multi-tenant-setup/SKILL.md
.vibe/skills/state-records/SKILL.md
.vibe/skills/stimulus-patterns/SKILL.md
.vibe/skills/testing-patterns/SKILL.md
.vibe/skills/turbo-patterns/SKILL.md

Metadata

Files
0
Version
edd76af
Hash
fdad7419
Indexed
2026-08-20 14:24

- 위키
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-25 06:23
浙ICP备14020137号-1 $방문자$