A comprehensive Ruby on Rails 8.1 application for managing Kyudo (Japanese archery) tournaments with support for multiple tournament formats, complex scoring systems, and role-based access control.
Core:
- Ruby: 3.4.7
- Rails: 8.1.3.1 (fully modernized to Rails 8 patterns)
- Database: PostgreSQL with custom enums
- Authentication: Rails 8 built-in authentication (has_secure_password + Sessions)
- Authorization: Pundit (role-based)
Frontend:
- JavaScript: Importmap-rails (no Node.js bundler needed)
- CSS: Sass/Bulma via cssbundling-rails
- Interactivity: Hotwire (Turbo + Stimulus)
Background Jobs & Infrastructure:
- Queue: Solid Queue (database-backed Active Job)
- Cache: Solid Cache (database-backed Rails.cache)
- WebSockets: Solid Cable (database-backed Action Cable)
Key Gems:
- State Machine: Statesman (tournament workflow)
- Auditing: audited (change tracking)
- I18n: Mobility (model translations EN/FR)
- Testing: Minitest with Apparition (headless Chrome)
- Ruby 3.4.7
- PostgreSQL 14+
- Yarn (for CSS bundling only)
bundle install
yarn install# Create and migrate database
bin/rails db:create
bin/rails db:migrate
# Load default data (staff roles, etc.)
bin/rails db:seed# Start all services (Rails server + CSS watcher)
bin/devThe application will be available at http://localhost:3000
# Full local CI (see prerequisites and isolation below)
bin/ci
# Unit and integration tests
RAILS_ENV=test bin/rails db:prepare db:fixtures:load
bin/rails test
# System tests (headless Chrome)
RAILS_ENV=test bin/rails db:fixtures:load
bin/rails test:system
# Watch mode (auto-run tests on file changes)
guardUse a dedicated test database; set DATABASE_URL when running in an isolated worktree.
Fixture loading replaces database contents. Preload fixtures before each suite because
some tests discover tournament cases from database records when their files are loaded.
System tests use Selenium with headless Chrome, which must be installed.
CI runs both suites, RuboCop, Bundler Audit, and Brakeman. Test failures and lint/security findings block their respective jobs; coverage reporting is not configured.
bin/ci runs the checks in config/ci.rb: Ruby linting, an updated gem
vulnerability audit, a version-coverage check and vulnerability audit for
JavaScript dependencies, production asset precompilation, EN/FR missing-key and
interpolation checks, Rails tests, headless browser tests, and a seed-data smoke
check. Any failed check produces a nonzero exit status. Independent checks
continue after failures; database-dependent tests and seeds are skipped if
database creation or preparation fails.
The pipeline uses Rails' standard CI.run, step, and success? DSL. The
ci:prepare and ci:assets Rails tasks handle database preparation and asset
compilation. CLI-only helpers live in script/ci, outside application autoload
paths. The database helper records successful creation in the run directory so
the outer bin/ci wrapper can clean up even when a subsequent Rails task fails.
Before running it:
- Select Ruby from
.ruby-versionand runbundle install, including development and test groups. CI loads Bundler before it starts; it does not install gems or change the lockfile. - Start PostgreSQL 14+ with a role that can connect to the
postgresmaintenance database and create/drop databases. Local connection defaults work, or usePGHOST,PGPORT,PGUSER, andPGPASSWORDfor a dedicated CI PostgreSQL instance. Do not point these variables at a production server. - Install Chrome/Chromium and make a compatible ChromeDriver available to
Selenium. Selenium Manager may download a driver when one is not available.
CI always uses headless mode, even if
CHROME_DEBUG=true. - Allow network access to the Ruby advisory database and npm registry. Audit refresh/network failures fail CI rather than silently using a stale result. The current asset smoke check uses Propshaft and vendored assets; it does not require Node.js or Yarn.
Unset both DATABASE_URL and PRIMARY_DATABASE_URL, and either unset RAILS_ENV
or set it to test. CI rejects inherited database URLs and non-test environments
before allocating resources. It also verifies that Rails resolves exactly one
test database matching the disposable database before creating or preparing it:
env -u DATABASE_URL -u PRIMARY_DATABASE_URL -u RAILS_ENV bin/ci
# Example: a local PostgreSQL instance exposed over TCP
env -u DATABASE_URL -u PRIMARY_DATABASE_URL -u RAILS_ENV PGHOST=localhost PGPORT=5432 PGUSER=postgres bin/ciEach invocation creates a uniquely named pikaichu_ci_<random-id> database,
prepares it in the test environment, and drops only that database on exit,
including failed runs and normal interrupts. Creation failures never trigger a
drop of an existing database. CI does not invoke bin/setup, migrate development
data, clear existing logs, or write precompiled assets to public/assets.
CI sets PARALLEL_WORKERS=1, overriding inherited worker counts so Rails cannot
create additional worker databases. The previous value is restored on exit.
The printed tmp/ci/<database-name>/ directory retains the run's test log,
screenshots, downloads, test storage, and production asset output for diagnosis.
A force kill or database outage can prevent cleanup; in that case, inspect and remove only
the exact CI database named in that run's output. Never remove databases by a
broad name pattern. Cleanup failures are errors, not successful CI results.
Third-party JavaScript pins must carry the exact npm version in importmap's
supported comment format, for example # @1.2.3. Verify that version against the
vendored source when updating a dependency. bin/ci-importmap rejects an empty
audit inventory, unversioned dependencies, missing assets, and unpinned vendored
JavaScript before invoking bin/importmap audit. Application-owned JavaScript
belongs under app/javascript, not in the third-party vendor inventory.
The ci:assets task runs production precompilation with a dummy secret key and
the run's isolated asset directory. This is a build smoke check, not a
replacement for a deployment test.
Translation checks do not rewrite locale files or enforce unused-key or
formatting policies. The i18n-tasks configuration discovers all EN/FR locale
files, including authentication, defaults, model/view translations, and
Kaminari. To run just these checks:
bundle exec i18n-tasks missing
bundle exec i18n-tasks check-consistent-interpolationsThe checks are strict from the start; existing findings are not allowlisted. In particular, the current missing-key report includes XLSX helper relative-key resolution and external Kaminari locale reports that need reconciliation, as well as missing English tournament-validation translations.
GitHub Actions workflows are currently separate and are not changed by this local CI setup; a local pass is not a claim of hosted-workflow parity.
- JavaScript: Uses importmap-rails. No esbuild/webpack needed. Modules are loaded directly from CDN or vendored.
- CSS: Uses cssbundling-rails with Sass. Bulma framework with custom styling.
Modern Rails 8 authentication:
- Session-based with secure signed cookies
- Password reset via signed tokens (no database tokens)
- Rate limiting on login and password reset
- Email confirmation required
Production uses the primary PostgreSQL database for Solid Queue, Solid Cache, and
Solid Cable, matching the Azure deployment's single DATABASE_URL. Their tables
are installed by the normal application migrations:
RAILS_ENV=production bin/rails db:prepare
RAILS_ENV=production bin/jobsThe Docker entrypoint prepares the database before starting the web server.
Run bin/jobs as a separate worker, or set SOLID_QUEUE_IN_PUMA=true to use the
existing Puma worker integration, which the Azure template enables. No separate
cache, queue, or cable databases are required; these services share the primary
Active Record connection pool.
The Solid migration only adds tables; it does not modify tournament data. Retain
these tables when rolling back an application image so queued work is not lost.
Solid Queue handles background jobs:
# Queue a job
MyJob.perform_later(arg1, arg2)
# View queue dashboard
# Visit /solid_queue in development# Linting
bin/rubocop
bin/rubocop -a # Auto-fix
# Security audit
bundle audit check
bin/brakemanApplication is containerized with Docker:
# Build image
docker build -t pikaichu .
# Run container
docker run -p 3000:3000 pikaichuAzure Deployment:
- Bicep templates in
infra/directory - Azure Container Apps with PostgreSQL Flexible Server
- See
azure.yamlfor Azure Developer CLI config
# Deploy to Azure
azd up- Individual: Solo archer competition
- Team: 3-5 archers per team
- 2in1: Hybrid format with both individual and team rankings
- Matches: Elimination bracket (4 or 8 teams)
- Kinteki: Traditional hit/miss scoring
- Enteki: Distance-based scoring (0, 3, 5, 7, 9, 10 points)
Statesman-based workflow:
- new - Tournament structure can be modified
- registration - Adding participants, teams, dojos
- marking - Score entry and validation
- tie_break - Resolving ties
- done - Tournament complete (immutable)
- taikai_admin - Full tournament control
- dojo_admin - Manage own dojo's participants
- chairman - Tournament director
- marking_referee - Score entry
- shajo_referee - Floor referee
- target_referee - Target judge
- More roles documented in docs/STAFF_ROLES.md
- Useful Notes - Development recipes and console commands
- Staff Roles - Role definitions and constraints
- Modernization Report - Rails 8 upgrade documentation
- Copilot Instructions - Comprehensive technical guide
- Create a feature branch
- Make your changes
- Run tests:
bin/rails testandbin/rails test:system - Run linters:
bin/rubocop - Submit a pull request
See LICENSE file for details.
This application was fully modernized to Rails 8 in January 2025:
- ✅ Removed Devise, implemented Rails 8 authentication
- ✅ Migrated to importmap-rails (from jsbundling-rails)
- ✅ Added Solid Queue, Solid Cache, Solid Cable
- ✅ Updated to Rails 8 code patterns and best practices
- ✅ Full test coverage maintained throughout migration
See MODERNIZATION_REPORT.md for detailed migration notes.