
Ce Dhh Rails Style
Scaffold Rails apps with DHH-style CRUD routing, singular resources, shallow nesting, and path-based multi-tenancy instead of generic REST controllers.
Overview
ce-dhh-rails-style is an agent skill most often used in Build (also Ship, Operate) that encodes DHH Rails routing, resource naming, and path-based multi-tenancy patterns for CRUD-heavy SaaS backends.
Install
npx skills add https://github.com/everyinc/compound-engineering-plugin --skill ce-dhh-rails-styleWhat is this skill?
- Maps every action to CRUD with nested resources and verb-to-noun singular resources (closure, goldness, archival)
- Shallow nesting and singular resource conventions to keep URLs shallow
- Path-prefix multi-tenancy via Rack middleware and SCRIPT_NAME/PATH_INFO rewriting
- Route resolve blocks for correct url_for on nested models like comments
Adoption & trust: 1.5k installs on skills.sh; 20.5k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your agent keeps inventing non-idiomatic Rails routes, deep nesting, or tenant hacks that do not match how 37signals-style apps are structured.
Who is it for?
Solo builders on Ruby on Rails shipping multi-tenant or board-style SaaS who want agents to mirror 37signals routing and tenancy idioms.
Skip if: Teams on non-Rails stacks, GraphQL-only APIs without REST resources, or greenfield apps that explicitly reject CRUD-centric Rails routing.
When should I use this skill?
Designing or refactoring Rails routes, multi-tenant URL strategy, or CRUD resource naming for a 37signals-style app.
What do I get? / Deliverables
After the skill runs, route files, tenancy middleware, and resource naming follow DHH conventions so CRUD and Hotwire-friendly URLs stay consistent across features.
- Route draw patterns
- Tenant middleware sketch
- Verb-to-resource naming table
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Canonical shelf is Build/backend because the skill encodes Rails architecture patterns—routes, tenancy middleware, and verb-to-noun resource modeling—for implementation work. Backend subphase fits routing tables, tenant extractors, cookie scoping, and RESTful resource design that ship in the server layer.
Where it fits
Define boards/cards routes with singular closure and goldness resources before implementing controllers.
Audit a PR for deep nesting or wrong plural resources against DHH routing tables.
Add a new tenant-scoped feature without breaking path-prefix tenancy and cookie scoping.
How it compares
Use instead of generic “Rails best practices” chat that ignores singular resources, shallow nesting, and path-based tenancy.
Common Questions / FAQ
Who is ce-dhh-rails-style for?
Rails solo and indie developers who want agent-generated code to match DHH/37signals architecture for routing and multi-tenancy.
When should I use ce-dhh-rails-style?
During Build when designing routes and tenancy; in Ship when reviewing REST surface area; in Operate when extending tenant-scoped features without breaking URL conventions.
Is ce-dhh-rails-style safe to install?
It is procedural documentation only—review the Security Audits panel on this Prism page before installing from the source repo.
SKILL.md
READMESKILL.md - Ce Dhh Rails Style
# Architecture - DHH Rails Style <routing> ## Routing Everything maps to CRUD. Nested resources for related actions: ```ruby Rails.application.routes.draw do resources :boards do resources :cards do resource :closure resource :goldness resource :not_now resources :assignments resources :comments end end end ``` **Verb-to-noun conversion:** | Action | Resource | |--------|----------| | close a card | `card.closure` | | watch a board | `board.watching` | | mark as golden | `card.goldness` | | archive a card | `card.archival` | **Shallow nesting** - avoid deep URLs: ```ruby resources :boards do resources :cards, shallow: true # /boards/:id/cards, but /cards/:id end ``` **Singular resources** for one-per-parent: ```ruby resource :closure # not resources resource :goldness ``` **Resolve for URL generation:** ```ruby # config/routes.rb resolve("Comment") { |comment| [comment.card, anchor: dom_id(comment)] } # Now url_for(@comment) works correctly ``` </routing> <multi_tenancy> ## Multi-Tenancy (Path-Based) **Middleware extracts tenant** from URL prefix: ```ruby # lib/tenant_extractor.rb class TenantExtractor def initialize(app) @app = app end def call(env) path = env["PATH_INFO"] if match = path.match(%r{^/(\d+)(/.*)?$}) env["SCRIPT_NAME"] = "/#{match[1]}" env["PATH_INFO"] = match[2] || "/" end @app.call(env) end end ``` **Cookie scoping** per tenant: ```ruby # Cookies scoped to tenant path cookies.signed[:session_id] = { value: session.id, path: "/#{Current.account.id}" } ``` **Background job context** - serialize tenant: ```ruby class ApplicationJob < ActiveJob::Base around_perform do |job, block| Current.set(account: job.arguments.first.account) { block.call } end end ``` **Recurring jobs** must iterate all tenants: ```ruby class DailyDigestJob < ApplicationJob def perform Account.find_each do |account| Current.set(account: account) do send_digest_for(account) end end end end ``` **Controller security** - always scope through tenant: ```ruby # Good - scoped through user's accessible records @card = Current.user.accessible_cards.find(params[:id]) # Avoid - direct lookup @card = Card.find(params[:id]) ``` </multi_tenancy> <authentication> ## Authentication Custom passwordless magic link auth (~150 lines total): ```ruby # app/models/session.rb class Session < ApplicationRecord belongs_to :user before_create { self.token = SecureRandom.urlsafe_base64(32) } end # app/models/magic_link.rb class MagicLink < ApplicationRecord belongs_to :user before_create do self.code = SecureRandom.random_number(100_000..999_999).to_s self.expires_at = 15.minutes.from_now end def expired? expires_at < Time.current end end ``` **Why not Devise:** - ~150 lines vs massive dependency - No password storage liability - Simpler UX for users - Full control over flow **Bearer token** for APIs: ```ruby module Authentication extend ActiveSupport::Concern included do before_action :authenticate end private def authenticate if bearer_token = request.headers["Authorization"]&.split(" ")&.last Current.session = Session.find_by(token: bearer_token) else Current.session = Session.find_by(id: cookies.signed[:session_id]) end redirect_to login_path unless Current.session end end ``` </authentication> <background_jobs> ## Background Jobs Jobs are shallow wrappers calling model methods: ```ruby class NotifyWatchersJob < ApplicationJob def perform(card) card.notify_watchers end end ``` **Naming convention:** - `_later` suffix for async: `card.notify_watchers_later` - `_now` suffix for immediate: `card.notify_watchers_now` ```ruby module Watchable def notify_watchers_later NotifyWatchersJob.perform_later(self) end def notify_watchers_now NotifyWatchersJob.perform_now(self) end def notify_watchers