agentleFS
dilolabsdilolabs/nosia/.claude/skills/crud-patterns/SKILL.md

crud-patterns

Skill211 starsChanged 5 months agoMITView on GitHub

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).

The file

We split this skill into the rules it gives an agent

7 rules. About 0 are common, 5 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈5

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Routing: Use scope module: for namespacing nested resources

    line 20
  2. Distinctive:

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

    line 32
  3. Distinctive:

    Create new scoping concerns as needed:

    line 207
  4. Distinctive:

    Keep authorization checks thin, delegate logic to models:

    line 253
  5. Very specific:

    Route entry: Add to config/routes.rb

    line 268
  6. Distinctive:

    Ask first: Before adding custom actions, before creating non-REST routes, before modifying routing constraints

    line 275
  7. Distinctive:

    Never: Add member/collection routes, create controllers without tests, put business logic in controllers

    line 276

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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 requestResource 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

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.