Back to Skills

vanilla-rails-controllers

verified

Use when writing Rails controllers, adding controller actions, or implementing state changes (close, archive, publish, assign) - enforces resource extraction instead of custom actions

View on GitHub

Marketplace

zemptime-marketplace

ZempTime/zemptime-marketplace

Plugin

vanilla-rails

Repository

ZempTime/zemptime-marketplace
1stars

vanilla-rails/skills/controllers/SKILL.md

Last Verified

January 20, 2026

Install Skill

Select agents to install to:

Scope:
npx add-skill https://github.com/ZempTime/zemptime-marketplace/blob/main/vanilla-rails/skills/controllers/SKILL.md -a claude-code --skill vanilla-rails-controllers

Installation paths:

Claude
.claude/skills/vanilla-rails-controllers/
Powered by add-skill CLI

Instructions

# Vanilla Rails Controllers

**Core principle:** State changes are resources. Model state changes with CRUD operations on resource controllers, never custom actions.

## When to Use

Use this skill when:
- Adding any state change to a model (close, archive, publish, assign, follow, etc.)
- Creating new controller actions
- Routing state transitions

**Red flags - STOP and extract a resource:**
- Adding `post :close`, `post :archive`, `patch :activate`
- Adding custom actions to existing resource routes
- Thinking "it's just a boolean toggle"
- Time pressure rationalizing "can refactor later"

## Resource Extraction Pattern

**The 37signals pattern:** Every state change becomes its own resource controller.

```ruby
# ❌ BAD - custom actions (typical Rails tutorials)
resources :cards do
  post :close
  post :reopen
  post :archive
  post :unarchive
end

# ✅ GOOD - state as resource (37signals pattern)
resources :cards do
  resource :closure, only: [:create, :destroy]
  resource :archival, only: [:create, :destroy]
end
```

**Why singular `resource`?** Each card has at most ONE closure state, ONE archival state. Singular resource = no ID in URL.

**Why `only: [:create, :destroy]`?** Creating resource = entering state. Destroying resource = leaving state.

## Thin Controllers Calling Model Methods

Controllers delegate to intention-revealing model API. Keep business logic in models.

```ruby
# ❌ BAD - ActiveRecord calls in controller
class Cards::ArchivalsController < ApplicationController
  def create
    @card = Card.find(params[:id])
    @card.update(archived: true)  # Business logic in controller
    redirect_to board_cards_path(@card.board)
  end
end

# ✅ GOOD - delegate to model
class Cards::ArchivalsController < ApplicationController
  include CardScoped  # Sets @card from params

  def create
    @card.archive  # Intention-revealing model method

    respond_to do |format|
      format.turbo_stream
      format.json { head :no_content }
    end
  end

  def destr

Validation Details

Front Matter
Required Fields
Valid Name Format
Valid Description
Has Sections
Allowed Tools
Instruction Length:
7425 chars

Issues Found:

  • name_directory_mismatch