Develop Ruby on Rails applications with models, controllers, views, Active Record ORM, authentication, and RESTful routes. Use when building Rails applications, managing database relationships, and implementing MVC architecture.
Scanned 2/12/2026
Install via CLI
openskills install aj-geddes/useful-ai-prompts---
name: ruby-rails-application
description: Develop Ruby on Rails applications with models, controllers, views, Active Record ORM, authentication, and RESTful routes. Use when building Rails applications, managing database relationships, and implementing MVC architecture.
---
# Ruby Rails Application
## Overview
Build comprehensive Ruby on Rails applications with proper model associations, RESTful controllers, Active Record queries, authentication systems, middleware chains, and view rendering following Rails conventions.
## When to Use
- Building Rails web applications
- Implementing Active Record models with associations
- Creating RESTful controllers and actions
- Integrating authentication and authorization
- Building complex database relationships
- Implementing Rails middleware and filters
## Instructions
### 1. **Rails Project Setup**
```bash
rails new myapp --api --database=postgresql
cd myapp
rails db:create
```
### 2. **Models with Active Record**
```ruby
# app/models/user.rb
class User < ApplicationRecord
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
enum role: { user: 0, admin: 1 }
validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }
validates :password, presence: true, length: { minimum: 8 }, if: :new_record?
validates :first_name, :last_name, presence: true
has_secure_password
before_save :downcase_email
def full_name
"#{first_name} #{last_name}"
end
def active?
is_active
end
private
def downcase_email
self.email = email.downcase
end
end
# app/models/post.rb
class Post < ApplicationRecord
belongs_to :user
has_many :comments, dependent: :destroy
enum status: { draft: 0, published: 1, archived: 2 }
validates :title, presence: true, length: { minimum: 1, maximum: 255 }
validates :content, presence: true, length: { minimum: 1 }
validates :user_id, presence: true
scope :published, -> { where(status: :published) }
scope :recent, -> { order(created_at: :desc) }
scope :by_author, ->(user_id) { where(user_id: user_id) }
def publish!
update(status: :published)
end
def unpublish!
update(status: :draft)
end
end
# app/models/comment.rb
class Comment < ApplicationRecord
belongs_to :user
belongs_to :post
validates :content, presence: true, length: { minimum: 1 }
validates :user_id, :post_id, presence: true
scope :recent, -> { order(created_at: :desc) }
scope :by_author, ->(user_id) { where(user_id: user_id) }
end
```
### 3. **Database Migrations**
```ruby
# db/migrate/20240101120000_create_users.rb
class CreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :email, null: false
t.string :password_digest, null: false
t.string :first_name, null: false
t.string :last_name, null: false
t.integer :role, default: 0
t.boolean :is_active, default: true
t.timestamps
end
add_index :users, :email, unique: true
add_index :users, :role
end
end
# db/migrate/20240101120001_create_posts.rb
class CreatePosts < ActiveRecord::Migration[7.0]
def change
create_table :posts do |t|
t.string :title, null: false
t.text :content, null: false
t.integer :status, default: 0
t.references :user, null: false, foreign_key: true
t.timestamps
end
add_index :posts, :status
add_index :posts, [:user_id, :status]
end
end
# db/migrate/20240101120002_create_comments.rb
class CreateComments < ActiveRecord::Migration[7.0]
def change
create_table :comments do |t|
t.text :content, null: false
t.references :user, null: false, foreign_key: true
t.references :post, null: false, foreign_key: true
t.timestamps
end
add_index :comments, [:post_id, :created_at]
add_index :comments, [:user_id, :created_at]
end
end
```
### 4. **Controllers with RESTful Actions**
```ruby
# app/controllers/api/v1/users_controller.rb
module Api
module V1
class UsersController < ApplicationController
before_action :authenticate_request, except: [:create]
before_action :set_user, only: [:show, :update, :destroy]
before_action :authorize_user!, only: [:update, :destroy]
def index
users = User.all
users = users.where("email ILIKE ?", "%#{params[:q]}%") if params[:q].present?
users = users.page(params[:page]).per(params[:limit] || 20)
render json: {
data: users,
pagination: pagination_data(users)
}
end
def show
render json: @user
end
def create
user = User.new(user_params)
if user.save
token = encode_token(user.id)
render json: {
user: user,
token: token
}, status: :created
else
render json: { errors: user.errors.full_messages }, status: :unprocessable_entity
end
end
def update
if @user.update(user_params)
render json: @user
else
render json: { errors: @user.errors.full_messages }, status: :unprocessable_entity
end
end
def destroy
@user.destroy
head :no_content
end
private
def set_user
@user = User.find(params[:id])
rescue ActiveRecord::RecordNotFound
render json: { error: 'User not found' }, status: :not_found
end
def authorize_user!
unless current_user.id == @user.id || current_user.admin?
render json: { error: 'Unauthorized' }, status: :forbidden
end
end
def user_params
params.require(:user).permit(:email, :password, :first_name, :last_name)
end
def pagination_data(collection)
{
page: collection.current_page,
per_page: collection.limit_value,
total: collection.total_count,
total_pages: collection.total_pages
}
end
end
end
end
# app/controllers/api/v1/posts_controller.rb
module Api
module V1
class PostsController < ApplicationController
before_action :authenticate_request, except: [:index, :show]
before_action :set_post, only: [:show, :update, :destroy, :publish]
before_action :authorize_post_owner!, only: [:update, :destroy, :publish]
def index
posts = Post.published.recent
posts = posts.by_author(params[:author_id]) if params[:author_id].present?
posts = posts.where("title ILIKE ?", "%#{params[:q]}%") if params[:q].present?
posts = posts.page(params[:page]).per(params[:limit] || 20)
render json: {
data: posts,
pagination: pagination_data(posts)
}
end
def show
if @post.published? || current_user&.id == @post.user_id
render json: @post
else
render json: { error: 'Post not found' }, status: :not_found
end
end
def create
@post = current_user.posts.build(post_params)
if @post.save
render json: @post, status: :created
else
render json: { errors: @post.errors.full_messages }, status: :unprocessable_entity
end
end
def update
if @post.update(post_params)
render json: @post
else
render json: { errors: @post.errors.full_messages }, status: :unprocessable_entity
end
end
def destroy
@post.destroy
head :no_content
end
def publish
@post.publish!
render json: @post
end
private
def set_post
@post = Post.find(params[:id])
rescue ActiveRecord::RecordNotFound
render json: { error: 'Post not found' }, status: :not_found
end
def authorize_post_owner!
unless current_user.id == @post.user_id || current_user.admin?
render json: { error: 'Unauthorized' }, status: :forbidden
end
end
def post_params
params.require(:post).permit(:title, :content, :status)
end
def pagination_data(collection)
{
page: collection.current_page,
per_page: collection.limit_value,
total: collection.total_count
}
end
end
end
end
```
### 5. **Authentication with JWT**
```ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::API
include ActionController::Cookies
SECRET_KEY = Rails.application.secrets.secret_key_base
def encode_token(user_id)
payload = { user_id: user_id, exp: 24.hours.from_now.to_i }
JWT.encode(payload, SECRET_KEY, 'HS256')
end
def decode_token(token)
begin
JWT.decode(token, SECRET_KEY, true, { algorithm: 'HS256' })
rescue JWT::ExpiredSignature, JWT::DecodeError
nil
end
end
def authenticate_request
header = request.headers['Authorization']
token = header.split(' ').last if header.present?
decoded = decode_token(token)
if decoded
@current_user_id = decoded[0]['user_id']
@current_user = User.find(@current_user_id)
else
render json: { error: 'Unauthorized' }, status: :unauthorized
end
end
def current_user
@current_user
end
def logged_in?
current_user.present?
end
end
# config/routes.rb
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
post 'auth/login', to: 'auth#login'
post 'auth/register', to: 'auth#register'
resources :users
resources :posts do
member do
patch :publish
end
resources :comments, only: [:index, :create, :destroy]
end
end
end
end
```
### 6. **Active Record Queries**
```ruby
# app/services/post_service.rb
class PostService
def self.get_user_posts(user_id, status: nil)
posts = Post.by_author(user_id)
posts = posts.where(status: status) if status.present?
posts.recent
end
def self.trending_posts(limit: 10)
Post.published
.joins(:comments)
.group('posts.id')
.order('COUNT(comments.id) DESC')
.limit(limit)
end
def self.search_posts(query)
Post.published
.where("title ILIKE ? OR content ILIKE ?", "%#{query}%", "%#{query}%")
.recent
end
def self.archive_old_drafts(days: 30)
Post.where(status: :draft)
.where('created_at < ?', days.days.ago)
.update_all(status: :archived)
end
end
# Usage
posts = Post.includes(:user).recent.limit(10)
recent_comments = Comment.where(post_id: post.id).order(created_at: :desc).limit(5)
```
### 7. **Serializers**
```ruby
# app/serializers/user_serializer.rb
class UserSerializer
def initialize(user)
@user = user
end
def to_json
{
id: @user.id,
email: @user.email,
first_name: @user.first_name,
last_name: @user.last_name,
full_name: @user.full_name,
role: @user.role,
is_active: @user.is_active,
created_at: @user.created_at.iso8601,
updated_at: @user.updated_at.iso8601
}
end
end
# In controller
def show
render json: UserSerializer.new(@user).to_json
end
```
## Best Practices
### ✅ DO
- Use conventions over configuration
- Leverage Active Record associations
- Implement proper scopes for queries
- Use strong parameters for security
- Implement authentication in ApplicationController
- Use services for complex business logic
- Implement proper error handling
- Use database migrations for schema changes
- Validate all inputs at model level
- Use before_action filters appropriately
### ❌ DON'T
- Use raw SQL without parameterization
- Implement business logic in controllers
- Trust user input without validation
- Store secrets in code
- Use select * without specifying columns
- Forget N+1 query problems (use includes/joins)
- Implement authentication in each controller
- Use global variables
- Ignore database constraints
## Complete Example
```ruby
# Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 7.0.0'
gem 'pg', '~> 1.1'
gem 'bcrypt', '~> 3.1.7'
gem 'jwt'
gem 'kaminari'
# models.rb + controllers.rb (see sections above)
# routes.rb and migrations (see sections above)
```
No comments yet. Be the first to comment!