Marketplace

rails-ai:project-setup

Setting up and configuring Rails 8+ projects - Gemfile dependencies, environment config, credentials, initializers, Docker, RuboCop, project validation

$ Instalar

git clone https://github.com/zerobearing2/rails-ai /tmp/rails-ai && cp -r /tmp/rails-ai/skills/project-setup ~/.claude/skills/rails-ai

// tip: Run this command in your terminal to install the skill


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.

Note: During project verification, this skill coordinates with domain skills (jobs, testing, security, styling) to ensure comprehensive validation against current standards.

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

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:

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:

# 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):

gem "solid_queue"      # Background job processing
gem "solid_cache"      # Application caching
gem "solid_cable"      # WebSocket connections

Frontend:

gem "tailwindcss-rails"  # Utility-first CSS framework
gem "daisyui-rails"      # Component library

Testing (TEAM RULE #2):

# Minitest is Rails 8 default - no gem needed
# Verify RSpec is NOT present

Recommended Gems

Code Quality:

gem "rubocop-rails-omakase", require: false  # Rails 8 default linter

Security:

gem "brakeman", require: false         # Static security scanner
gem "bundler-audit", require: false    # Dependency vulnerability scanner

Deployment:

gem "kamal", require: false  # Docker deployment to any server

Development/Test:

group :development, :test do
  gem "letter_opener"  # Open emails in browser
end

Installation Commands

New Rails 8+ app with rails-ai stack:

# 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

Environment Detection:

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

Rails 8 defaults are excellent. Only customize if needed:

# 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

Rails 8 test defaults are excellent. Only add if needed:

# 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

Rails 8 production defaults are secure and optimized. Customize these:

# 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

config/environments/staging.rb:

# 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

# 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

# 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

# โŒ BAD - Same credentials for all environments
stripe:
  secret_key: sk_live_XXXXXXXXXXXX  # Production key in all environments!

# โœ… 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

# โŒ BAD - No SSL enforcement
config.force_ssl = false  # SECURITY RISK!

# โœ… 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

Edit master credentials:

bin/rails credentials:edit

Edit environment-specific credentials:

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

config/credentials.yml.enc (decrypted view):

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

Basic Access (use Hash#dig per TEAM_RULES.md Rule #20):

# โœ… GOOD - Safe nested access with dig
Rails.application.credentials.dig(:aws, :access_key_id)
Rails.application.credentials.secret_key_base

In Configuration Files:

# 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:

# config/initializers/stripe.rb
Stripe.api_key = Rails.application.credentials.dig(:stripe, :secret_key)

In Models/Services:

class AiService
  def initialize
    @api_key = Rails.application.credentials.dig(:openai, :api_key)
  end
end

Master Key Management

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

Environment Variable Method (Preferred):

export RAILS_MASTER_KEY=abc123def456...

Kamal:

# config/deploy.yml
env:
  secret:
    - RAILS_MASTER_KEY

Docker:

docker run -e RAILS_MASTER_KEY=abc123... myapp

Heroku:

heroku config:set RAILS_MASTER_KEY=abc123def456...

Per-Environment Credentials

Generate Environment Credentials:

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:

aws:
  access_key_id: AKIAPROD...
  bucket: myapp-production

stripe:
  secret_key: sk_live_...

Development Credentials:

aws:
  access_key_id: AKIADEV...
  bucket: myapp-development

stripe:
  secret_key: sk_test_...

Access: Same code works everywhere - Rails auto-loads correct environment

Rails.application.credentials.dig(:stripe, :secret_key)

# โŒ CRITICAL SECURITY VIOLATION
git add config/master.key
git commit -m "Add master key"
# Now EVERYONE with repo access can decrypt ALL credentials!

# โœ… SECURE - Never commit keys (.gitignore excludes them)
# Share via password manager, encrypted channels, or CI/CD secrets

# โŒ SECURITY VIOLATION
class PaymentService
  STRIPE_SECRET_KEY = "sk_live_abc123xyz789"
end

# โœ… 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

Boot Sequence:

# 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:

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

# 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

# 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

# โŒ 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

# โŒ BAD
Stripe.api_key = "sk_live_abc123def456ghi789"  # NEVER!

# โœ… 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

# 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 includes Dockerfile by default - no configuration needed:

rails new myapp  # Creates Dockerfile + .dockerignore automatically
docker build -t app .
docker run -p 3000:3000 --env RAILS_MASTER_KEY=<key> app

Stock Dockerfile provides: Multi-stage builds, health checks, Kamal compatibility

Kamal Deployment

Rails 8 includes Kamal by default with config/deploy.yml:

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

# BAD: No .dockerignore โ†’ copies docs/, test/, spec/
COPY . .

# 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.yml automatically - excellent defaults, minimal overrides needed.

Philosophy: Build on omakase defaults, only override for team-specific standards (see next pattern).

Team-Specific Customizations

Customize the stock Rails configuration by adding overrides to .rubocop.yml:

# .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

RuleCopEnforcementAuto-correctable
Rule #16: Double QuotesStyle/StringLiteralsโœ… AlwaysYes
Rule #20: Hash#digStyle/HashFetchChain, Style/DigChainโœ… AlwaysYes
Rule #17: bin/ci Must PassRuboCop integrated in bin/ciโœ… CI blockerN/A

How it works:

  1. Style/StringLiterals - Enforces double quotes (Rule #16)

    # Bad (detected and auto-corrected)
    name = 'John'
    
    # Good
    name = "John"
    
    
  2. Style/HashFetchChain - Detects chained fetch calls (Rule #20)

    # Bad (detected)
    hash.fetch(:a, nil)&.fetch(:b, nil)
    
    # Good (suggested)
    hash.dig(:a, :b)
    
    
  3. Style/DigChain - Collapses chained dig calls (Rule #20)

    # Bad (detected)
    hash.dig(:a).dig(:b).dig(:c)
    
    # Good (suggested)
    hash.dig(:a, :b, :c)
    
    

Integration with bin/ci

Add RuboCop to bin/ci:

#!/usr/bin/env bash
set -e
bin/rails test
bin/rubocop              # Add this line
bin/brakeman -q

Usage:

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

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

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:

# โŒ 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:

# .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'

# 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

# GOOD: Build on Rails defaults
inherit_gem: { rubocop-rails-omakase: rubocop.yml }

# Only override team-specific rules
Style/StringLiterals:
  EnforcedStyle: double_quotes

# BAD: bin/ci without RuboCop
#!/usr/bin/env bash
bin/rails test  # Missing RuboCop check!

#!/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

GitHub Actions:

# .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


# 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

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:

Gems & Libraries:

Tools:

  • Kamal - Deploy web apps anywhere
  • RuboCop - Ruby static code analyzer

Community Resources: