Skip to content
Back to skills

Rails Idioms

ASecurity

Ruby on Rails framework patterns: ActiveRecord scopes, N+1 query prevention, model validations, RESTful routing, and RSpec/Minitest testing. Use when building, maintaining, or auditing Rails web applications and APIs. Pair with ruby-idioms.

  • 157 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 5, 2026
developmentjavascriptgojavarubysqlrailstestingapidatabasesecurity

Works with

  • api

Security analysis

A100/100

Scanned September 30, 2026

npx -y skills add irahardianto/antigravity-setup --skill rails-idioms --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Rails Idioms?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Rails Idioms
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/irahardianto-rails-idioms/badge)](https://www.skillsdirectory.com/skills/irahardianto-rails-idioms)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: rails-idioms
description: >-
  Ruby on Rails framework patterns: ActiveRecord scopes, N+1 query prevention, model validations, RESTful routing, and RSpec/Minitest testing. Use when building, maintaining, or auditing Rails web applications and APIs. Pair with ruby-idioms.
---

## Rails Idioms and Patterns

Rails rewards convention over configuration, Active Record, and RESTful design. Idiomatic Rails = conventional, tested, Hotwire-aware.

> Scope: Rails-specific patterns. For Ruby: `@.agents/skills/ruby-idioms/SKILL.md`.

### Active Record

1. **Scopes for reusable queries:**
   ```ruby
   class Task < ApplicationRecord
     scope :active, -> { where(status: :active) }
     scope :by_priority, -> { order(priority: :desc) }
     scope :created_after, ->(date) { where('created_at > ?', date) }

     # ✅ Chainable
     # Task.active.by_priority.created_after(1.week.ago)
   end
   ```

2. **Validations in models, not controllers:**
   ```ruby
   class Task < ApplicationRecord
     validates :title, presence: true, length: { maximum: 200 }
     validates :priority, inclusion: { in: %w[low medium high] }

     # ✅ Custom validation
     validate :deadline_must_be_in_future, on: :create

     private

     def deadline_must_be_in_future
       return unless deadline.present? && deadline < Time.current
       errors.add(:deadline, 'must be in the future')
     end
   end
   ```

3. **`includes`/`preload`** for eager loading — avoid N+1:
   ```ruby
   # ❌ N+1 — fires a query per task to load user
   Task.all.each { |t| puts t.user.name }

   # ✅ Eager load — 2 queries total
   Task.includes(:user).each { |t| puts t.user.name }

   # ✅ Use Bullet gem to detect N+1 in development
   ```

4. **Callbacks — use sparingly, prefer service objects:**
   ```ruby
   # ❌ Complex callback chains — hard to test and debug
   before_save :normalize_title, :set_defaults, :notify_assignee

   # ✅ Service object — explicit, testable
   class CreateTask
     def call(params)
       task = Task.new(normalize(params))
       task.save!
       NotificationService.notify(task.assignee, task)
       task
     end
   end
   ```

### Controllers

1. **RESTful actions** — only standard 7 actions per controller. Custom actions = new controller:
   ```ruby
   # ❌ Custom action crammed into TasksController
   def complete; end

   # ✅ Dedicated controller
   class TaskCompletionsController < ApplicationController
     def create
       task = Task.find(params[:task_id])
       task.complete!
       redirect_to task
     end
   end
   ```

2. **Strong parameters** — never mass-assign without permit:
   ```ruby
   private

   def task_params
     params.require(:task).permit(:title, :priority, :deadline, :description)
   end
   ```

3. **Service objects** for complex business logic:
   ```ruby
   class TasksController < ApplicationController
     def create
       result = CreateTask.new.call(task_params)
       if result.success?
         redirect_to result.task, notice: 'Task created'
       else
         @task = result.task
         render :new, status: :unprocessable_entity
       end
     end
   end
   ```

### Error Handling

> For universal error handling principles, see `.agents/rules/error-handling-principles.md`.

1. **`rescue_from` for controller-level error handling:**
   ```ruby
   class ApplicationController < ActionController::Base
     rescue_from ActiveRecord::RecordNotFound, with: :not_found
     rescue_from ActiveRecord::RecordInvalid, with: :unprocessable

     private

     def not_found(exception)
       render json: { error: exception.message }, status: :not_found
     end

     def unprocessable(exception)
       render json: { errors: exception.record.errors.full_messages },
              status: :unprocessable_entity
     end
   end
   ```

2. **Custom domain errors:**
   ```ruby
   module TaskErrors
     class NotAssignable < StandardError; end
     class DeadlinePassed < StandardError; end
   end
   ```

3. **Never rescue `Exception`** — always rescue `StandardError` or specific subclasses.

### Hotwire (7+)

1. **Turbo Frames** for partial page updates:
   ```erb
   <!-- Wraps content that can be independently loaded/replaced -->
   <%= turbo_frame_tag "task_#{task.id}" do %>
     <%= render task %>
   <% end %>
   ```

2. **Turbo Streams** for real-time updates:
   ```ruby
   # In controller — auto-broadcasts updates
   def create
     @task = Task.create!(task_params)
     respond_to do |format|
       format.turbo_stream
       format.html { redirect_to tasks_path }
     end
   end
   ```

3. **Stimulus** for JavaScript sprinkles — minimal JS.

### Security

> For universal security principles, see `.agents/rules/security-principles.md`.

- **CSRF protection** — enabled by default, never disable
- **Content Security Policy** — configure in `config/initializers/content_security_policy.rb`
- **Parameterized queries** — Active Record handles this, but never use string interpolation in `where`:
  ```ruby
  # ❌ SQL injection risk
  Task.where("title LIKE '%#{params[:q]}%'")

  # ✅ Parameterized
  Task.where("title LIKE ?", "%#{params[:q]}%")
  ```

### Anti-Patterns

- ❌ **Fat models** — extract to service objects / form objects / query objects
- ❌ **Business logic in controllers** — controllers are thin routing layers
- ❌ **Callbacks for complex side effects** — use service objects
- ❌ **`default_scope`** — implicit, surprising, hard to override
- ❌ **`update_attribute` (skips validation)** — use `update!`
- ❌ **`rescue Exception`** — catches everything including `SystemExit`, `Interrupt`
- ❌ **String interpolation in SQL** — SQL injection vector

### Testing

> For universal testing principles, see `.agents/rules/testing-strategy.md`. Below: language-specific patterns only.

1. **RSpec (preferred):**
   ```ruby
   RSpec.describe TasksController, type: :request do
     describe 'POST /tasks' do
       it 'creates a task with valid params' do
         post tasks_path, params: { task: { title: 'Test', priority: 'high' } }
         expect(response).to have_http_status(:created)
         expect(Task.last.title).to eq('Test')
       end

       it 'returns errors with invalid params' do
         post tasks_path, params: { task: { title: '' } }
         expect(response).to have_http_status(:unprocessable_entity)
       end
     end
   end
   ```

2. **FactoryBot** for test data:
   ```ruby
   FactoryBot.define do
     factory :task do
       title { Faker::Lorem.sentence(word_count: 3) }
       priority { %w[low medium high].sample }
       association :user
     end
   end
   ```

3. **Database Cleaner** for test isolation.
4. **Shoulda Matchers** for model spec shortcuts.

### Formatting and Static Analysis

| Tool | Purpose | Command |
|---|---|---|
| RuboCop + rubocop-rails | Linting | `rubocop --autocorrect` |
| Brakeman | Security | `brakeman --no-pager` |
| `bundle audit` | CVE scanning | `bundle audit check --update` |
| `rails_best_practices` | Code quality | `rails_best_practices .` |

### Related
- Ruby Idioms @.agents/skills/ruby-idioms/SKILL.md
- Database Design Principles @.agents/rules/database-design-principles.md
- Security Principles @.agents/rules/security-principles.md
- API Design Principles @.agents/rules/api-design-principles.md

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.