This file provides guidance to AI agents when working with code in this repository.
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.
Never work directly on the master branch. All changes land through a pull request from a feature branch:
- Branch:
git checkout -b feature/descriptive-name - Commit, push the branch, open a pull request
- Wait for approval before merging to
master
Never commit or push to master directly, and never merge without review.
IMPORTANT: Always use native installation with bundle exec commands. Never use Docker or bin/d* commands.
- mise — install via
brew install mise, then enablemise activatein your shell profile (see mise docs). Alternatively, usemise execto run commands with project env vars without shell activation. - PostgreSQL running locally (default port 5432, or configure via
DB_PORT)
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 testorbundle 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
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.
- Members: students, coaches, both, or neither. GitHub OAuth via
auth_services; roles via Rolify (admin,organiserper 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.
- Authentication: GitHub OAuth via OmniAuth. Session stores
member_id. - Authorization: Pundit policies in
app/policies/. Key roles:admin- global admin accessorganiser- 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.
ApplicationControllerrescuesPundit::NotAuthorizedError.
- 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
- Queue: Delayed Job (database-backed)
- Jobs defined in
app/jobs/(inheriting fromApplicationJob) - Worker process:
bin/delayed_job start(native) or managed by Docker
- 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 testorbundle exec parallel_rspec spec/ -n 3for ~45% faster test runs - JavaScript tests: Capybara with Playwright driver
- Use
PLAYWRIGHT_HEADLESS=falseto debug with visible browser - Use
PWDEBUG=1for Playwright Inspector (step-through debugging) - Use
PLAYWRIGHT_BROWSER=firefoxorwebkitfor cross-browser testing
- Use
- 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).
- 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:
rubocoporbin/drubocop(Docker)
Key RuboCop exclusions:
db/,spec/,config/,bin/excluded from most cops- Documentation not required (
Style/Documentation: false)
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).
- Use Pundit
authorizeto check permissions - Admin controllers in
app/controllers/admin/namespace - Super admin controllers in
app/controllers/super_admin/ - Use
authenticate_admin!orauthenticate_admin_or_organiser!before_action for protected routes
- 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)
- Use Presenters (
app/presenters/) for complex view logic - Form objects in
app/form_models/for complex forms - Helpers in
app/helpers/
- Service objects in
app/services/for complex business logic - Example: invitation management, email sending logic
- 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)
- RDBMS: PostgreSQL
- Migrations: Standard Rails migrations in
db/migrate/ - Historical migrations: All pre-2026 migrations were removed. New databases are bootstrapped from
db/schema.rbviadb:prepare. The 13 migrations from 2026 onwards remain and are audited by strong_migrations. - Seeds:
db/seeds.rbcreates sample data for development
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 toignored_columnsand 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_assuredaround 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, thenheroku features:enable preboot.
This app uses Heroku. See Makefile for deployment commands (requires appropriate Heroku access):
make deploy_productionmake deploy_staging
- 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