ruby-rails-application

安装量: 232
排名: #3784

安装

npx skills add https://github.com/aj-geddes/useful-ai-prompts --skill ruby-rails-application

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

  1. 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

  1. 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

  1. 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

  1. 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

  1. 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)

  1. 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)

返回排行榜