Skip to content

Repository files navigation

LibraryHub — Online Library Management System

🌐 Live Demo: https://libraryhub-yangzhao-1b53d70f2081.herokuapp.com/

Name Email Password Role
Admin User admin@libraryhub.com password123 admin
Alice Smith alice@example.com password123 user
Bob Jones bob@example.com password123 user

SEP 759 Prototyping Web Applications — Final Project (Phase 1 & 2) McMaster University, Winter 2026


Table of Contents

  1. Project Description
  2. Submission Checklist
  3. Technology Stack
  4. Project Structure
  5. Local Setup & Running the Application
  6. Test Accounts
  7. Application Features
  8. API Endpoints & Testing with Postman
  9. Heroku Deployment
  10. ER Diagram & Application Flow

1. Project Description

LibraryHub is a Ruby on Rails web application that implements a full library management system. Users can browse books, borrow and return them, and leave reviews. Administrators manage the book catalog and monitor all borrowing activity.

The project is built in two phases:

  • Phase 1 — Full MVC web application with custom authentication (has_secure_password + sessions), nested resources, all four model relationship types, DRY partials, and seed data.
  • Phase 2 — Authentication migrated to Devise gem, plus a complete JWT-secured RESTful JSON API with 21 endpoints, tested via Postman.

2. Submission Checklist

Phase 1 Requirements

Requirement Status Details
At least 4 models User, Book, Category, Borrow, Review, BookCategory (6 models)
At least 1 nested resource books/:book_id/reviews and books/:book_id/borrows
one-to-one relationship Not required (spec says "at least 3 of 4")
one-to-many relationship User → Borrows, User → Reviews, Book → Borrows, Book → Reviews
many-to-many relationship Books ↔ Categories via book_categories
rich one-to-many (has-through) User → Books through Borrows
User model with authentication has_secure_password (Phase 1), Devise (Phase 2)
Sessions for logged-in users session[:user_id] stores current user
DRY using partials _navbar, _flash, _book_card, _form (books & categories)
No Devise in Phase 1 Custom auth from scratch
Application is functional All CRUD operations working
Hosted on Heroku https://libraryhub-yangzhao-1b53d70f2081.herokuapp.com/
CSS in stylesheet (no inline) All styles in application.css
Seed data rails db:seed — 3 users, 4 books, 4 categories, borrows, reviews
Test accounts See Section 6
ER diagram See LibraryHub_Proposal.docx
Application flow document See LibraryHub_Proposal.docx

Phase 2 Requirements

Requirement Status Details
At least 5 API endpoints 21 endpoints total
Devise gem for authentication Replaces custom auth
API user authentication (JWT) Bearer token via Authorization header
API testing — user signup/login POST /api/v1/auth/register and /login
API testing — GET collection GET /api/v1/books, /categories, /borrows
API testing — GET single item GET /api/v1/books/:id, /categories/:id
Bonus: image upload Not implemented
Bonus: POST/PATCH/DELETE tested Borrow, return, delete review via API
Bonus: notification emails Not implemented
Video demonstration See submission
Seed data on Heroku See Section 9
Test accounts See Section 6
ER diagram See LibraryHub_Proposal.docx

3. Technology Stack

Component Technology
Framework Ruby on Rails 7.1.6
Language Ruby 3.1.4
Database (local) SQLite3
Database (Heroku) PostgreSQL
Authentication Devise gem + custom sessions (Phase 1)
API Auth JWT (JSON Web Tokens)
Frontend ERB, Turbo Rails, Stimulus JS
Styling Custom CSS (application.css)
API Testing Postman
Deployment Heroku

4. Project Structure

library_hub/
├── app/
│   ├── controllers/
│   │   ├── api/
│   │   │   └── v1/
│   │   │       ├── base_controller.rb        # JWT auth for all API routes
│   │   │       ├── auth_controller.rb        # login / register / logout
│   │   │       ├── books_controller.rb       # API books CRUD
│   │   │       ├── categories_controller.rb  # API categories CRUD
│   │   │       ├── borrows_controller.rb     # API borrow / return
│   │   │       ├── reviews_controller.rb     # API reviews
│   │   │       └── users_controller.rb       # API user profile
│   │   ├── users/
│   │   │   ├── sessions_controller.rb        # Devise sessions
│   │   │   └── registrations_controller.rb  # Devise registrations
│   │   ├── application_controller.rb
│   │   ├── books_controller.rb
│   │   ├── borrows_controller.rb
│   │   ├── categories_controller.rb
│   │   └── reviews_controller.rb
│   ├── models/
│   │   ├── user.rb
│   │   ├── book.rb
│   │   ├── category.rb
│   │   ├── borrow.rb
│   │   ├── review.rb
│   │   └── book_category.rb
│   ├── views/
│   │   ├── shared/
│   │   │   ├── _navbar.html.erb    # partial: navigation bar
│   │   │   ├── _flash.html.erb     # partial: flash messages
│   │   │   └── _book_card.html.erb # partial: reusable book card
│   │   ├── books/
│   │   │   ├── _form.html.erb      # partial: shared book form
│   │   │   ├── index.html.erb
│   │   │   ├── show.html.erb
│   │   │   ├── new.html.erb
│   │   │   └── edit.html.erb
│   │   ├── categories/
│   │   │   ├── _form.html.erb      # partial: shared category form
│   │   │   ├── index.html.erb
│   │   │   ├── show.html.erb
│   │   │   ├── new.html.erb
│   │   │   └── edit.html.erb
│   │   ├── borrows/
│   │   │   └── index.html.erb
│   │   └── users/
│   │       ├── sessions/new.html.erb
│   │       ├── registrations/new.html.erb
│   │       ├── show.html.erb
│   │       └── edit.html.erb
│   ├── assets/stylesheets/
│   │   └── application.css         # all custom styles (no inline CSS)
│   └── lib/
│       └── json_web_token.rb       # JWT encode/decode helper
├── config/
│   ├── routes.rb                   # all web + API routes
│   ├── initializers/
│   │   └── cors.rb                 # CORS config for API
│   └── database.yml
├── db/
│   ├── migrate/
│   └── seeds.rb                    # seed data for all environments
└── Gemfile

5. Local Setup & Running the Application

Prerequisites

Make sure you have the following installed:

  • Ruby 3.1.4 — rubyinstaller.org (Windows) or rbenv/rvm (Mac/Linux)
  • Bundler — comes with Ruby, or run gem install bundler
  • Git

Step 1 — Clone the repository

git clone https://github.com/YangZ0225/library-hub.git
cd library-hub

Step 2 — Install dependencies

bundle install

If you are on Windows and encounter native extension errors, run:

ridk install
# Choose option 3 (MSYS2 and MINGW development toolchain)

Then retry bundle install.

Step 3 — Set up the database

rails db:create
rails db:migrate
rails db:seed

This creates the SQLite database, runs all migrations, and loads seed data including 3 users, 4 books, 4 categories, sample borrows, and sample reviews.

Step 4 — Start the server

rails server

The application will be available at:

http://localhost:3000

Step 5 — Log in and explore

Use one of the test accounts from Section 6 to log in. The admin account can add/edit/delete books and categories.


6. Test Accounts

The following accounts are created automatically by rails db:seed:

Name Email Password Role
Admin User admin@libraryhub.com password123 admin
Alice Smith alice@example.com password123 user
Bob Jones bob@example.com password123 user

Admin capabilities:

  • Add, edit, and delete books and categories
  • View all users' borrow records
  • Delete any review
  • Full CRUD access via the API

User capabilities:

  • Browse all books and categories
  • Borrow available books (14-day loan period)
  • Return borrowed books
  • Write one review per book
  • View and edit their own profile

7. Application Features

Books

  • Browse all books with title, author, category tags, availability, and average rating
  • Search by title or author; filter by category
  • View full book details including all reviews
  • Borrow available books with one click
  • Return borrowed books from the book detail page or My Borrows page

Categories

  • Browse all categories with book counts
  • View all books belonging to a category
  • Admin: create, edit, delete categories

Borrows

  • Users see their own active and past borrow history
  • Admins see all borrow records across all users
  • Status automatically tracks: active, returned, overdue

Reviews

  • Logged-in users can submit one review per book (1–5 stars + text)
  • Reviews display on the book detail page with star ratings and dates
  • Users can delete their own reviews; admins can delete any review

Authentication (Phase 2 — Devise)

  • Registration, login, logout via Devise
  • Password recovery via email (configured with default mailer settings)
  • Rememberable session option on login form

8. API Endpoints & Testing with Postman

The API base URL is:

http://localhost:3000/api/v1

All API responses are JSON. Protected endpoints require the following header:

Authorization: Bearer <your_jwt_token>

Step 1 — Register a new user

POST /api/v1/auth/register

Body (raw JSON):

{
  "name": "Test User",
  "email": "test@example.com",
  "password": "password123",
  "password_confirmation": "password123"
}

Expected response 201 Created:

{
  "message": "Registration successful",
  "token": "eyJhbGciOiJIUzI1NiJ9...",
  "user": { "id": 4, "name": "Test User", "email": "test@example.com", "role": "user" }
}

Step 2 — Login

POST /api/v1/auth/login

Body (raw JSON):

{
  "email": "admin@libraryhub.com",
  "password": "password123"
}

Expected response 200 OK:

{
  "message": "Login successful",
  "token": "eyJhbGciOiJIUzI1NiJ9...",
  "user": { "id": 1, "name": "Admin User", "email": "admin@libraryhub.com", "role": "admin" }
}

Copy the token value. In Postman, set it as an environment variable or paste it into the Authorization header as Bearer <token> for all subsequent requests.


Step 3 — GET all books (no auth required)

GET /api/v1/books

Expected response 200 OK:

[
  {
    "id": 1,
    "title": "The Great Gatsby",
    "author": "F. Scott Fitzgerald",
    "isbn": "978-0743273565",
    "available_copies": 4,
    "total_copies": 5,
    "average_rating": 4.0,
    "categories": [{ "id": 1, "name": "Fiction" }]
  },
  ...
]

Step 4 — GET single book details (no auth required)

GET /api/v1/books/1

Expected response 200 OK — includes full description and all reviews.


Step 5 — Borrow a book (auth required)

POST /api/v1/borrows

Header: Authorization: Bearer <token>

Body (raw JSON):

{
  "book_id": 2
}

Expected response 201 Created:

{
  "id": 3,
  "book": { "id": 2, "title": "Clean Code" },
  "borrowed_at": "2026-03-29",
  "due_date": "2026-04-12",
  "status": "active"
}

Step 6 — Return a book (PATCH — auth required)

PATCH /api/v1/borrows/3

Header: Authorization: Bearer <token>

No body required.

Expected response 200 OK:

{
  "message": "Book returned",
  "status": "returned"
}

Step 7 — POST a review (auth required)

POST /api/v1/reviews

Header: Authorization: Bearer <token>

Body (raw JSON):

{
  "book_id": 3,
  "rating": 5,
  "content": "An absolutely fascinating read. Highly recommended."
}

Expected response 201 Created.


Step 8 — DELETE a review (auth required)

DELETE /api/v1/reviews/1

Header: Authorization: Bearer <admin_token>

Expected response 200 OK:

{ "message": "Review deleted" }

Full API Endpoint Reference

Method Endpoint Auth Description
POST /api/v1/auth/register No Register new user
POST /api/v1/auth/login No Login, returns JWT
DELETE /api/v1/auth/logout Yes Logout
GET /api/v1/books No List all books
GET /api/v1/books/:id No Book detail + reviews
POST /api/v1/books Admin Create book
PATCH /api/v1/books/:id Admin Update book
DELETE /api/v1/books/:id Admin Delete book
GET /api/v1/categories No List categories
GET /api/v1/categories/:id No Category + books
POST /api/v1/categories Admin Create category
DELETE /api/v1/categories/:id Admin Delete category
GET /api/v1/borrows Yes My borrows (admin: all)
POST /api/v1/borrows Yes Borrow a book
PATCH /api/v1/borrows/:id Yes Return a book
DELETE /api/v1/borrows/:id Admin Delete borrow record
GET /api/v1/reviews No List reviews
POST /api/v1/reviews Yes Submit review
DELETE /api/v1/reviews/:id Yes Delete review
GET /api/v1/users/:id Yes User profile
PATCH /api/v1/users/:id Yes Update profile

9. Heroku Deployment

Prerequisites

Step 1 — Add PostgreSQL gem for production

In Gemfile, add:

group :production do
  gem "pg"
end

And move sqlite3 to development/test only:

group :development, :test do
  gem "sqlite3"
end

Run bundle install.

Step 2 — Configure database.yml for production

In config/database.yml, the production section should use the DATABASE_URL environment variable (Heroku sets this automatically when you add the Postgres addon).

Step 3 — Login to Heroku and create app

heroku login
heroku create library-hub-yourname

Step 4 — Add Postgres addon

heroku addons:create heroku-postgresql:mini

Step 5 — Set environment variables

heroku config:set RAILS_MASTER_KEY=$(cat config/master.key)
heroku config:set SECRET_KEY_BASE=$(rails secret)

Step 6 — Deploy

git add .
git commit -m "Deploy to Heroku"
git push heroku main

Step 7 — Run migrations and seed data on Heroku

heroku run rails db:migrate
heroku run rails db:seed

Step 8 — Open the app

heroku open

The live URL will be: https://libraryhub-yangzhao-1b53d70f2081.herokuapp.com/


10. ER Diagram & Application Flow

See the file LibraryHub_Proposal.docx included in the submission for:

  • Full database Entity Relationship Diagram with all attributes and data types
  • Complete application flow description
  • Model relationship types (one-to-many, many-to-many, rich has-through)
  • All API endpoints documented

Quick ER Summary

users ──< borrows >── books
users ──< reviews >── books
books >──< categories  (via book_categories)
Relationship Type
User → Borrows one-to-many
User → Reviews one-to-many
Book → Borrows one-to-many
Book → Reviews one-to-many
Books ↔ Categories many-to-many (via book_categories)
User → Books via Borrows rich one-to-many (has-through)

Special Instructions

  • On Windows, if you encounter gem native extension errors on first setup, run ridk install and choose option 3 before running bundle install.
  • The config/master.key file is not committed to version control. Keep this file safe — it is required to run the application.
  • To reset the database completely: rails db:drop db:create db:migrate db:seed
  • The API uses JWT tokens that expire after 24 hours. If you receive a 401 Unauthorized response, log in again to get a fresh token.

LibraryHub — SEP 759 Final Project, McMaster University, Winter 2026

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages