mirror of
https://github.com/ryankazokas/turbovault-app.git
synced 2026-04-16 22:12:53 +00:00
12 KiB
12 KiB
TurboVault - Development Guide
Quick Start
# Start PostgreSQL
task docker:up
# Setup database (first time only)
task db:setup
# Start Rails server
task server
# Or use bin/dev for Tailwind watch mode
bin/dev
Visit http://localhost:3000 and create an account!
Development Workflow
Daily Development
# Start all services
task docker:up # PostgreSQL
bin/dev # Rails server + Tailwind watcher
Database Operations
# Create a new migration
rails generate migration AddFieldToModel field:type
# Run migrations
task db:migrate
# Rollback last migration
task db:rollback
# Reset database (drops, creates, migrates, seeds)
task db:reset
# Open Rails console
task console
# Check pending migrations
rails db:migrate:status
Generating Code
# Generate a model
rails generate model ModelName field:type
# Generate a controller
rails generate controller ControllerName action1 action2
# Generate a scaffold (model + controller + views)
rails generate scaffold ModelName field:type
# Destroy generated code
rails destroy model ModelName
Running Tests
# Run all tests
task test
# Run specific test file
rails test test/models/game_test.rb
# Run specific test
rails test test/models/game_test.rb:10
# Run system tests
task test:system
Code Quality
# Run RuboCop linter
task lint
# Auto-fix RuboCop issues
task lint:fix
# Security checks
task security
Project Structure
app/
├── controllers/
│ ├── concerns/ # Shared controller modules
│ ├── api/v1/ # API controllers
│ └── *.rb # Web controllers
├── models/
│ ├── concerns/ # Shared model modules
│ └── *.rb # ActiveRecord models
├── views/
│ ├── layouts/ # Application layouts
│ └── */ # Controller-specific views
├── helpers/ # View helpers
├── mailers/ # Email mailers
└── jobs/ # Background jobs
config/
├── routes.rb # URL routing
├── database.yml # Database configuration
└── environments/ # Environment-specific config
db/
├── migrate/ # Database migrations
├── seeds.rb # Seed data
└── schema.rb # Current database schema
test/
├── models/ # Model tests
├── controllers/ # Controller tests
├── system/ # End-to-end tests
└── fixtures/ # Test data
Common Tasks
Adding a New Model
- Generate the model:
rails generate model Post user:references title:string body:text published:boolean
- Edit the migration (add indexes, constraints, RLS if user-scoped):
class CreatePosts < ActiveRecord::Migration[8.1]
def change
create_table :posts do |t|
t.references :user, null: false, foreign_key: true, index: true
t.string :title, null: false
t.text :body
t.boolean :published, default: false, null: false
t.timestamps
end
add_index :posts, :title
# Enable RLS if user-scoped
execute <<-SQL
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
CREATE POLICY posts_isolation_policy ON posts
USING (user_id = current_setting('app.current_user_id', true)::bigint);
SQL
end
end
- Run the migration:
rails db:migrate
- Add associations and validations to the model:
class Post < ApplicationRecord
belongs_to :user
validates :title, presence: true
validates :body, presence: true
scope :published, -> { where(published: true) }
end
- Add association to User model:
class User < ApplicationRecord
has_many :posts, dependent: :destroy
end
Adding a New Controller
- Generate the controller:
rails generate controller Posts index show new create edit update destroy
- Implement controller actions:
class PostsController < ApplicationController
before_action :require_authentication
before_action :set_post, only: [:show, :edit, :update, :destroy]
def index
@posts = current_user.posts.order(created_at: :desc)
end
def show
end
def new
@post = current_user.posts.build
end
def create
@post = current_user.posts.build(post_params)
if @post.save
redirect_to @post, notice: "Post created successfully."
else
render :new, status: :unprocessable_entity
end
end
def edit
end
def update
if @post.update(post_params)
redirect_to @post, notice: "Post updated successfully."
else
render :edit, status: :unprocessable_entity
end
end
def destroy
@post.destroy
redirect_to posts_path, notice: "Post deleted successfully."
end
private
def set_post
@post = current_user.posts.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :body, :published)
end
end
- Add routes:
resources :posts
- Create views in
app/views/posts/
Adding an API Endpoint
- Create API controller:
mkdir -p app/controllers/api/v1
- Create controller file:
# app/controllers/api/v1/posts_controller.rb
module Api
module V1
class PostsController < BaseController
def index
@posts = current_user.posts.order(created_at: :desc)
render json: @posts
end
def show
@post = current_user.posts.find(params[:id])
render json: @post
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
private
def post_params
params.require(:post).permit(:title, :body, :published)
end
end
end
end
- Add API routes:
namespace :api do
namespace :v1 do
resources :posts, only: [:index, :show, :create]
end
end
Adding a Background Job
- Generate the job:
rails generate job ProcessData
- Implement the job:
class ProcessDataJob < ApplicationJob
queue_as :default
def perform(user_id)
user = User.find(user_id)
# Do some processing
end
end
- Enqueue the job:
ProcessDataJob.perform_later(user.id)
Adding Email Functionality
- Generate a mailer:
rails generate mailer UserMailer welcome
- Implement the mailer:
class UserMailer < ApplicationMailer
def welcome(user)
@user = user
mail(to: @user.email, subject: "Welcome to TurboVault!")
end
end
-
Create email templates in
app/views/user_mailer/ -
Configure SMTP in
config/environments/:
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: ENV['SMTP_ADDRESS'],
port: ENV['SMTP_PORT'],
user_name: ENV['SMTP_USERNAME'],
password: ENV['SMTP_PASSWORD'],
authentication: 'plain',
enable_starttls_auto: true
}
- Send the email:
UserMailer.welcome(user).deliver_later
Testing Guide
Writing Model Tests
# test/models/game_test.rb
require "test_helper"
class GameTest < ActiveSupport::TestCase
test "should not save game without title" do
game = Game.new
assert_not game.save, "Saved game without title"
end
test "should save valid game" do
game = games(:one) # Uses fixture
assert game.save, "Failed to save valid game"
end
test "should belong to user" do
game = games(:one)
assert_respond_to game, :user
end
end
Writing Controller Tests
# test/controllers/games_controller_test.rb
require "test_helper"
class GamesControllerTest < ActionDispatch::IntegrationTest
setup do
@user = users(:one)
sign_in_as @user # Helper method to sign in
@game = games(:one)
end
test "should get index" do
get games_url
assert_response :success
end
test "should create game" do
assert_difference('Game.count') do
post games_url, params: {
game: {
title: "New Game",
platform_id: platforms(:one).id,
format: "physical"
}
}
end
assert_redirected_to game_path(Game.last)
end
end
Writing System Tests
# test/system/games_test.rb
require "application_system_test_case"
class GamesTest < ApplicationSystemTestCase
setup do
@user = users(:one)
sign_in_as @user
end
test "visiting the index" do
visit games_url
assert_selector "h1", text: "My Games"
end
test "creating a game" do
visit games_url
click_on "Add Game"
fill_in "Title", with: "Test Game"
select "Nintendo 64", from: "Platform"
select "Physical", from: "Format"
click_on "Create Game"
assert_text "Game was successfully created"
end
end
Debugging
Rails Console
rails console
# Test queries
User.first
Game.where(format: :physical).count
current_user.games.includes(:platform)
# Test RLS
ActiveRecord::Base.connection.execute(
"SET LOCAL app.current_user_id = 1"
)
Logs
# Tail development log
tail -f log/development.log
# View specific log
cat log/test.log
Debug with Byebug
Add to your code:
require 'debug'
debugger # Execution will pause here
Then interact in the terminal:
n # Next line
c # Continue
p var # Print variable
exit # Exit debugger
Performance Tips
Avoid N+1 Queries
Bad:
@games = current_user.games
# In view: @games.each { |g| g.platform.name } # N+1!
Good:
@games = current_user.games.includes(:platform)
Use Database Indexes
add_index :games, :title
add_index :games, [:user_id, :platform_id]
Use Counter Caches
class Collection < ApplicationRecord
has_many :games, counter_cache: true
end
Pagination
@games = current_user.games.page(params[:page]).per(25)
Environment Variables
Create .env for development (never commit!):
DATABASE_HOST=localhost
DATABASE_USERNAME=postgres
DATABASE_PASSWORD=postgres
SMTP_ADDRESS=smtp.example.com
SMTP_USERNAME=user@example.com
SMTP_PASSWORD=secret
Load with:
# config/application.rb
config.before_configuration do
env_file = Rails.root.join('.env')
if File.exist?(env_file)
File.readlines(env_file).each do |line|
key, value = line.split('=', 2)
ENV[key.strip] = value.strip if key && value
end
end
end
Deployment
Kamal (Recommended)
Already configured! Just:
# First time setup
kamal setup
# Deploy
kamal deploy
# Check status
kamal app exec --interactive --reuse "bin/rails console"
Railway/Render
- Push to Git
- Connect repository
- Set environment variables
- Add build command:
bundle install && rails db:migrate - Add start command:
rails server -b 0.0.0.0
Troubleshooting
Database Connection Errors
# Check if PostgreSQL is running
docker compose ps
# Start PostgreSQL
task docker:up
# Check database configuration
cat config/database.yml
Asset Issues
# Rebuild assets
rails assets:precompile
# Rebuild Tailwind
rails tailwindcss:build
Migration Issues
# Check migration status
rails db:migrate:status
# Rollback and retry
rails db:rollback
rails db:migrate
Resources
- Rails Guides
- Rails API Documentation
- Tailwind CSS Docs
- Hotwire Documentation
- PostgreSQL Documentation
Getting Help
- Check the logs:
tail -f log/development.log - Use Rails console:
rails console - Check database:
rails dbconsole - Read the error message carefully
- Search Stack Overflow
- Check Rails Guides
Best Practices
- Always filter by current_user in controllers
- Use strong parameters for mass assignment
- Add validations to models
- Write tests for new features
- Keep controllers thin - move logic to models
- Use concerns for shared code
- Keep views simple - use helpers for complex logic
- Add indexes for frequently queried columns
- Use scopes for common queries
- Document your API endpoints
Happy coding! 🚀