rails-conventions

This skill should be used when the user is working in a Rails 7+ application and asks about "Rails conventions", "naming conventions", "Rails structure", "Hotwire patterns", "Turbo frames", "Stimulus

SKILL.md

GitHub

Rails 7+ Conventions for Production Systems

Production-focused guidance for Rails 7+ conventions, naming patterns, directory structure, and modern frontend integration with Hotwire.

Core Naming Conventions

Models

  • Class names: Singular, CamelCase (User, OrderItem, PaymentTransaction)
  • Table names: Plural, snake_case (users, order_items, payment_transactions)
  • Foreign keys: Singular model name + _id (user_id, order_id)
  • Join tables: Alphabetical order, plural (categories_products, roles_users)

Controllers

  • Class names: Plural, CamelCase + Controller (UsersController, Api::V1::OrdersController)
  • Files: Plural, snake_case (users_controller.rb, api/v1/orders_controller.rb)
  • RESTful actions: index, show, new, create, edit, update, destroy

Routes

Prefer resourceful routes over custom routes:

# Production pattern
resources :orders do
  resources :line_items, shallow: true
  member do
    post :cancel
    post :refund
  end
  collection do
    get :pending
  end
end

# API versioning
namespace :api do
  namespace :v1 do
    resources :orders, only: [:index, :show, :create]
  end
end

Views and Partials

  • Views: app/views/controller_name/action.html.erb
  • Partials: Prefix with underscore _partial.html.erb
  • Shared partials: app/views/shared/_partial.html.erb
  • Component partials: app/views/components/_button.html.erb

Directory Structure

Standard Rails 7 Layout

app/
├── assets/
│   └── stylesheets/
├── channels/           # ActionCable channels
├── controllers/
│   ├── concerns/       # Controller concerns
│   └── api/           # API controllers
├── helpers/
├── javascript/
│   └── controllers/   # Stimulus controllers
├── jobs/              # ActiveJob classes
├── mailers/
├── models/
│   └── concerns/      # Model concerns
├── views/
│   ├── layouts/
│   ├── shared/
│   └── components/    # View components (if using)
config/
├── initializers/
├── locales/
└── environments/
db/
├── migrate/
└── seeds.rb
lib/
├── tasks/             # Rake tasks
└── templates/         # Generator templates
spec/ or test/

Service Objects

Place in app/services/ with clear naming:

# app/services/orders/create_service.rb
module Orders
  class CreateService
    def initialize(user:, cart:)
      @user = user
      @cart = cart
    end

    def call
      # Implementation
    end
  end
end

# Usage: Orders::CreateService.new(user: current_user, cart: @cart).call

Query Objects

Place in app/queries/:

# app/queries/orders/pending_query.rb
module Orders
  class PendingQuery
    def initialize(relation = Order.all)
      @relation = relation
    end

    def call
      @relation.where(status: :pending)
               .where("created_at > ?", 24.hours.ago)
               .includes(:line_items, :user)
    end
  end
end

Hotwire Patterns (Rails 7+)

Turbo Frames

Use for partial page updates without full navigation:

<%# Index page with inline editing %>
<%= turbo_frame_tag "orders" do %>
  <% @orders.each do |order| %>
    <%= turbo_frame_tag dom_id(order) do %>
      <%= render order %>
    <% end %>
  <% end %>
<% end %>

<%# Edit form that replaces the frame %>
<%= turbo_frame_tag dom_id(@order) do %>
  <%= render "form", order: @order %>
<% end %>

Turbo Streams

Use for real-time updates and multi-element updates:

# Controller action
def create
  @order = Order.create(order_params)

  respond_to do |format|
    format.turbo_stream
    format.html { redirect_to orders_path }
  end
end
<%# create.turbo_stream.erb %>
<%= turbo_stream.prepend "orders", @order %>
<%= turbo_stream.update "order_count", Order.count %>

Stimulus Controllers

Naming convention: controller-name_controller.js

// app/javascript/controllers/dropdown_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["menu"]
  static values = { open: Boolean }

  toggle() {
    this.openValue = !this.openValue
  }

  openValueChanged() {
    this.menuTarget.classList.toggle("hidden", !this.openValue)
  }
}
<div data-controller="dropdown" data-dropdown-open-value="false">
  <button data-action="click->dropdown#toggle">Menu</button>
  <div data-dropdown-target="menu" class="hidden">
    <!-- Menu content -->
  </div>
</div>

Configuration Patterns

Credentials (Rails 7+)

# Edit credentials
bin/rails credentials:edit

# Environment-specific
bin/rails credentials:edit --environment production

Access pattern:

Rails.application.credentials.dig(:aws, :access_key_id)
Rails.application.credentials.stripe[:secret_key]

Environment Configuration

# config/environments/production.rb
Rails.application.configure do
  config.force_ssl = true
  config.log_level = :info
  config.active_job.queue_adapter = :sidekiq
end

Initializers

Name by feature, not gem:

# config/initializers/stripe.rb (not payments.rb)
Stripe.api_key = Rails.application.credentials.stripe[:secret_key]

Production Patterns

Strong Parameters

def order_params
  params.require(:order).permit(
    :shipping_address_id,
    :notes,
    line_items_attributes: [:id, :product_id, :quantity, :_destroy]
  )
end

Callbacks Best Practices

Avoid callback chains for business logic. Prefer service objects:

# Avoid
class Order < ApplicationRecord
  after_create :send_confirmation, :update_inventory, :notify_warehouse
end

# Prefer
class Orders::CreateService
  def call
    Order.transaction do
      order = Order.create!(params)
      OrderMailer.confirmation(order).deliver_later
      Inventory::DeductService.new(order).call
      Warehouse::NotifyJob.perform_later(order.id)
      order
    end
  end
end

Scopes

Define commonly used queries as scopes:

class Order < ApplicationRecord
  scope :recent, -> { where("created_at > ?", 30.days.ago) }
  scope :pending, -> { where(status: :pending) }
  scope :with_items, -> { includes(:line_items) }
  scope :for_user, ->(user) { where(user: user) }
end

Additional Resources

Reference Files

For detailed patterns and examples:

  • references/hotwire-patterns.md - Advanced Turbo and Stimulus patterns
  • references/api-conventions.md - API versioning, serialization, authentication patterns