Ruby on Rails Agent Rules
Project Context
You are building Rails 7.1+ applications. Follow Rails conventions, write idiomatic Ruby, and use Hotwire (Turbo + Stimulus) for interactive UIs without heavy JavaScript. Use Solid Queue (Rails 8) or Sidekiq for background jobs.
Code Style
- Add `# frozen_string_literal: true` to every file.
- Write idiomatic Ruby: guard clauses, blocks, safe navigation (`&.`), symbol-to-proc (`&:method`).
- Keep methods under 10 lines; extract private methods with intent-revealing names.
- Run Rubocop with the project's `.rubocop.yml` — fix all offenses before committing.
- Use `pp` or Rails' `logger.debug` for debugging; never leave `puts` in committed code.
Models
- Define all validations at the model level: `validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }`.
- Use named scopes for reusable query fragments: `scope :active, -> { where(active: true) }`.
- Add database-level constraints (NOT NULL, unique indexes) that mirror model validations — both layers must agree.
- Use `enum status: { draft: 0, published: 1, archived: 2 }` with explicit integer values that will not shift on reorder.
- Keep callbacks minimal; avoid `after_save` side effects that call external services — use service objects.
- Use `counter_cache: true` on `belongs_to` associations for frequently counted collections.
Controllers
- Keep controllers thin: one public action calls one service or model operation.
- Use `before_action :set_order, only: [:show, :update, :destroy]` to DRY up resource loading.
- Use strong parameters in a dedicated private method: `def order_params; params.require(:order).permit(:status, :notes); end`.
- Use `rescue_from ActiveRecord::RecordNotFound, with: :not_found` in `ApplicationController`.
- Apply Pundit or Action Policy for authorization; call `authorize @order` in every action that touches a record.
- Respond with proper HTTP status codes: `render json: @order, status: :created` for POST.
Views & Hotwire
- Use Turbo Frames for in-place updates (inline edit, pagination, tabbed content): `<%= turbo_frame_tag dom_id(@order) do %>`.
- Use Turbo Streams for real-time multi-element updates from controllers: `render turbo_stream: turbo_stream.replace(@order, partial: "order")`.
- Use Stimulus controllers for client-side behavior Turbo cannot handle — keep Stimulus controllers small and single-purpose.
- Build reusable UI components with ViewComponent (`app/components/`) to avoid deeply nested partials.
- Keep ERB templates logic-free; extract conditionals and formatting into helpers or presenter objects.
Background Jobs
- Use job arguments as IDs, not full objects: `NotifyUserJob.perform_later(user_id: user.id)`.
- Write jobs idempotent — they must succeed even if run multiple times with the same arguments.
- Use `retry_on NetworkError, wait: :polynomially_longer, attempts: 5`.
- Use `discard_on ActiveRecord::RecordNotFound` to skip jobs for deleted records.
- Assign queue names explicitly: `queue_as :critical`, `queue_as :default`, `queue_as :low_priority`.
Database
- Write reversible migrations using `change` method; use `up`/`down` only when reversal is complex.
- Add indexes for all foreign keys and columns used in `WHERE` clauses: `add_index :orders, :user_id`.
- Use `add_foreign_key` for referential integrity at the database level.
- Prevent N+1 queries with `includes`, `eager_load`, or `preload`; use the Bullet gem in development.
- Use `DB::transaction { }` for multi-step operations that must be atomic.
- Use `strong_migrations` gem to catch migrations that could lock production tables (adding NOT NULL columns, etc.).
Testing
- Use FactoryBot factories with minimal attributes; define traits for common variants: `trait :admin { role :admin }`.
- Write request specs for API endpoints: test status codes, response shape, and database side effects.
- Write system tests (Capybara + Selenium/Cuprite) for critical user flows: sign up, checkout, key feature paths.
- Use `shoulda-matchers` for concise model association and validation specs.
- Use `build_stubbed` over `create` when database persistence is not needed — it is 10x faster.
- Keep the test database clean between tests with `DatabaseCleaner` using the transaction strategy.
Security
- Use `rack-attack` to rate-limit login, registration, and password reset endpoints.
- Always use parameterized queries — never interpolate user input into SQL strings.
- Set `config.force_ssl = true` in production; configure HSTS headers.
- Use `protect_from_forgery` for HTML forms; disable only for stateless API-only controllers.
- Store secrets in `credentials.yml.enc` (Rails encrypted credentials) or environment variables — never in plain text.