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 controllers", "Rails directory structure", "Rails best practices", or needs guidance on idiomatic Rails patterns for production systems.
From ruby-on-railsnpx claudepluginhub betamatt/claude-plugins --plugin ruby-on-railsThis skill uses the workspace's default tool permissions.
references/api-conventions.mdreferences/hotwire-patterns.mdSearches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Guides implementation of event-driven hooks in Claude Code plugins using prompt-based validation and bash commands for PreToolUse, Stop, and session events.
Production-focused guidance for Rails 7+ conventions, naming patterns, directory structure, and modern frontend integration with Hotwire.
User, OrderItem, PaymentTransaction)users, order_items, payment_transactions)_id (user_id, order_id)categories_products, roles_users)UsersController, Api::V1::OrdersController)users_controller.rb, api/v1/orders_controller.rb)index, show, new, create, edit, update, destroyPrefer 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
app/views/controller_name/action.html.erb_partial.html.erbapp/views/shared/_partial.html.erbapp/views/components/_button.html.erbapp/
├── 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/
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
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
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 %>
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 %>
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>
# 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]
# config/environments/production.rb
Rails.application.configure do
config.force_ssl = true
config.log_level = :info
config.active_job.queue_adapter = :sidekiq
end
Name by feature, not gem:
# config/initializers/stripe.rb (not payments.rb)
Stripe.api_key = Rails.application.credentials.stripe[:secret_key]
def order_params
params.require(:order).permit(
:shipping_address_id,
:notes,
line_items_attributes: [:id, :product_id, :quantity, :_destroy]
)
end
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
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
For detailed patterns and examples:
references/hotwire-patterns.md - Advanced Turbo and Stimulus patternsreferences/api-conventions.md - API versioning, serialization, authentication patterns