This skill should be used when the user asks about "Rails conventions", "Rails best practices", "Rails file structure", "MVC patterns", "naming conventions", "Rails Way", "convention over configuration", "where to put code", "Rails architecture", or needs guidance on organizing Rails application code following established conventions.
/plugin marketplace add bastos/rails-plugin/plugin install bastos-ruby-on-rails@bastos/rails-pluginThis skill inherits all available tools. When active, it can use any tool Claude has access to.
references/advanced-patterns.mdreferences/code-organization.mdComprehensive guide to Ruby on Rails conventions, the Rails Way philosophy, and best practices for organizing Rails 7+ applications.
Rails follows "Convention over Configuration" - sensible defaults reduce decision fatigue and boilerplate. Learn the conventions to write less code and collaborate effectively.
app/
├── controllers/ # Handle HTTP requests
│ ├── concerns/ # Shared controller modules
│ └── application_controller.rb
├── models/ # Business logic and data
│ ├── concerns/ # Shared model modules
│ └── application_record.rb
├── views/ # Templates
│ ├── layouts/ # Page wrappers
│ └── shared/ # Partials used across views
├── helpers/ # View helper methods
├── mailers/ # Email logic
├── jobs/ # Background jobs (ActiveJob)
├── channels/ # ActionCable WebSocket channels
└── javascript/ # Hotwire/Stimulus (import maps)
└── controllers/ # Stimulus controllers
config/
├── routes.rb # URL routing
├── database.yml # Database configuration
├── credentials.yml.enc # Encrypted secrets
└── initializers/ # Startup configuration
db/
├── migrate/ # Database migrations
├── schema.rb # Current schema (auto-generated)
└── seeds.rb # Seed data
lib/
├── tasks/ # Custom Rake tasks
└── [custom modules] # Non-Rails-specific code
test/ or spec/ # Tests (Minitest or RSpec)
| Convention | Example | Notes |
|---|---|---|
| Class name | User, BlogPost | Singular, CamelCase |
| File name | user.rb, blog_post.rb | Singular, snake_case |
| Table name | users, blog_posts | Plural, snake_case |
| Foreign key | user_id, blog_post_id | Singular + _id |
| Convention | Example | Notes |
|---|---|---|
| Class name | UsersController | Plural + Controller |
| File name | users_controller.rb | Plural, snake_case |
| Actions | index, show, new, create, edit, update, destroy | RESTful |
| Convention | Example |
|---|---|
| Directory | app/views/users/ |
| Template | index.html.erb, show.html.erb |
| Partial | _user.html.erb, _form.html.erb |
| Layout | application.html.erb |
Standard resource routes map to controller actions:
# config/routes.rb
resources :articles do
resources :comments, only: [:create, :destroy]
end
| HTTP Verb | Path | Controller#Action | Purpose |
|---|---|---|---|
| GET | /articles | articles#index | List all |
| GET | /articles/new | articles#new | New form |
| POST | /articles | articles#create | Create |
| GET | /articles/:id | articles#show | Show one |
| GET | /articles/:id/edit | articles#edit | Edit form |
| PATCH/PUT | /articles/:id | articles#update | Update |
| DELETE | /articles/:id | articles#destroy | Delete |
Add member and collection routes when needed:
resources :articles do
member do
post :publish # POST /articles/:id/publish
end
collection do
get :drafts # GET /articles/drafts
end
end
class ArticlesController < ApplicationController
before_action :set_article, only: [:show, :edit, :update, :destroy]
def index
@articles = Article.all
end
def show; end
def new
@article = Article.new
end
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article, notice: "Article created."
else
render :new, status: :unprocessable_entity
end
end
def edit; end
def update
if @article.update(article_params)
redirect_to @article, notice: "Article updated."
else
render :edit, status: :unprocessable_entity
end
end
def destroy
@article.destroy
redirect_to articles_url, notice: "Article deleted."
end
private
def set_article
@article = Article.find(params[:id])
end
def article_params
params.require(:article).permit(:title, :body, :published)
end
end
Always whitelist permitted parameters:
def article_params
params.require(:article).permit(:title, :body, :published, tag_ids: [])
end
class Article < ApplicationRecord
# Constants first
STATUSES = %w[draft published archived].freeze
# Associations
belongs_to :author, class_name: "User"
has_many :comments, dependent: :destroy
has_many :taggings, dependent: :destroy
has_many :tags, through: :taggings
# Validations
validates :title, presence: true, length: { maximum: 255 }
validates :body, presence: true
validates :status, inclusion: { in: STATUSES }
# Callbacks (use sparingly)
before_validation :normalize_title
after_create :notify_subscribers
# Scopes
scope :published, -> { where(status: "published") }
scope :recent, -> { order(created_at: :desc) }
scope :by_author, ->(author) { where(author: author) }
# Class methods
def self.search(query)
where("title ILIKE ?", "%#{query}%")
end
# Instance methods
def publish!
update!(status: "published", published_at: Time.current)
end
private
def normalize_title
self.title = title&.strip&.titleize
end
def notify_subscribers
ArticleNotificationJob.perform_later(self)
end
end
Extract shared behavior into concerns:
# app/models/concerns/publishable.rb
module Publishable
extend ActiveSupport::Concern
included do
scope :published, -> { where.not(published_at: nil) }
scope :draft, -> { where(published_at: nil) }
end
def published?
published_at.present?
end
def publish!
update!(published_at: Time.current)
end
end
# app/models/article.rb
class Article < ApplicationRecord
include Publishable
end
For complex business logic, use service objects in app/services/:
# app/services/article_publisher.rb
class ArticlePublisher
def initialize(article, user:)
@article = article
@user = user
end
def call
return failure("Not authorized") unless @user.can_publish?(@article)
return failure("Already published") if @article.published?
ActiveRecord::Base.transaction do
@article.publish!
notify_subscribers
track_analytics
end
success(@article)
rescue StandardError => e
failure(e.message)
end
private
def notify_subscribers
# notification logic
end
def track_analytics
# analytics logic
end
def success(result)
OpenStruct.new(success?: true, result: result)
end
def failure(error)
OpenStruct.new(success?: false, error: error)
end
end
| Code Type | Location | Example |
|---|---|---|
| Business logic | Models or Services | Validation, calculations |
| HTTP handling | Controllers | Params, redirects, rendering |
| View formatting | Helpers or View Components | Date formatting, HTML helpers |
| Background work | Jobs | Email sending, API calls |
| Database queries | Models (scopes) | Complex queries |
| External APIs | lib/ or Services | API wrappers |
| Shared behavior | Concerns | Reusable modules |
references/advanced-patterns.md - Service objects, query objects, form objectsreferences/code-organization.md - When models get too bigThis skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.