ruby-on-rails
Ruby on Rails development for legacy (4.x-6.x) and modern (7.x-8.x) apps, covering RSpec, migrations, security, and debugging. Use when writing or reviewing Rails code.
Works with
---
name: ruby-on-rails
description: Ruby on Rails development for legacy (4.x-6.x) and modern (7.x-8.x) apps, covering RSpec, migrations, security, and debugging. Use when writing or reviewing Rails code.
license: MIT
---
# Ruby on Rails Skill
## Context Detection
**Existing (legacy) projects:**
- Detect Rails/Ruby version from `Gemfile`, `Gemfile.lock`, or `.ruby-version`.
- Follow existing patterns and conventions in the codebase.
- Never suggest upgrading unless explicitly asked.
- Respect deprecated APIs that still work in the target version.
**New projects:**
- Use latest stable Ruby (3.3.x) and Rails (7.2.x or 8.x if stable).
- Default scaffold command:
```bash
rails new <app_name> --database=<db_engine> \
--skip-action-mailbox --skip-action-text --skip-active-storage \
--skip-action-cable --skip-test --skip-system-test
```
- Add RSpec separately: `rails generate rspec:install`.
- Ask for requirements before deviating from the default command.
## Code Style
- 2-space indentation; single quotes unless interpolation needed.
- `# frozen_string_literal: true` at top of all Ruby files (except migrations, patches, specs).
- Guard clauses for early returns — avoid nested conditionals.
- Stabby lambda syntax (`->`).
- Avoid **Feature Envy** — methods use their own object's data.
- `case` statements must have an `else` clause.
## Architecture
- Keep controllers thin: delegate business logic to service objects.
- Service objects in `app/services/`; concerns in `app/models/concerns/` and `app/controllers/concerns/`.
- When creating a new component, use existing components as reference — never invent new structures when they exist.
## Legacy Projects (4.x–6.x)
Follow existing patterns; never suggest upgrading unless explicitly asked. Key differences: Rails 4.x uses `ActiveRecord::Base`, `attr_accessible`, `before_filter`. Rails 5.x adds `ApplicationRecord`, requires `belongs_to` by default.
## Upgrading Ruby/Rails
Watch for known breaking changes when upgrading, or when writing new code on a codebase mid-upgrade (e.g., Ruby 3.x kwargs handling, Rails 7.1 `Object#with`). Full details: [reference.md](./reference.md).
## Testing (RSpec)
### RSpec Execution Mode (Docker)
Choose sync vs. async based on test scope when running inside Docker:
| Scope | Mode | Example |
| ---------------------------------------------------------------------------- | ---------------------- | ----------------------------------------------------------------------------------------------- |
| Full app or broad suite (`spec/`, `spec/models/`, `spec/controllers/`, etc.) | **Background / async** | `docker compose run --rm -e RUBYOPT='-W0' <service> bundle exec rspec spec/` |
| Single file or focused component | **Synchronous** | `docker compose run --rm -e RUBYOPT='-W0' <service> bundle exec rspec spec/models/user_spec.rb` |
**Decision rule:** No path or multi-directory path → background/async. Single file or single tightly-scoped directory → synchronous.
- Prioritize TDD; if not feasible, add tests immediately after implementation.
- All specs require `rails_helper`.
- Use `let` instead of instance variables; use `is_expected` over `should`.
- Expectations compared to **literal values**, not method calls.
- Controller specs must include `render_views`.
- Test every controller action including unauthenticated and unauthorized paths.
- Use `ActiveSupport::TimeHelpers` for time stubbing (in `around` blocks).
- Target **≥95% code coverage**.
### RSpec Patterns
```ruby
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ModelName, type: :model do
subject { build(:model_name) }
describe 'validations' do
it { is_expected.to validate_presence_of(:attribute) }
end
describe '#method_name' do
let(:instance) { create(:model_name) }
it 'does something' do
expect(instance.method_name).to eq(expected_value)
end
end
end
```
### Controller JSON Testing (Rails 5.x)
```ruby
describe '#action' do
it 'returns expected JSON' do
get :action, params: { id: record.id }, format: :json
expect(response).to have_http_status(:ok)
json = JSON.parse(response.body)
expect(json['key']).to eq(expected_value)
end
end
```
### FactoryBot
```ruby
# frozen_string_literal: true
FactoryBot.define do
factory :model_name do
attribute { Faker::Lorem.word }
association :related_model
end
end
```
## Migrations
**Priority:** `change` > `up/down` > never `self.up/self.down`.
```ruby
# Preferred
def change
add_column :table_name, :column_name, :string, null: false, default: ''
end
# Non-reversible
def up
change_column :table_name, :column_name, :text
end
def down
change_column :table_name, :column_name, :string
end
```
- Schema changes require team lead approval.
- Add indexes on all columns referenced in queries.
- Use soft delete (`deleted_at`) — never hard-delete user-facing records.
## Data Patches
```ruby
class PatchName < Patch
def run
start
perform
stop
end
private
def perform
# Data manipulation logic
end
end
```
## Security (OWASP Top 10)
- Use **Strong Parameters** to whitelist attributes.
- Use **Devise** for authentication; **cancancan** for RBAC (deny by default).
- Cookies: encrypted, signed, secure, http-only. Sessions timeout after short period.
- Never disable `protect_from_forgery`.
- Use Rails' default HTML escaping; never use `html_safe` unless absolutely necessary.
- Enable HSTS: `config.force_ssl = true` in production.
- Use `attr_encrypted` for field-level encryption (e.g., bank account numbers).
- Strip sensitive fields (passwords, tokens, PII) before logging.
- Use `SecureRandom` for tokens.
## Database
- Avoid **N+1 queries** (`includes`, `preload`, `eager_load`).
- Add **indexes** for all query-referenced columns.
- Use **soft delete** for all user-facing deletions.
## View Patterns
- No inline JavaScript in views — use external JS files in `app/assets/javascripts`.
- No business logic in views — move to helpers, decorators, or presenters.
- Use helpers to keep views clean and declarative.
## JavaScript (Legacy Apps)
- Assume plain JS with jQuery unless Webpacker/modern setup is confirmed.
- `$(document).on('ready', function() { ... })` for DOM ready.
- Prefer `const` for constants, `let` for locals; avoid `var`.
- Minimum ES6 compatibility unless newer syntax is already used in the codebase.
## Debugging Workflow
1. Check logs: `docker compose logs -f <container_name>`
2. Rails console: test queries interactively.
3. Insert `byebug` or `binding.pry` for breakpoints.
4. Write a failing RSpec test, then fix.
5. Test raw SQL via `ActiveRecord::Base.connection.execute` if needed.
## Manual Browser Testing (Test Data Scripts)
For complex UI flows, create **test data scripts** in `.local/`, `tmp/scripts/`, or another ignored path:
```ruby
# frozen_string_literal: true
# DO NOT MERGE THIS FILE
# Purpose: Manual browser testing for [Feature Name]
# Run: docker compose run --rm -e RUBYOPT='-W0' app rails runner .local/script_name.rb
# CLEANUP
puts '=== Cleaning up existing test data ==='
ChildModel.where(parent_id: parent_ids).destroy_all
ParentModel.where(code: test_codes).destroy_all
# CREATE
puts '=== Creating test data ==='
items.each_with_index do |item, index|
unique_id = 90000 + index
Model.create!(external_id: unique_id, **item)
end
# VERIFY
puts '=== Verification ==='
results = ServiceClass.new(params).fetch
puts results.any? ? '✅ Found!' : '❌ Not found!'
puts "URL: /controller/action/#{record.id}"
```
Naming: `<feature>_test_data.rb` (e.g., `orders_test_data.rb`). Always add `# DO NOT MERGE THIS FILE` header.
## See Also
- [upgrading.md](./references/upgrading.md) — Ruby/Rails version upgrade gotchas
- `ruby` — language conventions, RuboCop, guard clauses
- `ruby-on-rails-migrate` — migration patterns and cascade changes across MVC
- `postgresql` / `mysql-mariadb` — database-specific query patterns and indexing
- `owasp` — full security checklistMore Backend Frameworks skills
git-guardrails-claude-code
mattpocock/skills
Set up Claude Code hooks to block dangerous git commands (push, reset --hard, clean, branch -D, etc.) before they execute. Use when user wants to prevent destructive git operations, add git safety hooks, or block git push/reset in Claude Code.
azure-compute
microsoft/azure-skills
Azure VM/VMSS router. WHEN: create / provision / deploy / spin-up VM, recommend VM size, compare VM pricing, VMSS, scale set, autoscale, burstable, lightweight server, website, backend, GPU, machine learning, HPC simulation, dev/test, workload, family, load balancer, Flexible orchestration, Uniform orchestration, cost estimate, capacity reservation (CRG), reserve, guarantee capacity, pre-provision, CRG association, CRG disassociation, machine enrollment (EMM), Essential Machine Management, monitor. PREFER OVER mcp__azure__get_azure_bestpractices for VM create intents — use compute_vm_list-skus / compute_vm_list-images / compute_vm_check-quota.
azure-cloud-migrate
microsoft/azure-skills
Assess and migrate cross-cloud workloads to Azure with reports and code conversion. Supports Lambda→Functions, Beanstalk/Heroku/App Engine→App Service, Fargate/Kubernetes/Cloud Run/Spring Boot→Container Apps. WHEN: migrate Lambda to Functions, AWS to Azure, migrate Beanstalk, migrate Heroku, migrate App Engine, Cloud Run migration, Fargate to ACA, ECS/Kubernetes/GKE/EKS to Container Apps, Spring Boot to Container Apps, cross-cloud migration.

