This document explains the architecture, integrations, and operational details of the Collaborado course discussion platform so developers can work productively and deploy safely.
- Getting Started
- Stack and Architecture
- Domain Model Overview
- API Documentation / Routing
- Authentication and Authorization
- Environments and Configuration
- Known Validations and Constraints
- Deployment Notes (Heroku)
# Clone via HTTPS
git clone https://github.com/tamu-edu-students/606-project3-group5.git
# Or clone via SSH
git clone git@github.com:tamu-edu-students/606-project3-group5.git
# Navigate into the project directory
cd 606-project3-group5# Install Ruby dependencies
bundle install
# Set up the database
bundle exec rails db:create db:migrate
# (Optional) Seed the database with sample data
bundle exec rails db:seed# Start the development server
bin/dev
# Or start Rails server directly
bundle exec rails serverThe application will be available at http://localhost:3000.
📖 For step-by-step local setup instructions, see LOCAL_SETUP.md.
| Component | Technology |
|---|---|
| Framework | Ruby on Rails 8 (MVC architecture) |
| Ruby | See .ruby-version or Gemfile constraints |
| Database | SQLite (development/test), PostgreSQL (production via DATABASE_URL) |
| Authentication | Google OAuth2 via OmniAuth, domain-restricted to @tamu.edu |
| Assets | ERB templates, SCSS compiled with dartsass-rails, Importmaps for JavaScript |
| Testing | RSpec (unit/integration), Cucumber (BDD), SimpleCov (coverage) |
| Layer | Components |
|---|---|
| Models | User, Course, CourseEnrollment, Channel, Post, Comment, PostUpvote, CommentUpvote |
| Controllers | CoursesController, ChannelsController, PostsController, ProfilesController, SessionsController, DashboardController |
| Views | ERB templates under app/views/ |
| Field | Description |
|---|---|
first_name, last_name |
User's name (required) |
email |
Google-sourced email (unique, required) |
google_uid, provider |
OAuth identifiers |
role |
student, ta, or professor |
NetID: Extracted from email (email.split("@").first)
Associations:
has_many :course_enrollments— courses joinedhas_many :courses_created— courses they createdhas_many :posts,has_many :commentshas_many :post_upvotes,has_many :comment_upvotes
Key Method: User.from_google(auth_hash) — creates/finds user from OAuth
| Field | Description |
|---|---|
name |
Course name (required) |
department |
Must be valid TAMU department code |
course_code |
Course number (required) |
instructor |
Instructor name |
description |
Course description |
Associations:
belongs_to :creator— User who created ithas_many :course_enrollments,has_many :usershas_many :channels
Notes:
- Auto-creates a default "General" channel on creation
- Provides
DEPARTMENTSconstant (hash of department codes to names) departmentsclass method returns sorted department codes
Links users to courses (join table with role tracking).
| Field | Description |
|---|---|
user_id |
Reference to user |
course_id |
Reference to course |
role |
User's role in the course |
Students enroll via the "Join" button; professors/TAs don't need to join.
| Field | Description |
|---|---|
name |
Channel name |
description |
Channel description |
course_id |
Parent course reference |
Associations:
belongs_to :coursehas_many :posts
Groups discussions within a course (e.g., "Homework", "Lectures", "Projects").
| Field | Description |
|---|---|
title |
Post title (max 255 chars) |
content |
Post body (required) |
type |
question or note |
resolved |
Boolean for questions |
upvote_count |
Number of upvotes |
channel_id, user_id |
References |
Associations:
belongs_to :channel,belongs_to :userhas_many :comments,has_many :post_upvotes
⚠️ Important:self.inheritance_column = :_type_disableddisables Rails STI since we usetypecolumn.
Scopes: questions, notes, resolved, unresolved, newest_first, oldest_first, most_upvoted
Methods: question?, note?, upvoted_by?(user)
| Field | Description |
|---|---|
content |
Comment text |
post_id, user_id |
References |
upvote_count |
Number of upvotes |
Associations:
belongs_to :post,belongs_to :userhas_many :comment_upvotes
Join tables tracking upvotes.
| Field | Description |
|---|---|
user_id |
User who upvoted |
post_id / comment_id |
Target reference |
One row per upvote; destroying row removes upvote.
| Method | Path | Controller#Action | Description |
|---|---|---|---|
GET |
/auth/:provider/callback |
SessionsController#create |
OAuth callback |
GET |
/auth/failure |
SessionsController#failure |
OAuth error |
GET |
/logout |
SessionsController#destroy |
Logout |
DELETE |
/logout |
SessionsController#destroy |
Logout |
| Method | Path | Controller#Action | Description |
|---|---|---|---|
GET |
/ |
HomeController#index |
Landing page |
GET |
/dashboard |
DashboardController#index |
User dashboard |
GET |
/role |
ProfilesController#select_role |
Role selection |
PATCH |
/role |
ProfilesController#update |
Set user role |
GET |
/profile/edit |
ProfilesController#edit |
Edit profile |
PATCH |
/profile |
ProfilesController#update |
Update profile |
| Method | Path | Controller#Action | Description |
|---|---|---|---|
GET |
/courses |
CoursesController#index |
List/search/filter |
POST |
/courses |
CoursesController#create |
Create course |
GET |
/courses/new |
CoursesController#new |
New course form |
GET |
/courses/:id |
CoursesController#show |
Show course |
GET |
/courses/:id/edit |
CoursesController#edit |
Edit course form |
PATCH |
/courses/:id |
CoursesController#update |
Update course |
DELETE |
/courses/:id |
CoursesController#destroy |
Delete course |
POST |
/courses/:id/join |
CoursesController#join |
Enroll student |
DELETE |
/courses/:id/leave |
CoursesController#leave |
Unenroll |
Query Parameters for Index:
| Parameter | Description |
|---|---|
department |
Filter by department code (e.g., CSCE) |
search |
Search by name, course_code, or instructor (case-insensitive) |
Authorization:
new/create/edit/update/destroy: professor only (require_professor)join: students onlyleave: any enrolled user
| Method | Path | Controller#Action |
|---|---|---|
GET |
/courses/:course_id/channels |
ChannelsController#index |
POST |
/courses/:course_id/channels |
ChannelsController#create |
GET |
/courses/:course_id/channels/new |
ChannelsController#new |
GET |
/courses/:course_id/channels/:id |
ChannelsController#show |
GET |
/courses/:course_id/channels/:id/edit |
ChannelsController#edit |
PATCH |
/courses/:course_id/channels/:id |
ChannelsController#update |
DELETE |
/courses/:course_id/channels/:id |
ChannelsController#destroy |
Query Parameters for Show:
| Parameter | Values | Description |
|---|---|---|
type |
question, note, all |
Filter posts by type |
status |
resolved, unresolved, all |
Filter by resolution |
sort |
newest, oldest, most_upvoted |
Sort posts |
page |
Integer | Pagination (10 per page via Kaminari) |
Authorization: Create/edit/destroy typically restricted to instructors/admins.
| Method | Path | Controller#Action |
|---|---|---|
GET |
/channels/:channel_id/posts/new |
PostsController#new |
POST |
/channels/:channel_id/posts |
PostsController#create |
GET |
/posts/:id/edit |
PostsController#edit |
PATCH |
/posts/:id |
PostsController#update |
DELETE |
/posts/:id |
PostsController#destroy |
POST |
/posts/:id/upvote |
PostsController#upvote |
DELETE |
/posts/:id/upvote |
PostsController#remove_upvote |
Create Parameters: post[title], post[content], post[type] (question/note), post[resolved] (optional)
Authorization:
- Edit/update: post owner or admin
- Delete: post owner or admin
- Upvote/remove_upvote: any authenticated user
| Method | Path | Controller#Action |
|---|---|---|
POST |
/posts/:post_id/comments |
CommentsController#create |
DELETE |
/comments/:id |
CommentsController#destroy |
POST |
/comments/:id/upvote |
CommentsController#upvote |
DELETE |
/comments/:id/upvote |
CommentsController#remove_upvote |
1. User clicks "Sign in with Google"
↓
2. Redirects to Google OAuth consent screen (restricted to @tamu.edu)
↓
3. Google redirects back to /auth/google_oauth2/callback
↓
4. SessionsController#create calls User.from_google(auth_hash)
↓
5. User created/found, session established
↓
6. Redirected to role selection (if first time) or dashboard
Configuration: config/initializers/omniauth.rb
hd: "tamu.edu"— enforces domain restrictionprovider_ignores_state: true— for callback compatibility- Test mode enabled in test environment
| Role | Permissions |
|---|---|
| Students | Join courses, view enrolled course channels, create posts/comments, upvote |
| TAs | Similar to students but can manage channels (auto-access) |
| Professors | Can create/edit/delete courses and channels, manage all content |
Authorization Helpers:
require_login— ensures user is authenticatedrequire_professor— restricts to professor rolecan_edit_post?— checks post edit permissionscan_delete_post?— checks post delete permissions
| Setting | Value |
|---|---|
| Database | SQLite (storage/development.sqlite3) |
| SCSS | bin/rails dartsass:watch |
Environment Variables (via .env file):
GOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRET
| Setting | Value |
|---|---|
| Database | SQLite (storage/test.sqlite3) |
| OmniAuth | Test mode enabled |
url: nullindatabase.ymlpreventsDATABASE_URLoverride.
| Setting | Value |
|---|---|
| Database | PostgreSQL via DATABASE_URL (Heroku add-on) |
| Connection Roles | primary, cable, queue, cache (all use same URL) |
Required Config Vars:
DATABASE_URL— auto-set by Heroku Postgres addonGOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRETRAILS_MASTER_KEY— for encrypted credentials
| Model | Constraint |
|---|---|
| User | Email must be unique and end with @tamu.edu |
| Course | Department must be valid TAMU department code; course_code must be numeric |
| Post | Type must be question or note; STI disabled via inheritance_column = :_type_disabled |
| Upvotes | One upvote per user per post/comment (enforced by unique index on join tables) |
| Enrollment | Students must join courses to access channels; professors/TAs have automatic access |
- Heroku account and CLI installed
- Git repository pushed to remote
heroku create your-app-name
heroku addons:create heroku-postgresql:essential-0
heroku config:set GOOGLE_CLIENT_ID=your_client_id
heroku config:set GOOGLE_CLIENT_SECRET=your_client_secret
heroku config:set RAILS_MASTER_KEY=$(cat config/master.key)git push heroku main
heroku run rails db:migrate
heroku run rails db:seed # optional| Issue | Solution |
|---|---|
| OAuth redirect errors | Ensure production callback URL is added to Google Cloud Console |
| Asset issues | Precompile assets locally: RAILS_ENV=production rails assets:precompile |