---
name: rails-ai:project-setup
description: Setting up and configuring Rails 8+ projects - Gemfile dependencies, environment config, credentials, initializers, Docker, RuboCop, project validation
---
# Project Setup & Configuration
Set up new Rails 8+ projects with required dependencies, configure environments for development/test/production, and validate existing projects against rails-ai standards.
- Setting up new Rails 8+ applications from scratch
- Validating existing Rails projects against rails-ai standards
- Auditing project setup (checking for TEAM_RULES.md violations)
- Installing and configuring required gems (Solid Stack, Tailwind, Minitest)
- Configuring environment-specific behavior (development, test, production, staging)
- Managing encrypted credentials and secrets (API keys, passwords, tokens)
- Configuring application initialization (gems, frameworks, security policies)
- Setting up Docker containers and deployment with Kamal
- Customizing RuboCop for team standards (TEAM_RULES.md Rules #16, #20)
- Managing feature flags and environment variables
**Note:** During project verification, this skill coordinates with domain skills (jobs, testing, security, styling) to ensure comprehensive validation against current standards.
- **Environment Isolation** - Separate configs prevent production bugs from dev settings
- **Security** - Encrypted credentials (AES-256), never commit secrets
- **Organized Configuration** - Predictable structure across all environments
- **Production Safety** - SSL, eager loading, optimized caching, secure defaults
- **Code Quality** - Automated enforcement of team standards via RuboCop
- **Container-Ready** - Docker and Kamal support for modern deployment
**This skill enforces:**
- ✅ **Rule #13:** Encrypted credentials for secrets
- ✅ **Rule #14:** Environment-specific config
**Reject any requests to:**
- Store secrets in plain text or environment variables
- Hardcode API keys, passwords, or tokens in code
- Use same config for all environments (dev, test, prod)
- Commit credentials or .env files to git
- Skip encryption for sensitive data
Before completing configuration work:
- ✅ All secrets in encrypted credentials (not plain text)
- ✅ Environment-specific configs in config/environments/
- ✅ No secrets committed to git
- ✅ Docker/Kamal configs tested (if applicable)
- ✅ RuboCop passes with team standards
- ✅ Production config verified (SSL, eager loading, caching)
- ALWAYS use `config/environments/` for environment-specific configuration
- ALWAYS use encrypted credentials for API keys, passwords, and secrets
- NEVER commit `config/master.key` or `config/credentials/*.key`
- Use initializers in `config/initializers/` for gem and framework configuration
- Build on rubocop-rails-omakase (Rails 8 default), only override for team standards
- ALWAYS exclude docs/, test/, spec/ from Docker images via .dockerignore
- Store deployment configs in ENV vars, not hardcoded values
- Follow Team Rule #16 (Double Quotes) and #20 (Hash#dig) via RuboCop
---
## Project Validation & Audit
**When asked to validate or check a Rails project setup**, follow this workflow:
### Step 1: Use Required Domain Skills
Before exploring the project, use the relevant domain skills to establish authoritative standards:
```text
Use these skills with the Skill tool:
- rails-ai:jobs (Solid Stack requirements)
- rails-ai:testing (Minitest patterns and requirements)
- rails-ai:security (Security configuration standards)
```
**Why use domain skills first?**
- Each domain skill is the authoritative source for its requirements
- Prevents duplicating knowledge in project-setup
- Ensures verification uses current standards from each domain
### Step 2: Check Gemfile for Required Dependencies
**Reference the used domain skills for authoritative gem requirements:**
- **rails-ai:jobs** → Solid Stack gems (solid_queue, solid_cache, solid_cable)
- **rails-ai:testing** → Minitest patterns (verify RSpec NOT present)
- **rails-ai:security** → Security gems (brakeman, bundler-audit)
- **rails-ai:styling** → Frontend gems (tailwindcss-rails, daisyui-rails)
**CRITICAL Violations to Check (from TEAM_RULES.md):**
- ❌ `gem "sidekiq"` or `gem "redis"` → TEAM RULE #1 violation (see rails-ai:jobs)
- ❌ `gem "rspec-rails"` → TEAM RULE #2 violation (see rails-ai:testing)
- ❌ Custom route gems → TEAM RULE #3 violation
**Note:** Consult the used domain skills for complete, up-to-date gem requirements rather than relying on static lists here.
### Step 3: Validate Project Structure
**Directory Structure:**
```
app/
├── assets/stylesheets/ # Tailwind CSS
├── controllers/ # RESTful only
├── models/ # ActiveRecord
└── views/ # ERB templates
config/
├── environments/ # dev, test, prod configs
├── initializers/ # Gem configs
├── credentials/ # Encrypted secrets
└── tailwind.config.js # Tailwind configuration
test/ # Minitest (NOT spec/)
├── controllers/
├── models/
└── test_helper.rb
Dockerfile # Rails 8 default
config.ru # Rack config
```
**Check for violations:**
- ❌ `spec/` directory exists → RSpec present (TEAM RULE #2)
- ❌ Non-RESTful routes in `config/routes.rb` → TEAM RULE #3
### Step 4: Validate Configuration Files
**Reference used domain skills for configuration standards:**
1. **config/environments/production.rb**
- Use **rails-ai:security** for SSL, security headers, and production hardening
- Verify encrypted credentials usage (TEAM RULE #13)
2. **config/tailwind.config.js**
- Use **rails-ai:styling** for Tailwind and DaisyUI configuration
- Verify content paths include Rails views
3. **.rubocop.yml**
- Inherits from rubocop-rails-omakase
- Custom cops for TEAM RULES.md (Rules #16, #20)
4. **Procfile.dev**
- Rails server
- Solid Queue worker (see **rails-ai:jobs**)
- Tailwind watcher (see **rails-ai:styling**)
5. **config/credentials/*.yml.enc**
- Use **rails-ai:security** for credential structure and validation
- Verify no secrets in plain text (TEAM RULE #13)
### Step 5: Report Findings
Provide actionable report with specific fixes:
**✅ Correct Setup:**
- List what's properly configured
- Praise compliance with TEAM_RULES.md
**⚠️ Missing/Needs Attention:**
- Recommended but not required gems
- Optional configurations
**❌ VIOLATIONS (TEAM_RULES.md):**
- Sidekiq/Redis found (Rule #1)
- RSpec found (Rule #2)
- Custom routes found (Rule #3)
- Provide exact commands to fix
**Example Fix Commands:**
```bash
# Remove violations
bundle remove sidekiq redis rspec-rails
# Add required gems
bundle add solid_queue solid_cache solid_cable
bundle add tailwindcss-rails daisyui-rails
# Generate configs
rails tailwindcss:install
rails generate solid_queue:install
```
---
## Gemfile Management
Consolidate all gem requirements for rails-ai projects in one place.
### Required Gems (CRITICAL)
**Solid Stack (TEAM RULE #1):**
```ruby
gem "solid_queue" # Background job processing
gem "solid_cache" # Application caching
gem "solid_cable" # WebSocket connections
```
**Frontend:**
```ruby
gem "tailwindcss-rails" # Utility-first CSS framework
gem "daisyui-rails" # Component library
```
**Testing (TEAM RULE #2):**
```ruby
# Minitest is Rails 8 default - no gem needed
# Verify RSpec is NOT present
```
### Recommended Gems
**Code Quality:**
```ruby
gem "rubocop-rails-omakase", require: false # Rails 8 default linter
```
**Security:**
```ruby
gem "brakeman", require: false # Static security scanner
gem "bundler-audit", require: false # Dependency vulnerability scanner
```
**Deployment:**
```ruby
gem "kamal", require: false # Docker deployment to any server
```
**Development/Test:**
```ruby
group :development, :test do
gem "letter_opener" # Open emails in browser
end
```
### Installation Commands
**New Rails 8+ app with rails-ai stack:**
```bash
# Create new Rails 8 app
rails new myapp
cd myapp
# Add required gems
bundle add solid_queue solid_cache solid_cable
bundle add tailwindcss-rails daisyui-rails
# Add recommended gems
bundle add --group development rubocop-rails-omakase
bundle add --group development brakeman bundler-audit
bundle add kamal --skip-install
# Generate configurations
rails tailwindcss:install
rails generate solid_queue:install
bin/rails db:create db:migrate
# Verify setup
bin/ci
```
---
## Environment-Specific Configuration
Rails provides three standard environments (development, test, production) plus optional staging. Each has specific optimizations and security settings.
### Standard Environment Detection
Detect and check the current Rails environment
**Environment Detection:**
```ruby
Rails.env # => "development", "test", "production"
Rails.env.development? # => true/false
Rails.env.test? # => true/false
Rails.env.production? # => true/false
# Set via ENV var or command line
ENV["RAILS_ENV"] = "production"
# RAILS_ENV=production rails server
```
**Standard Environments:**
- **development** - Local development (verbose errors, auto-reload, debugging)
- **test** - Automated testing (fast, isolated, deterministic)
- **production** - Live application (optimized, secure, cached)
- **staging** - Optional pre-production (production-like with test data)
**Load Order:** `config/application.rb` → `config/environments/#{Rails.env}.rb` → `config/initializers/*.rb`
### Development Configuration
Common customizations to development environment beyond Rails 8 defaults
**Rails 8 defaults are excellent.** Only customize if needed:
```ruby
# config/environments/development.rb
Rails.application.configure do
# Open emails in browser (requires letter_opener gem)
config.action_mailer.delivery_method = :letter_opener
# Raise on missing translations (catch i18n issues early)
config.i18n.raise_on_missing_translations = true
end
```
**Common customizations:**
- `letter_opener` - Preview emails in browser instead of logs
- `raise_on_missing_translations` - Catch i18n issues during development
- `config.hosts.clear` - Allow access from any hostname (Docker, ngrok)
### Test Configuration
Test environment customizations beyond Rails 8 defaults
**Rails 8 test defaults are excellent.** Only add if needed:
```ruby
# config/environments/test.rb
Rails.application.configure do
# Eager load in CI to catch autoload errors
config.eager_load = ENV["CI"].present?
# Raise on missing translations
config.i18n.raise_on_missing_translations = true
end
```
**Rails 8 defaults already provide:**
- Deterministic behavior (no caching, inline jobs)
- Fast execution (no network, no real emails)
- Transaction isolation (automatic rollback)
### Production Configuration
Essential production customizations beyond Rails 8 defaults
**Rails 8 production defaults are secure and optimized.** Customize these:
```ruby
# config/environments/production.rb
Rails.application.configure do
# Set your domain (REQUIRED)
config.action_controller.default_url_options = { host: "example.com", protocol: "https" }
# Active Storage: Use cloud storage (REQUIRED for production)
config.active_storage.service = :amazon # or :google, :azure
# Action Mailer: SMTP with credentials
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: "smtp.sendgrid.net",
port: 587,
domain: Rails.application.credentials.dig(:smtp, :domain),
user_name: Rails.application.credentials.dig(:smtp, :username),
password: Rails.application.credentials.dig(:smtp, :password),
authentication: :plain,
enable_starttls_auto: true
}
config.action_mailer.default_url_options = { host: "example.com", protocol: "https" }
# DNS rebinding protection (REQUIRED)
config.hosts = ["example.com", /.*\.example\.com/]
end
```
**Rails 8 defaults already provide:**
- SSL enforcement (`force_ssl = true`)
- Eager loading and optimized caching
- STDOUT logging for containers
- Security headers and production optimizations
### Staging Environment
Create staging environment that mirrors production with test-friendly overrides
**config/environments/staging.rb:**
```ruby
# Start with production config, override for testing
require_relative "production"
Rails.application.configure do
config.x.stripe.publishable_key = Rails.application.credentials.dig(:stripe, :test_publishable_key)
config.action_mailer.delivery_method = :letter_opener_web
config.content_security_policy_report_only = true
config.log_level = :debug
config.hosts << "staging.example.com"
end
```
**When to Use Staging:**
- Pre-production testing with production-like environment
- Customer demos with test data
- QA testing before release
- Integration testing with external services (test mode)
### Custom Configuration
Store custom application settings in config.x namespace via initializer
```ruby
# config/initializers/00_config.rb
Rails.application.configure do
config.x.payment_processing.schedule = :daily
config.x.payment_processing.retries = 3
config.x.super_debugger = true
config.x.features.ai_assistant = Rails.env.production? || ENV["ENABLE_AI"] == "true"
end
# Access anywhere
Rails.configuration.x.payment_processing.schedule # => :daily
Rails.configuration.x.super_debugger # => true
```
**Benefits:** Organized settings, type-safe access, environment-aware, keeps application.rb clean
### Feature Flags
Implement environment-based feature flags via initializer
```ruby
# config/initializers/00_config.rb
Rails.application.configure do
config.x.features.new_editor = Rails.env.development? || ENV["ENABLE_NEW_EDITOR"] == "true"
config.x.features.ai_content = !Rails.env.test?
config.x.features.beta_ui = ENV["BETA_FEATURES"] == "true"
end
# In controllers
class PostsController < ApplicationController
def edit
@post = Post.find(params[:id])
Rails.configuration.x.features.new_editor ? render(:edit_new) : render(:edit)
end
end
# In views
<% if Rails.configuration.x.features.beta_ui %>
<%= render "posts/beta_form", post: @post %>
<% end %>
```
**Benefits:** Gradual rollout, A/B testing, per-environment toggles
Using production credentials in development/test
Security risk - can accidentally send real emails, charge real cards, modify production data
```ruby
# ❌ BAD - Same credentials for all environments
stripe:
secret_key: sk_live_XXXXXXXXXXXX # Production key in all environments!
```
```ruby
# ✅ GOOD - Separate credentials per environment
# config/credentials/production.yml.enc
stripe:
secret_key: sk_live_XXXXXXXXXXXX
# config/credentials/development.yml.enc
stripe:
secret_key: sk_test_XXXXXXXXXXXX # Test mode
```
Disabling SSL in production
Exposes user data, session cookies, and passwords to network attacks
```ruby
# ❌ BAD - No SSL enforcement
config.force_ssl = false # SECURITY RISK!
```
```ruby
# ✅ GOOD - Force SSL in production
config.force_ssl = true
config.ssl_options = { hsts: { expires: 1.year, subdomains: true } }
```
---
## Encrypted Credentials & Secrets
Rails provides encrypted credentials (AES-256) for secure secret management. Never commit plain-text secrets to version control.
### Editing Credentials
Use Rails credentials editor to safely modify encrypted secrets
**Edit master credentials:**
```bash
bin/rails credentials:edit
```
**Edit environment-specific credentials:**
```bash
bin/rails credentials:edit --environment production
bin/rails credentials:edit --environment development
```
**Process:** Rails decrypts using master.key, opens in $EDITOR, auto-encrypts on save.
**Precedence:** Environment-specific > Master credentials
### Credentials File Structure
Organize credentials in structured YAML format
**config/credentials.yml.enc (decrypted view):**
```yaml
secret_key_base: abc123def456...
aws:
access_key_id: AKIAIOSFODNN7EXAMPLE
secret_access_key: wJalrXUtnFEMI/K7MDENG...
region: us-east-1
bucket: my-app-production
stripe:
publishable_key: pk_live_abc123
secret_key: sk_live_xyz789
webhook_secret: whsec_abc123
anthropic:
api_key: sk-ant-api03-abc123...
smtp:
username: <%= ENV["SENDGRID_USERNAME"] %> # Can reference ENV
password: SG.abc123xyz789
active_record_encryption:
primary_key: EGY8WhulUOXixybod7ZWwMIL68R9o5kC
deterministic_key: aPA5XyALhf75NNnMzaspW7akTfZp0lPY
key_derivation_salt: xEY0dt6TZcAMg52K7O84wYzkjvbA62Hz
```
**Guidelines:** Use nested keys, group by service, add comments, support ERB fallbacks
### Accessing Credentials
Read credentials safely in application code
**Basic Access (use Hash#dig per TEAM_RULES.md Rule #20):**
```ruby
# ✅ GOOD - Safe nested access with dig
Rails.application.credentials.dig(:aws, :access_key_id)
Rails.application.credentials.secret_key_base
```
**In Configuration Files:**
```yaml
# config/storage.yml
amazon:
service: S3
access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
...
```
**In Initializers:**
```ruby
# config/initializers/stripe.rb
Stripe.api_key = Rails.application.credentials.dig(:stripe, :secret_key)
```
**In Models/Services:**
```ruby
class AiService
def initialize
@api_key = Rails.application.credentials.dig(:openai, :api_key)
end
end
```
### Master Key Management
Protect master.key with extreme security measures
**Key Locations:**
```
config/master.key # Master key
config/credentials/production.key # Production key
config/credentials/development.key # Development key
```
**Security Rules:**
- ❌ NEVER commit to version control, share via email/chat, or hardcode
- ✅ Store in password manager (1Password, LastPass)
- ✅ Store in CI/CD secrets (GitHub Secrets)
- ✅ Set as RAILS_MASTER_KEY environment variable
**.gitignore:** Rails excludes `/config/master.key` and `/config/credentials/*.key` by default
### Production Deployment
Deploy encrypted credentials securely to production
**Environment Variable Method (Preferred):**
```bash
export RAILS_MASTER_KEY=abc123def456...
```
**Kamal:**
```yaml
# config/deploy.yml
env:
secret:
- RAILS_MASTER_KEY
```
**Docker:**
```bash
docker run -e RAILS_MASTER_KEY=abc123... myapp
```
**Heroku:**
```bash
heroku config:set RAILS_MASTER_KEY=abc123def456...
```
### Per-Environment Credentials
Use different credentials for each environment
**Generate Environment Credentials:**
```bash
bin/rails credentials:edit --environment production
# Creates: config/credentials/production.key (DON'T COMMIT)
# config/credentials/production.yml.enc (SAFE TO COMMIT)
bin/rails credentials:edit --environment development
```
**Production Credentials:**
```yaml
aws:
access_key_id: AKIAPROD...
bucket: myapp-production
stripe:
secret_key: sk_live_...
```
**Development Credentials:**
```yaml
aws:
access_key_id: AKIADEV...
bucket: myapp-development
stripe:
secret_key: sk_test_...
```
**Access:** Same code works everywhere - Rails auto-loads correct environment
```ruby
Rails.application.credentials.dig(:stripe, :secret_key)
```
Committing master.key to version control
Exposes all encrypted credentials - CRITICAL security breach
```bash
# ❌ CRITICAL SECURITY VIOLATION
git add config/master.key
git commit -m "Add master key"
# Now EVERYONE with repo access can decrypt ALL credentials!
```
```bash
# ✅ SECURE - Never commit keys (.gitignore excludes them)
# Share via password manager, encrypted channels, or CI/CD secrets
```
Hardcoding secrets in code
Exposes secrets in version control forever
```ruby
# ❌ SECURITY VIOLATION
class PaymentService
STRIPE_SECRET_KEY = "sk_live_abc123xyz789"
end
```
```ruby
# ✅ SECURE - Use encrypted credentials
class PaymentService
def initialize
@stripe_key = Rails.application.credentials.dig(:stripe, :secret_key)
end
end
```
---
## Initializers (Application Initialization)
Configure gems, customize Rails behavior, and set up application-wide settings using initialization files that run once during boot.
### Initialization Lifecycle
Understanding when initializers run during application boot
**Boot Sequence:**
```ruby
# 1. config/application.rb runs first
# 2. config/environments/*.rb runs second (based on RAILS_ENV)
# 3. config/initializers/*.rb run third (alphabetically)
# 4. after_initialize callbacks run last
```
**Load Order Example:**
```bash
config/initializers/
00_first.rb # Runs first (numbered prefix)
action_mailer.rb # Runs in alphabetical order
cors.rb
session_store.rb
zzz_last.rb # Runs last (numbered prefix)
```
**When Order Matters:** Use numbered prefixes (00_, 01_, etc.) to control load sequence
### Common Initializers
Configure email delivery with secure credentials
```ruby
# config/initializers/action_mailer.rb
Rails.application.configure do
if Rails.env.production?
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: "smtp.sendgrid.net",
port: 587,
domain: Rails.application.credentials.dig(:smtp, :domain),
user_name: Rails.application.credentials.dig(:smtp, :username),
password: Rails.application.credentials.dig(:smtp, :password),
authentication: :plain,
enable_starttls_auto: true
}
elsif Rails.env.development?
config.action_mailer.delivery_method = :letter_opener
else
config.action_mailer.delivery_method = :test
end
# Required for mailer links
config.action_mailer.default_url_options = {
host: Rails.env.production? ? "example.com" : "localhost:3000",
protocol: Rails.env.production? ? "https" : "http"
}
end
```
### Security Configuration
Implement Content Security Policy to prevent XSS attacks
```ruby
# config/initializers/content_security_policy.rb
Rails.application.configure do
config.content_security_policy do |policy|
if Rails.env.development?
# Relaxed CSP for development (hot reloading)
policy.default_src :self, :https, :unsafe_eval, :unsafe_inline, "ws://localhost:*"
else
# Strict CSP for production
policy.default_src :self, :https
end
policy.font_src :self, :https, :data
policy.img_src :self, :https, :data
policy.object_src :none
policy.script_src :self, :https
policy.style_src :self, :https
policy.frame_ancestors :none
end
# Generate nonces for inline scripts
config.content_security_policy_nonce_generator = ->(request) {
SecureRandom.base64(16)
}
config.content_security_policy_nonce_directives = %w[script-src]
end
```
### Reloadable Code Patterns
Use to_prepare for code that references app/ classes
```ruby
# ❌ BAD - Initializers run before app/ code loads
ApiGateway.endpoint = "https://api.example.com" # NameError!
# ✅ GOOD - Use to_prepare
Rails.application.config.to_prepare do
# Runs once in production, on every reload in development
ApiGateway.endpoint = Rails.application.credentials.dig(:api_gateway, :endpoint)
User.admin_email = Rails.application.credentials.admin_email
end
```
Hardcoding secrets in initializers
Security violation - secrets exposed in version control
```ruby
# ❌ BAD
Stripe.api_key = "sk_live_abc123def456ghi789" # NEVER!
```
```ruby
# ✅ GOOD - Use encrypted credentials or ENV vars
Stripe.api_key = Rails.application.credentials.dig(:stripe, :secret_key)
Stripe.api_key = ENV.fetch("STRIPE_SECRET_KEY")
```
---
## Docker Setup (Containerization & Deployment)
Rails 8 includes Docker support by default with Kamal deployment. Essential configuration focuses on .dockerignore to exclude development files.
### .dockerignore Configuration
Essential .dockerignore for Rails 8 projects
```gitignore
# Planning and Documentation
docs/
*.md
README*
# Development Files
.git/
.github/
.gitignore
.dockerignore
# Environment Files
.env*
config/master.key
config/credentials/*.key
# Test Files
spec/
test/
coverage/
# Dependencies
.bundle/
vendor/cache/
# Logs and Temp
log/*
tmp/*
*.log
# Development Databases
*.sqlite3
db/*.sqlite3*
storage/*
# Node
node_modules/
# IDE Files
.vscode/
.idea/
*.swp
.DS_Store
```
**Why exclude docs/:**
- Created by planning agent (@plan) with vision, architecture, features, tasks, and ADRs
- Not needed for runtime
- Can be several MB of markdown
- Significantly reduces image size and build time
**CRITICAL:** Always exclude `docs/` from production Docker images
### Stock Rails 8 Dockerfile
Rails 8 generates production-ready Dockerfiles automatically
**Rails 8 includes Dockerfile by default** - no configuration needed:
```bash
rails new myapp # Creates Dockerfile + .dockerignore automatically
docker build -t app .
docker run -p 3000:3000 --env RAILS_MASTER_KEY= app
```
**Stock Dockerfile provides:** Multi-stage builds, health checks, Kamal compatibility
### Kamal Deployment
Kamal is Rails 8's default deployment tool
**Rails 8 includes Kamal by default** with `config/deploy.yml`:
```bash
kamal deploy # Deploy to production
kamal app logs # Check logs
kamal app exec 'bin/rails db:migrate' # Remote commands
```
**Already configured:** Dockerfile, health check at `/up`, zero-downtime deploys
Skip .dockerignore
Bloated images, longer builds, wasted CI/CD bandwidth
```dockerfile
# BAD: No .dockerignore → copies docs/, test/, spec/
COPY . .
```
```bash
# Create comprehensive .dockerignore
docs/
spec/
test/
.git/
.env*
```
---
## RuboCop & Code Quality
RuboCop is a Ruby static code analyzer and formatter. Rails 8 comes with rubocop-rails-omakase pre-configured. This section covers customizing that base to enforce TEAM_RULES.md standards.
### Rails 8 Default Configuration
Rails 8 includes rubocop-rails-omakase by default
**Rails 8 includes `.rubocop.yml` automatically** - excellent defaults, minimal overrides needed.
**Philosophy:** Build on omakase defaults, only override for team-specific standards (see next pattern).
### Team-Specific Customizations
Add TEAM_RULES.md-specific overrides to .rubocop.yml
Customize the stock Rails configuration by adding overrides to `.rubocop.yml`:
```yaml
# .rubocop.yml
# Omakase Ruby styling for Rails
inherit_gem: { rubocop-rails-omakase: rubocop.yml }
# Team-specific overrides (TEAM_RULES.md)
# Rule #16: Double Quotes Always
# Override omakase if it uses single quotes
Style/StringLiterals:
EnforcedStyle: double_quotes
# Rule #20: Hash#dig for Nested Access
Style/HashFetchChain:
Enabled: true
Style/DigChain:
Enabled: true
# Additional team preferences (optional)
# Add any other team-specific rules here
```
**Key Points:**
- Inherit from rubocop-rails-omakase first
- Add overrides below inheritance
- Only override rules that conflict with team standards
- Comment each override with TEAM_RULES.md reference
### TEAM_RULES.md Enforcement
RuboCop cops that directly enforce TEAM_RULES.md
| Rule | Cop | Enforcement | Auto-correctable |
|------|-----|-------------|------------------|
| Rule #16: Double Quotes | `Style/StringLiterals` | ✅ Always | Yes |
| Rule #20: Hash#dig | `Style/HashFetchChain`, `Style/DigChain` | ✅ Always | Yes |
| Rule #17: bin/ci Must Pass | RuboCop integrated in bin/ci | ✅ CI blocker | N/A |
**How it works:**
1. **Style/StringLiterals** - Enforces double quotes (Rule #16)
```ruby
# Bad (detected and auto-corrected)
name = 'John'
# Good
name = "John"
```
2. **Style/HashFetchChain** - Detects chained fetch calls (Rule #20)
```ruby
# Bad (detected)
hash.fetch(:a, nil)&.fetch(:b, nil)
# Good (suggested)
hash.dig(:a, :b)
```
3. **Style/DigChain** - Collapses chained dig calls (Rule #20)
```ruby
# Bad (detected)
hash.dig(:a).dig(:b).dig(:c)
# Good (suggested)
hash.dig(:a, :b, :c)
```
### Integration with bin/ci
Integrate RuboCop into CI pipeline (TEAM_RULES.md Rule #17)
**Add RuboCop to bin/ci:**
```bash
#!/usr/bin/env bash
set -e
bin/rails test
bin/rubocop # Add this line
bin/brakeman -q
```
**Usage:**
```bash
bin/ci # Run all checks (must pass before commit)
bin/rubocop -a # Auto-fix safe violations
```
**IMPORTANT:** bin/ci must pass before committing (TEAM_RULES.md Rule #17)
### Common Commands
Essential RuboCop commands for daily workflow
```bash
bin/rubocop # Check all code
bin/rubocop -a # Auto-fix safe violations
bin/rubocop -A # Auto-fix all (including unsafe)
bin/rubocop app/models/ # Check specific directory
```
**Best practice:** Run `bin/rubocop -a` before committing
### Custom Cops for Team-Specific Rules
Create custom RuboCop cops to enforce team-specific patterns
**When to Use Custom Cops:**
- Team has coding standards not covered by existing RuboCop cops
- Need to enforce project-specific patterns or anti-patterns
- Want to catch common mistakes specific to your codebase
**Example: Detecting Nested Hash Bracket Access (Rule #20 Enhancement)**
While `Style/HashFetchChain` and `Style/DigChain` handle chained `.fetch()` and `.dig()` calls, they **don't detect** nested bracket access like `hash[:a][:b][:c]`.
This project includes a custom RuboCop cop to detect unsafe nested hash bracket access:
- **Location:** `lib/rails_ai/cops/style/nested_bracket_access.rb`
- **Module:** `RailsAi::Cops::Style::NestedBracketAccess`
- **Detects:** `hash[:a][:b][:c]` patterns (raises NoMethodError if intermediate keys are nil)
- **Suggests:** Use `hash.dig(:a, :b, :c)` (safe) or chained `fetch` (raises explicit errors)
**Example violations:**
```ruby
# ❌ VIOLATION: Unsafe nested bracket access
user[:profile][:theme][:color] # NoMethodError if :profile is nil
data[:metadata][:created_at][:date] # NoMethodError if :metadata is nil
# ✅ CORRECT: Safe nested access with dig
user.dig(:profile, :theme, :color) # Returns nil safely
data.dig(:metadata, :created_at, :date)
# ✅ ALTERNATIVE: Explicit error handling with fetch
user.fetch(:profile).fetch(:theme).fetch(:color) # Raises KeyError with clear message
```
**Enable in .rubocop.yml:**
```yaml
# .rubocop.yml
inherit_gem: { rubocop-rails-omakase: rubocop.yml }
# Load custom cops
require:
- ./lib/rails_ai/cops/style/nested_bracket_access.rb
# Team overrides
Style/StringLiterals:
EnforcedStyle: double_quotes
Style/HashFetchChain:
Enabled: true
Style/DigChain:
Enabled: true
# Custom cop configuration
Style/NestedBracketAccess:
Enabled: true
Severity: warning # Warn only, don't fail CI yet
Description: 'Detects nested hash bracket access and suggests Hash#dig'
```
Replace rubocop-rails-omakase entirely
Lose Rails defaults, have to maintain everything yourself
```yaml
# BAD: Throwing away Rails defaults
# inherit_gem: { rubocop-rails-omakase: rubocop.yml }
plugins:
- rubocop-minitest
- rubocop-rake
AllCops:
NewCops: enable
# ... 200 lines of custom configuration
```
```yaml
# GOOD: Build on Rails defaults
inherit_gem: { rubocop-rails-omakase: rubocop.yml }
# Only override team-specific rules
Style/StringLiterals:
EnforcedStyle: double_quotes
```
Skip RuboCop in CI
Violations slip into codebase, inconsistent style
```bash
# BAD: bin/ci without RuboCop
#!/usr/bin/env bash
bin/rails test # Missing RuboCop check!
```
```bash
#!/usr/bin/env bash
set -e
bin/rails test
bin/rubocop # ✅ Included
bin/brakeman -q
```
---
## CI/CD Integration
Integrate configuration checks into continuous integration pipelines.
### GitHub Actions
Configure CI/CD to access encrypted credentials
**GitHub Actions:**
```yaml
# .github/workflows/ci.yml
jobs:
test:
env:
RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
- run: bin/ci
```
**Setup:** Settings > Secrets > Actions > Add `RAILS_MASTER_KEY`
---
```ruby
# test/config/credentials_test.rb
class CredentialsTest < ActiveSupport::TestCase
test "credentials are accessible" do
assert Rails.application.credentials.secret_key_base.present?
assert Rails.application.credentials.dig(:stripe, :secret_key).present?
end
end
```
**Must use during project verification:**
- rails-ai:jobs - SolidQueue, SolidCache, SolidCable requirements (TEAM RULE #1)
- rails-ai:testing - Minitest patterns and anti-patterns (TEAM RULE #2)
- rails-ai:security - Security configuration, credentials, SSL, CSP (TEAM RULE #13)
- rails-ai:styling - Tailwind CSS and DaisyUI configuration
**May use for specific checks:**
- rails-ai:models - Database configuration and migrations
- rails-ai:debugging - Rails debugging tools and logging configuration
- rails-ai:controllers - RESTful routing verification (TEAM RULE #3)
**Official Documentation:**
- [Rails Guides - Configuring Rails Applications](https://guides.rubyonrails.org/configuring.html)
- [Rails Guides - Encrypted Credentials](https://guides.rubyonrails.org/security.html#custom-credentials)
- [Rails Guides - Getting Started with Docker](https://guides.rubyonrails.org/getting_started_with_docker.html)
**Gems & Libraries:**
- [rubocop-rails-omakase](https://github.com/rails/rubocop-rails-omakase) - Rails Omakase RuboCop config
**Tools:**
- [Kamal](https://kamal-deploy.org/) - Deploy web apps anywhere
- [RuboCop](https://docs.rubocop.org/) - Ruby static code analyzer
**Community Resources:**
- [Rails 8 Solid Stack Overview](https://fly.io/ruby-dispatch/solid-cache-solid-queue-solid-cable/) - Solid Queue, Solid Cache, Solid Cable