Claude Code Plugins

Community-maintained marketplace

Feedback

ruby-rails-application

@aj-geddes/useful-ai-prompts
4
0

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.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

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

rails new myapp --api --database=postgresql
cd myapp
rails db:create

2. Models with Active Record

# 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

# 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

# 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

# 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

# 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

# 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

# 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)