homeschool-master-docs

Homeschool Master - Rails API Implementation Guide

Technical implementation specification for the Homeschool Master Rails API backend.

Philosophy: Each phase is self-contained. At the end of every phase, the application compiles, all tests pass.

Table of Contents


Phase 0: Project Foundation

Goal

Set up a working Rails API with PostgreSQL, UUID primary keys, CORS, linting, and testing infrastructure. At the end of this phase, the server starts and responds to a health check.


Dependencies

Gem Purpose Why This Phase Why This Gem
rails Web framework Foundation of the entire API. Only choice for a Rails API.
pg PostgreSQL adapter Need to connect to PostgreSQL database. Only maintained PostgreSQL adapter for Rails.
puma Web server Need to serve HTTP requests. Rails default, handles concurrent requests well. Falcon is faster but less battle-tested.
rack-cors CORS middleware Mobile app runs on different origin than API—browsers block cross-origin requests without CORS headers. Standard choice, simple configuration.
dotenv-rails Environment variable loading Need to keep secrets out of code, load from .env files in development. Simple and widely used. Alternatives like figaro work but dotenv is more common.
rspec-rails Testing framework Need to write tests from day one. More expressive than Minitest, better community resources. Minitest is lighter but less flexible.
factory_bot_rails Test data factories Need to create test records without fixtures. Industry standard for Rails. Fabrication is similar but less popular.
faker Fake data generation Need realistic test data (names, emails, etc.). Most comprehensive fake data library.
rubocop-rails Code linting Enforce consistent code style from the start. Standard Ruby linter with Rails-specific rules.
shoulda-matchers Model test helpers Simplifies testing validations and associations. Makes model specs much cleaner. No real alternative.
database_cleaner-active_record Test database cleanup Need clean database state between tests. Standard choice. Rails transactional tests work but can have edge cases.

PostgreSQL Setup


CORS Configuration


Environment Variables

Variable Description
DATABASE_URL PostgreSQL connection string
APP_HOST API host URL (http://localhost:3000)
FRONTEND_URL Mobile app URL (http://localhost:8081)

Directory Structure

app/
  controllers/
    api/
      v1/
        base_controller.rb
  models/
  services/

Base Controller

Location: app/controllers/api/v1/base_controller.rb

Phase 0 version has response helpers only. No authentication (added in Phase 1).

Required Capabilities:

Response Type Status Structure
Success 200 { success: true, data: ..., meta?: ... }
Created 201 { success: true, data: ... }
No Content 204 Empty
Error varies { success: false, error: { code: ..., message: ..., details?: ... } }
Unauthorized 401 Error with code ‘UNAUTHORIZED’
Forbidden 403 Error with code ‘FORBIDDEN’
Not Found 404 Error with code ‘NOT_FOUND’
Validation Error 422 Error with code ‘VALIDATION_ERROR’, details from model errors

Routes

Phase 0 only defines:


Testing Requirements

Project Setup:

CORS:

Health Endpoint:

Base Controller Response Helpers:


Validation Checklist


Phase 1: Authentication System

Goal

Implement complete authentication with JWT tokens, teacher registration, login, logout, password reset, and email verification. At the end of this phase, users can register, log in, and access protected endpoints.


Dependencies

Gem Purpose Why This Phase Why This Gem
bcrypt Password hashing for has_secure_password Teacher model needs secure password storage for registration and login. Rails’ built-in has_secure_password expects bcrypt. It’s the standard—no real alternatives here.
jwt Encode/decode JSON Web Tokens Authentication requires issuing access tokens and refresh tokens to clients. The most popular Ruby JWT library. jose is an alternative but more complex and overkill for simple token auth.

Environment Variables

Variable Description Default
JWT_SECRET_KEY Secret for signing tokens (required)
JWT_ACCESS_TOKEN_EXPIRY Access token lifetime in seconds 3600
JWT_REFRESH_TOKEN_EXPIRY Refresh token lifetime in seconds 2592000

Database Schema

Teachers Table:

Column Type Constraints
id uuid Primary key
first_name string Not null
last_name string Not null
nickname string Nullable
email string Not null, unique
phone string Nullable
newsletter_subscribed boolean Default: false
password_digest string Not null
profile_image_url string Nullable
email_verified_at datetime Nullable
email_verification_token string Unique, nullable
password_reset_token string Unique, nullable
password_reset_sent_at datetime Nullable
is_active boolean Default: true
created_at datetime Not null
updated_at datetime Not null

Indexes:


Refresh Tokens Table:

Column Type Constraints
id uuid Primary key
teacher_id uuid Foreign key, not null
token string Not null, unique
jti string Not null, unique
expires_at datetime Not null
revoked_at datetime Nullable
created_at datetime Not null
updated_at datetime Not null

Indexes:

Foreign Keys:


JWT Service

Location: app/services/jwt_service.rb

Required Capabilities:

Configuration:


Teacher Model

Behaviors:

Validations:

Field Rules
first_name presence, max length 100
last_name presence, max length 100
nickname max length 100 (optional)
email presence, uniqueness (case insensitive), valid format
password minimum 8 characters (on create or when present)
phone max length 20 (optional)

Required Capabilities:


Refresh Token Model

Behaviors:

Validations:

Field Rules
token presence, uniqueness
jti presence, uniqueness
expires_at presence

Required Capabilities:


Base Controller (Updated)

Add authentication to the base controller.

New Behaviors:

Authentication Logic:

  1. Extract token from Authorization header (Bearer scheme)
  2. Decode token using JWT service
  3. Find teacher by decoded teacher_id
  4. Verify teacher exists and is active
  5. Return 401 for any failure

Routes

Add to api/v1 namespace under auth namespace:

Method Path Controller#Action
POST /auth/register authentication#register
POST /auth/login authentication#login
POST /auth/refresh authentication#refresh
POST /auth/logout authentication#logout
POST /auth/password/reset-request passwords#reset_request
POST /auth/password/reset passwords#reset
POST /auth/password/change passwords#change
POST /auth/email/verify email_verification#verify
POST /auth/email/resend-verification email_verification#resend

Authentication Controller

Location: app/controllers/api/v1/auth/authentication_controller.rb

Skip authentication for: register, login, refresh, logout

Endpoints:

Endpoint Request Success Errors
register first_name, last_name, email, password 201, teacher data (no password_digest) 422 validation errors
login email, password 200, access_token + refresh_token 401 invalid credentials, 401 inactive
refresh refresh_token 200, new access_token 401 invalid/expired/revoked
logout refresh_token 204 401 invalid token

Login Flow:

  1. Find teacher by email
  2. Verify password
  3. Check account is active
  4. Generate access token with teacher_id
  5. Generate refresh token
  6. Store refresh token in database (token string, jti, expires_at)
  7. Return both tokens

Refresh Flow:

  1. Decode refresh token
  2. Find RefreshToken record by jti
  3. Verify not revoked and not expired
  4. Generate new access token
  5. Return new access token

Passwords Controller

Location: app/controllers/api/v1/auth/passwords_controller.rb

Skip authentication for: reset_request, reset

Endpoints:

Endpoint Request Success Errors
reset_request email 200 (always, for security) -
reset token, password 200 400 invalid/expired token
change current_password, new_password 200 401 wrong current password

Note: change requires authentication


Email Verification Controller

Location: app/controllers/api/v1/auth/email_verification_controller.rb

Skip authentication for: verify

Endpoints:

Endpoint Request Success Errors
verify token 200 400 invalid token
resend - 200 -

Note: resend requires authentication


Testing Requirements

JWT Service:

Teacher Model:

Refresh Token Model:

Base Controller Authentication:

Authentication Controller:

Passwords Controller:

Email Verification Controller:


Validation Checklist


Phase 2: Student Management

Goal

Add student profiles linked to teachers. Teachers can create, view, update, and delete their students.


Dependencies

Gem Purpose Why This Phase Why This Gem
kaminari Pagination for ActiveRecord collections Students index is the first list endpoint—can’t return unbounded results to mobile clients. Clean API, well-maintained, plays nice with Rails. Pagy is faster but more manual setup. Will-paginate is older and less actively maintained.

Database Schema

Students Table:

Column Type Constraints
id uuid Primary key
teacher_id uuid Foreign key, not null
first_name string Not null
last_name string Not null
nickname string Nullable
date_of_birth date Nullable
grade_level string Nullable
profile_image_url string Nullable
notes text Nullable
is_active boolean Default: true
created_at datetime Not null
updated_at datetime Not null

Indexes:

Foreign Keys:


Model Updates

Teacher: Add association to students (destroyed when teacher deleted)


Student Model

Behaviors:

Validations:

Field Rules
first_name presence, max length 100
last_name presence, max length 100
nickname max length 100 (optional)
grade_level max length 50 (optional)

Required Capabilities:


Base Controller (Updated)

Add pagination helper.

Pagination Behavior:


Routes

Add to api/v1 namespace:


Students Controller

Location: app/controllers/api/v1/students_controller.rb

Endpoints:

Method Path Description
GET /students List current teacher’s students (paginated)
GET /students/:id Show student details
POST /students Create student for current teacher
PATCH /students/:id Update student
DELETE /students/:id Delete student

Index Filters:

Authorization: All actions scoped to current teacher’s students only


Testing Requirements

Student Model:

Students Controller:

Pagination Helper:


Validation Checklist


Phase 3: Subjects & Calendar

Goal

Add subjects and calendar events. Teachers can create subjects (like “Math” or “Science”) and schedule events on the calendar.


Dependencies

None—all required gems already installed.


Database Schema

Subjects Table:

Column Type Constraints
id uuid Primary key
teacher_id uuid Foreign key, not null
name string Not null
color string Nullable
description text Nullable
is_active boolean Default: true
created_at datetime Not null
updated_at datetime Not null

Indexes:


Calendar Events Table:

Column Type Constraints
id uuid Primary key
teacher_id uuid Foreign key, not null
student_id uuid Foreign key, nullable
subject_id uuid Foreign key, nullable
title string Not null
description text Nullable
start_time datetime Not null
end_time datetime Not null
all_day boolean Default: false
recurrence_rule string Nullable
location string Nullable
created_at datetime Not null
updated_at datetime Not null

Indexes:

Foreign Keys:


Model Updates

Teacher: Add associations to subjects and calendar events (destroyed when teacher deleted)

Student: Add association to calendar events (set null when student deleted)


Subject Model

Behaviors:

Validations:

Field Rules
name presence, max length 100, unique per teacher
color valid hex format (optional)

Required Capabilities:


Calendar Event Model

Behaviors:

Validations:

Field Rules
title presence, max length 255
start_time presence
end_time presence, must be after start_time

Required Capabilities:


Routes

Add to api/v1 namespace:


Subjects Controller

Location: app/controllers/api/v1/subjects_controller.rb

Standard CRUD scoped to current teacher’s subjects.


Calendar Events Controller

Location: app/controllers/api/v1/calendar_events_controller.rb

Endpoints:

Method Path Description
GET /calendar_events List events (requires start_date, end_date params)
GET /calendar_events/:id Show event details
POST /calendar_events Create event
PATCH /calendar_events/:id Update event
DELETE /calendar_events/:id Delete event

Index Filters:

Create/Update Validation:


Testing Requirements

Subject Model:

Calendar Event Model:

Subjects Controller:

Calendar Events Controller:


Validation Checklist


Phase 4: Assignments & Tasks

Goal

Add assignments (homework, projects) and tasks (to-do items). Assignments are tied to students and optionally subjects. Tasks are standalone to-do items for the teacher.


Dependencies

None—all required gems already installed.


Database Schema

Assignments Table:

Column Type Constraints
id uuid Primary key
teacher_id uuid Foreign key, not null
student_id uuid Foreign key, not null
subject_id uuid Foreign key, nullable
title string Not null
description text Nullable
due_date date Nullable
status string Default: ‘pending’
grade string Nullable
max_grade string Nullable
graded_at datetime Nullable
created_at datetime Not null
updated_at datetime Not null

Indexes:

Status values: pending, in_progress, completed, graded

Foreign Keys:


Tasks Table:

Column Type Constraints
id uuid Primary key
teacher_id uuid Foreign key, not null
title string Not null
description text Nullable
due_date date Nullable
priority string Default: ‘medium’
status string Default: ‘pending’
completed_at datetime Nullable
created_at datetime Not null
updated_at datetime Not null

Indexes:

Priority values: low, medium, high Status values: pending, completed


Model Updates

Teacher: Add associations to assignments and tasks (destroyed when teacher deleted)

Student: Add association to assignments (destroyed when student deleted)

Subject: Add association to assignments (set null when subject deleted)


Assignment Model

Behaviors:

Validations:

Field Rules
title presence, max length 255
status inclusion in valid values

Required Capabilities:


Task Model

Behaviors:

Validations:

Field Rules
title presence, max length 255
priority inclusion in valid values
status inclusion in valid values

Required Capabilities:


Routes

Add to api/v1 namespace:

Assignments:

Tasks:


Controllers

Assignments Controller: app/controllers/api/v1/assignments_controller.rb

Tasks Controller: app/controllers/api/v1/tasks_controller.rb


Testing Requirements

Assignment Model:

Task Model:

Controllers:


Validation Checklist


Phase 5: Report Cards

Goal

Add report cards with grade entries per subject for each student.


Dependencies

None—all required gems already installed.


Database Schema

Report Cards Table:

Column Type Constraints
id uuid Primary key
teacher_id uuid Foreign key, not null
student_id uuid Foreign key, not null
title string Not null
period_start date Not null
period_end date Not null
notes text Nullable
issued_at datetime Nullable
created_at datetime Not null
updated_at datetime Not null

Indexes:

Foreign Keys:


Grade Entries Table:

Column Type Constraints
id uuid Primary key
report_card_id uuid Foreign key, not null
subject_id uuid Foreign key, not null
grade string Not null
notes text Nullable
created_at datetime Not null
updated_at datetime Not null

Indexes:

Foreign Keys:


Model Updates

Teacher: Add association to report cards (destroyed when teacher deleted)

Student: Add association to report cards (destroyed when student deleted)


Report Card Model

Behaviors:

Validations:

Field Rules
title presence
period_start presence
period_end presence, must be after period_start

Required Capabilities:


Grade Entry Model

Behaviors:

Validations:

Field Rules
grade presence
subject_id unique per report card

Routes

Add to api/v1 namespace:

Report Cards:


Testing Requirements

Report Card Model:

Grade Entry Model:

Controllers:


Validation Checklist


Phase 6: Expense Tracking

Goal

Add expense categories and expenses for tracking homeschool costs.


Dependencies

None—all required gems already installed.


Database Schema

Expense Categories Table:

Column Type Constraints
id uuid Primary key
teacher_id uuid Foreign key, not null
name string Not null
color string Nullable
budget decimal(10,2) Nullable
is_active boolean Default: true
created_at datetime Not null
updated_at datetime Not null

Indexes:


Expenses Table:

Column Type Constraints
id uuid Primary key
teacher_id uuid Foreign key, not null
expense_category_id uuid Foreign key, nullable
student_id uuid Foreign key, nullable
description string Not null
amount decimal(10,2) Not null
date date Not null
vendor string Nullable
receipt_url string Nullable
notes text Nullable
created_at datetime Not null
updated_at datetime Not null

Indexes:

Foreign Keys:


Model Updates

Teacher: Add associations to expense categories and expenses (destroyed when teacher deleted)

Student: Add association to expenses (set null when student deleted)


Expense Category Model

Behaviors:

Validations:

Field Rules
name presence, unique per teacher
budget numericality, greater than 0 (if present)

Required Capabilities:


Expense Model

Behaviors:

Validations:

Field Rules
description presence
amount presence, numericality greater than 0
date presence

Required Capabilities:


Routes

Add to api/v1 namespace:


Testing Requirements

Expense Category Model:

Expense Model:

Controllers:


Validation Checklist


Phase 7: Lesson Planning

Goal

Add lesson plans that tie together subjects, students, and calendar events.


Dependencies

None—all required gems already installed.


Database Schema

Lesson Plans Table:

Column Type Constraints
id uuid Primary key
teacher_id uuid Foreign key, not null
subject_id uuid Foreign key, nullable
title string Not null
description text Nullable
objectives text Nullable
materials text Nullable
procedure text Nullable
assessment text Nullable
duration_minutes integer Nullable
is_template boolean Default: false
created_at datetime Not null
updated_at datetime Not null

Indexes:

Foreign Keys:


Lesson Plan Students Table (join):

Column Type Constraints
id uuid Primary key
lesson_plan_id uuid Foreign key, not null
student_id uuid Foreign key, not null
created_at datetime Not null
updated_at datetime Not null

Indexes:

Foreign Keys:


Model Updates

Teacher: Add association to lesson plans (destroyed when teacher deleted)

Subject: Add association to lesson plans (set null when subject deleted)


Lesson Plan Model

Behaviors:

Validations:

Field Rules
title presence, max length 255
duration_minutes numericality, greater than 0 (if present)

Required Capabilities:


Lesson Plan Student Model (Join)

Behaviors:

Validations:


Routes

Add to api/v1 namespace:


Testing Requirements

Lesson Plan Model:

Controllers:


Validation Checklist


Phase 8: Production Preparation

Goal

Add production dependencies, file uploads, background jobs, and deployment configuration.


Dependencies

Gem Purpose Why This Phase Why This Gem
sidekiq Background job processing Email delivery (verification, password reset) should be async in production—can’t block requests waiting for SMTP. Fast, battle-tested, great dashboard. Good_job is a solid DB-based alternative if you want to avoid Redis. Resque is older and slower.
redis In-memory data store for Sidekiq Required by Sidekiq for job queue storage. Required by Sidekiq. No alternative if using Sidekiq.
aws-sdk-s3 S3 file uploads for Active Storage Profile images and receipt uploads need cloud storage in production—local disk doesn’t scale and doesn’t persist on Heroku. Official AWS SDK. Shrine is an alternative to Active Storage entirely, but Active Storage is built-in and simpler to start.
image_processing Resize/transform uploaded images Profile images need thumbnails—can’t serve full-size images to mobile clients. Required by Active Storage for variants. Uses libvips (fast) or ImageMagick under the hood.

Environment Variables

Variable Description
REDIS_URL Redis connection string
AWS_ACCESS_KEY_ID AWS access key
AWS_SECRET_ACCESS_KEY AWS secret key
AWS_REGION AWS region (e.g., us-east-1)
AWS_BUCKET S3 bucket name

Active Storage Setup


Sidekiq Configuration


File Uploads

Add attachments to models:

Model Attachment
Teacher profile_image
Student profile_image
Expense receipt

CORS (Updated)


Email Configuration

Create mailers for:

Use async delivery via background jobs.


Testing Requirements

File Uploads:

Background Jobs:

CORS:


Validation Checklist


Complete Model Association Reference

After all phases, here are the complete associations:

Teacher:

Student:

Subject:


API Endpoints Summary

Phase Endpoints
0 GET /health
1 POST /api/v1/auth/* (register, login, refresh, logout, password/, email/)
2 GET/POST/PATCH/DELETE /api/v1/students
3 CRUD /api/v1/subjects, CRUD /api/v1/calendar_events
4 CRUD /api/v1/assignments (+ grade), CRUD /api/v1/tasks (+ complete)
5 CRUD /api/v1/report_cards (+ issue), nested grade_entries
6 CRUD /api/v1/expense_categories, CRUD /api/v1/expenses
7 CRUD /api/v1/lesson_plans (+ duplicate)