Skip to content

Latest commit

 

History

History
196 lines (139 loc) · 9.04 KB

AGENTS.md

File metadata and controls

196 lines (139 loc) · 9.04 KB

AGENTS.md

This file provides guidance to AI agents when working with code in this repository.

Project Overview

codebar planner is a Rails 8.1 application for managing codebar.io members and events. It handles workshop/event scheduling, member registration, invitations, RSVPs, and feedback collection for coding workshops organized by codebar chapters.

Development Workflow

Never work directly on the master branch. All changes land through a pull request from a feature branch:

  1. Branch: git checkout -b feature/descriptive-name
  2. Commit, push the branch, open a pull request
  3. Wait for approval before merging to master

Never commit or push to master directly, and never merge without review.

Development Setup

IMPORTANT: Always use native installation with bundle exec commands. Never use Docker or bin/d* commands.

Prerequisites

  • mise — install via brew install mise, then enable mise activate in your shell profile (see mise docs). Alternatively, use mise exec to run commands with project env vars without shell activation.
  • PostgreSQL running locally (default port 5432, or configure via DB_PORT)

Native Installation

cp mise.local.toml.example mise.local.toml
# Edit mise.local.toml with your GitHub OAuth credentials
# Optionally delete .env if it exists from a prior setup:
rm .env
bundle && rake db:create db:prepare db:seed
  • Server: bundle exec rails server
  • Tests: make test or bundle exec parallel_rspec spec/ -n 3 - runs RSpec tests in parallel (3 processes is optimal)
  • Single test: bundle exec rspec spec/path/to/file_spec.rb:42
  • Rails console: bundle exec rails console
  • Run rake tasks: bundle exec rake [task]
  • Linting: bundle exec rubocop

Environment Variables

Managed by mise via mise.toml (shared, committed) and mise.local.toml (secrets, gitignored). Automatically loaded when you enter the project directory (requires mise activate).

Variable Default Description
DB_HOST localhost Postgres host
DB_PORT 5432 Postgres port
DB_USER (empty) Postgres user
POSTGRES_PASSWORD (empty) Postgres password
GITHUB_KEY — GitHub OAuth client ID (set in mise.local.toml)
GITHUB_SECRET — GitHub OAuth client secret (set in mise.local.toml)

Create GitHub OAuth app at https://github.com/settings/applications/new with callback URL http://localhost:3000/auth/github.

Architecture & Domain Model

Core Domain Concepts

  • Members: students, coaches, both, or neither. GitHub OAuth via auth_services; roles via Rolify (admin, organiser per chapter or workshop).
  • Chapters: local organisations (e.g. "London") with organisers; they host workshops.
  • Workshops: belong to one chapter; invitations go to chapter subscribers; attendance is first-come-first-served up to venue capacity, with an automatic waiting list (WaitingList, auto_rsvp) promoted when spaces free up.
  • Events: multi-chapter; attendance requires admin approval after RSVP.
  • Sponsors: venues with addresses and member contacts; one sponsor is the workshop host.
  • Invitations: WorkshopInvitation (auto-accepted up to capacity) for workshops, Invitation (needs admin verification) for events.

Association overview and the full data model: app/models/README.md.

Authorization & Authentication

  • Authentication: GitHub OAuth via OmniAuth. Session stores member_id.
  • Authorization: Pundit policies in app/policies/. Key roles:
    • admin - global admin access
    • organiser - per-chapter or per-workshop organiser role
  • Access checks: current_user.is_admin?, current_user.manager? (admin or organiser)
  • Policies must be called in controllers. ApplicationController rescues Pundit::NotAuthorizedError.

Frontend Stack

  • CSS Framework: Bootstrap 5
  • JavaScript: Stimulus controllers, Turbo for page transitions
  • View Engine: HAML (not ERB)
  • Asset Pipeline: Sprockets with importmap-rails
  • Icons: Font Awesome 5

Background Jobs

  • Queue: Delayed Job (database-backed)
  • Jobs defined in app/jobs/ (inheriting from ApplicationJob)
  • Worker process: bin/delayed_job start (native) or managed by Docker

Testing

  • Framework: RSpec with Capybara for feature tests
  • JavaScript Driver: Playwright (Chromium by default)
  • Factories: Fabrication (not FactoryBot)
  • Test data: Faker for generated data
  • Coverage: SimpleCov (runs in CI when COVERAGE=true)
  • Parallel testing: Use make test or bundle exec parallel_rspec spec/ -n 3 for ~45% faster test runs
  • JavaScript tests: Capybara with Playwright driver
    • Use PLAYWRIGHT_HEADLESS=false to debug with visible browser
    • Use PWDEBUG=1 for Playwright Inspector (step-through debugging)
    • Use PLAYWRIGHT_BROWSER=firefox or webkit for cross-browser testing
  • Matchers: Shoulda Matchers, RSpec Collection Matchers

Run single test: bundle exec rspec spec/path/to/file_spec.rb:42 (load the project environment first: mise, direnv, or plain exports all work).

Code Style

  • Linter: RuboCop with custom config (.rubocop.yml)
  • Max line length: 120 characters
  • Max method length: 10 lines (excludes tests)
  • Hash syntax: Modern style { key: value } not { :key => value }
  • HAML linting: haml_lint (config in .haml-lint.yml)
  • Run linter: rubocop or bin/drubocop (Docker)

Key RuboCop exclusions:

  • db/, spec/, config/, bin/ excluded from most cops
  • Documentation not required (Style/Documentation: false)

Parameter Handling

This application uses Rails 8.0 params.expect for parameter filtering and validation. Full patterns: docs/parameter-handling.md.

The trap: params.expect does not work with accepts_nested_attributes_for; the fix is in docs/parameter-handling.md (Nested Attributes).

Important Patterns

Controllers

  • Use Pundit authorize to check permissions
  • Admin controllers in app/controllers/admin/ namespace
  • Super admin controllers in app/controllers/super_admin/
  • Use authenticate_admin! or authenticate_admin_or_organiser! before_action for protected routes

Models

  • Concerns in app/models/concerns/ (e.g., Invitable, Listable, DateTimeConcerns)
  • Permissions via Rolify: member.add_role(:organiser, workshop)
  • Scopes commonly used for filtering (e.g., Member.not_banned, Workshop.students)

Views

  • Use Presenters (app/presenters/) for complex view logic
  • Form objects in app/form_models/ for complex forms
  • Helpers in app/helpers/

Services

  • Service objects in app/services/ for complex business logic
  • Example: invitation management, email sending logic

Routes

  • Root: dashboard#show
  • Admin namespace: /admin/* - requires admin/organiser access
  • Auth: /auth/github (login), /logout (logout)
  • Key resources: /workshops/:id, /events/:id, /meetings/:id
  • Chapter pages: /:id (catch-all at end of routes)

Database

  • RDBMS: PostgreSQL
  • Migrations: Standard Rails migrations in db/migrate/
  • Historical migrations: All pre-2026 migrations were removed. New databases are bootstrapped from db/schema.rb via db:prepare. The 13 migrations from 2026 onwards remain and are audited by strong_migrations.
  • Seeds: db/seeds.rb creates sample data for development

Migrations and Heroku preboot

Production runs with Heroku preboot: new web dynos boot before old ones stop, and traffic switches about 3 minutes after the deploy. The release phase (rake db:prepare) runs before new dynos boot, so old code serves against the new schema during that window. Migrations must therefore be forward-compatible:

  • Expand first, contract later. Add columns without a default, then set the default in a second migration. Backfill in batches.
  • Before remove_column, add the column to ignored_columns and deploy code that no longer reads or writes it, then remove the column in a separate deploy.
  • Rename columns in two steps: add the new column, sync data, deploy code that uses the new name, then remove the old column.
  • safety_assured around a destructive operation means "the old release tolerates this", not "this is fine". It does not make a migration forward-compatible.
  • A migration that cannot be made forward-compatible needs preboot temporarily disabled for its deploy: heroku features:disable preboot, deploy, then heroku features:enable preboot.

Deployment

This app uses Heroku. See Makefile for deployment commands (requires appropriate Heroku access):

  • make deploy_production
  • make deploy_staging

Additional Tools

  • Email preview: Letter Opener (development) - emails open in browser
  • Error tracking: Rollbar (production)
  • Performance monitoring: Scout APM
  • Activity tracking: PublicActivity gem for user action history
  • Pagination: Pagy
  • File uploads: CarrierWave (AWS S3 in production)
  • Friendly URLs: FriendlyId for slug generation