| name | rails |
| description | Ruby on Rails with service-oriented architecture, Dry-validation, Sidekiq/Solid Queue, Hotwire. Use for Rails API, Rails services, Rails forms, RSpec, ActiveRecord, Rails migrations. Not for standalone Ruby gems (use ruby skill), and not for judging whether a migration is safe to run in production (that is the database-reviewer agent). |
| compatibility | Requires Bundler and Rails. Optional: lefthook, RSpec. |
| allowed-tools | ["mcp__acp__Read","mcp__acp__Edit","mcp__acp__Write","mcp__acp__Bash"] |
ABOUTME: Rails service-oriented architecture, validation contracts, background jobs, Hotwire
ABOUTME: API development with thin controllers, services, forms, filters
Ruby on Rails
Quick Reference
bundle exec lefthook run all
bundle exec rspec
rails s / bin/dev
bin/jobs
See also: _AST_GREP.md (sg patterns), _PATTERNS.md, source-control
Architecture calls:
MyService.new(user:, params:).call
MyForm.new(params, user).save
MyFilter.result(params, scope)
MyJob.perform_async(id)
MyJob.perform_later(id)
Version (determine, don't assume)
See ../_LANG_COMMON.md. Fetch the truth:
bundle exec rails -v
ruby -v && cat .ruby-version 2>/dev/null
curl -s https://rubygems.org/api/v1/gems/rails.json | jq -r .version
Pre-Commit Verification (MANDATORY)
make check && make test-e2e must pass (enforced by the pre-commit-gate hook; see ../_LANG_COMMON.md). What make check expands to for Rails:
bundle exec lefthook run all
bundle exec rspec
bundle exec brakeman --no-pager
bundle audit check --update
Sacred Rules (NON-NEGOTIABLE)
- NO LOGIC IN CONTROLLERS: HTTP layer only
- ALL LOGIC IN SERVICES/FORMS/FILTERS
- NO ACTIVERECORD VALIDATIONS: Dry-validation contracts only
- MINIMUM MODEL LOGIC: Data structures + associations
- NO MODEL CALLBACKS: Exception: attachment destruction
Pattern Summary
Service: Complex business logic, multi-step operations, transactions
MyService.new(user:, params:).call
Form: User input validation + persistence
MyForm.new(params, user).save
Contract: Validation rules (Dry-validation)
CreateContract.new.call(params)
Controller: HTTP layer only, no business logic
def create
form = CreateForm.new(params, current_user)
form.save ? render(json: form.model, status: :created) : render(json: { errors: form.errors }, status: :unprocessable_entity)
end
Model: Associations, enums, simple scopes. NO validations, NO callbacks, NO business logic.
belongs_to :user
has_many :tags, dependent: :destroy
enum :status, { draft: 0, published: 1 }
scope :recent, -> { order(created_at: :desc) }
Background Jobs
| Component | Use When |
|---|
| Solid Queue | <100 jobs/sec, no Redis needed |
| Solid Cache | DB-backed caching |
| Solid Cable | WebSockets, no Redis infra |
| Sidekiq | Latency <100ms, 10k+ jobs/min |
Jobs MUST be idempotent.
Quality Checklist
Resources
Key gems: Dry-validation, Sidekiq/Solid Queue, Scenic, Pundit, Devise/Rodauth, ViewComponent/Phlex
For detailed patterns and examples, see references/rails-patterns.md