Rails controllers
Skill Shoebtamboli/rails_claude_skills/lib/generators/claude/skills_library/rails-controllers
A Rails generator gem that scaffolds Claude AI skills and agents for any Rails project.
npx -y skills add Shoebtamboli/rails_claude_skills --skill rails-controllersAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 14 stars14 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Controller actions, routing, REST conventions, filters, and response handling
SKILL.md
10.8 KB, as published. Nobody here has run it
Rails Controllers
Quick Reference
| Pattern | Example |
|---|---|
| Generate | rails g controller Posts index show |
| Route | resources :posts |
| Action | def show; @post = Post.find(params[:id]); end |
| Render | render :edit |
| Redirect | redirect_to posts_path |
| Filter | before_action :authenticate_user! |
| Strong Params | params.require(:post).permit(:title, :body) |
Controller Structure
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
# GET /posts
def index
@posts = Post.all.order(created_at: :desc)
end
# GET /posts/:id
def show
# @post set by before_action
end
# GET /posts/new
def new
@post = Post.new
end
# POST /posts
def create
@post = Post.new(post_params)
if @post.save
redirect_to @post, notice: 'Post created successfully.'
else
render :new, status: :unprocessable_entity
end
end
# GET /posts/:id/edit
def edit
# @post set by before_action
end
# PATCH/PUT /posts/:id
def update
if @post.update(post_params)
redirect_to @post, notice: 'Post updated successfully.'
else
render :edit, status: :unprocessable_entity
end
end
# DELETE /posts/:id
def destroy
@post.destroy
redirect_to posts_path, notice: 'Post deleted successfully.'
end
private
def set_post
@post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :body, :published)
end
end
Routing
RESTful Routes
# config/routes.rb
Rails.application.routes.draw do
# Creates 7 standard routes (index, show, new, create, edit, update, destroy)
resources :posts
# Limit actions
resources :posts, only: [:index, :show]
resources :posts, except: [:destroy]
# Nested resources
resources :authors do
resources :posts
end
# URLs: /authors/:author_id/posts
# Shallow nesting (recommended for deep nesting)
resources :authors do
resources :posts, shallow: true
end
# URLs: /authors/:author_id/posts (collection)
# /posts/:id (member)
# Custom member and collection routes
resources :posts do
member do
post :publish
post :unpublish
end
collection do
get :archived
end
end
# URLs: POST /posts/:id/publish
# GET /posts/archived
# Singular resource
resource :profile, only: [:show, :edit, :update]
# URLs: /profile (no :id needed)
end
Custom Routes
# Named routes
get 'about', to: 'pages#about', as: :about
# Usage: about_path
# Root route
root 'posts#index'
# Redirect
get '/old-path', to: redirect('/new-path')
# Constraints
get 'posts/:id', to: 'posts#show', constraints: { id: /\d+/ }
# Namespace
namespace :admin do
resources :posts
end
# URLs: /admin/posts
# Controller: Admin::PostsController
# Scope
scope module: 'admin' do
resources :posts
end
# URLs: /posts
# Controller: Admin::PostsController
# Concern for reusable routes
concern :commentable do
resources :comments
end
resources :posts, concerns: :commentable
resources :photos, concerns: :commentable
Filters (Callbacks)
class ApplicationController < ActionController::Base
before_action :authenticate_user!
before_action :set_locale
around_action :log_request
after_action :track_analytics
private
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
def log_request
start_time = Time.current
yield
duration = Time.current - start_time
Rails.logger.info "Request took #{duration}s"
end
end
class PostsController < ApplicationController
skip_before_action :authenticate_user!, only: [:index, :show]
before_action :set_post, only: [:show, :edit, :update, :destroy]
before_action :authorize_post, only: [:edit, :update, :destroy]
private
def authorize_post
unless @post.author == current_user
redirect_to root_path, alert: 'Not authorized'
end
end
end
Strong Parameters
class PostsController < ApplicationController
private
# Basic
def post_params
params.require(:post).permit(:title, :body, :published)
end
# Arrays
def post_params
params.require(:post).permit(:title, :body, tag_ids: [])
end
# Nested attributes
def post_params
params.require(:post).permit(
:title,
:body,
comments_attributes: [:id, :content, :_destroy]
)
end
# Conditional
def post_params
permitted = [:title, :body]
permitted << :published if current_user.admin?
params.require(:post).permit(permitted)
end
end
Rendering and Redirecting
class PostsController < ApplicationController
def show
@post = Post.find(params[:id])
# Implicit render: renders views/posts/show.html.erb
# Explicit template
# render :show
# Different template
# render :custom_template
# Partial
# render partial: 'post', locals: { post: @post }
# JSON
# render json: @post
# Plain text
# render plain: "Hello"
# Status codes
# render :show, status: :ok
# render :new, status: :unprocessable_entity
# render json: { error: 'Not found' }, status: :not_found
end
def create
@post = Post.new(post_params)
if @post.save
# Redirect to show page
redirect_to @post
# With flash message
# redirect_to @post, notice: 'Created!'
# With custom path
# redirect_to posts_path
# Back to previous page
# redirect_back(fallback_location: root_path)
else
render :new, status: :unprocessable_entity
end
end
end
Flash Messages
class PostsController < ApplicationController
def create
@post = Post.new(post_params)
if @post.save
# Set flash for next request
flash[:notice] = 'Post created!'
# Or shorter:
redirect_to @post, notice: 'Post created!'
# Different flash types
# flash[:success] = 'Success!'
# flash[:error] = 'Error!'
# flash[:alert] = 'Alert!'
# flash[:warning] = 'Warning!'
else
# flash.now for current request
flash.now[:alert] = 'Could not create post'
render :new
end
end
def update
# Keep flash for next request
flash.keep
redirect_to @post
end
end
Response Formats
class PostsController < ApplicationController
def show
@post = Post.find(params[:id])
respond_to do |format|
format.html # renders show.html.erb
format.json { render json: @post }
format.xml { render xml: @post }
format.pdf { render pdf: generate_pdf(@post) }
end
end
def create
@post = Post.new(post_params)
respond_to do |format|
if @post.save
format.html { redirect_to @post, notice: 'Created!' }
format.json { render json: @post, status: :created }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
end
Controller Concerns
# app/controllers/concerns/authenticatable.rb
module Authenticatable
extend ActiveSupport::Concern
included do
before_action :authenticate_user!
helper_method :current_user, :logged_in?
end
def current_user
@current_user ||= User.find_by(id: session[:user_id])
end
def logged_in?
current_user.present?
end
def authenticate_user!
unless logged_in?
redirect_to login_path, alert: 'Please log in'
end
end
end
# Usage
class PostsController < ApplicationController
include Authenticatable
end
Error Handling
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
rescue_from ActionController::ParameterMissing, with: :parameter_missing
private
def record_not_found
render file: "#{Rails.root}/public/404.html", status: :not_found
end
def parameter_missing
render json: { error: 'Missing parameter' }, status: :bad_request
end
end
class PostsController < ApplicationController
def show
@post = Post.find(params[:id])
rescue ActiveRecord::RecordNotFound
redirect_to posts_path, alert: 'Post not found'
end
end
Session and Cookies
class SessionsController < ApplicationController
def create
user = User.find_by(email: params[:email])
if user&.authenticate(params[:password])
# Set session
session[:user_id] = user.id
# Set cookie
cookies[:user_name] = user.name
# Signed cookie (tamper-proof)
cookies.signed[:user_id] = user.id
# Encrypted cookie
cookies.encrypted[:user_data] = { id: user.id, role: user.role }
# Permanent cookie (20 years)
cookies.permanent[:remember_token] = user.remember_token
redirect_to root_path
else
flash.now[:alert] = 'Invalid credentials'
render :new
end
end
def destroy
session.delete(:user_id)
cookies.delete(:user_name)
redirect_to root_path
end
end
Best Practices
- Keep controllers thin - Move business logic to models or service objects
- Use before_action for common setup code
- Always use strong parameters for security
- Return proper HTTP status codes
- Use concerns for shared controller behavior
- Follow REST conventions when possible
- Handle errors gracefully with rescue_from
- Use flash messages for user feedback
- Set instance variables only for view rendering
- Avoid complex queries in controllers - use scopes or query objects
Common Patterns
Service Objects for Complex Actions
class PostsController < ApplicationController
def create
result = Posts::CreateService.call(
params: post_params,
user: current_user
)
if result.success?
redirect_to result.post, notice: 'Created!'
else
@post = result.post
flash.now[:alert] = result.error
render :new
end
end
end
Query Objects for Complex Queries
class PostsController < ApplicationController
def index
@posts = PostsQuery.new(params).call
end
end