A Ruby on Rails application that allows schools to publish their placement preferences and providers to find schools that are open to, or intersted in hosting placements to facilitate more placements across the initial teacher training (ITT) market.
This service has different interfaces for schools and providers, whilst also allowing users with affiliations to multiple organisation types to switch between their organisations.
This service enables providers to find schools with placements based on the preferences that they list on the service.
- School interface: create and manage placement preferences, set dates/subjects/locations.
- Provider interface: find schools who the provider may want to work with.
- Rails 8.0.2 app (API+server-rendered views) using the GOV.UK Design System via
govuk-componentsandgovuk_design_system_formbuilder. - Two “service contexts”: schools and providers with distinct navigation and controllers.
- Database-backed sessions (
activerecord-session_store). - Solid Queue for background jobs; Mission Control Jobs UI for observability.
- Solid Cache (DB-backed) for Rails.cache.
- Geocoding by geocoder (e.g. for location-based search).
- Ruby: 3.4.10 (managed via asdf)
- Rails: 8.0.2
- DB: PostgreSQL (tested with 17.2)
- Job queue:
solid_queue - Cache:
solid_cache - Auth: OmniAuth OpenID Connect (DfE Sign-in)
- Assets:
jsbundling-rails+cssbundling-rails(Node + Yarn),propshaft - HTTP:
httparty - Pagination:
pagy - Presentation:
draper - Logging:
rails_semantic_logger - Deployment:
kamal
- Postgres 17.x (server + headers)
- NodeJS ≥ 18 and Yarn (via asdf)
- Build tools (for native gems)
macOS users: install
asdfvia Homebrew; Linux users: follow asdf docs.
This project depends on:
Install the required tools and then install versions pinned in .tool-versions:
brew install asdf # on macOS
asdf plugin add ruby
asdf plugin add nodejs
asdf plugin add yarn
asdf plugin add postgres
asdf installWhen installing the pg gem, Bundler executes outside the project directory and can miss the pinned Postgres version. To ensure pg compiles correctly use:
ASDF_POSTGRES_VERSION=17.2 bundle installWe use dotenv-rails in development and test. Create a .env file (not committed):
SIGN_IN_METHOD=persona
#SIGN_IN_METHOD=dfe-sign-in
APP_BASE_URL=localhost:3000
#DFE Sign in config
DFE_SIGN_IN_ISSUER_URL=https://dev-oidc.signin.education.gov.uk
DFE_SIGN_IN_CLIENT_ID=ittMentorSchoolPlacement
DFE_SIGN_IN_SECRET=TBD
GIAS_CSV_BASE_URL=https://ea-edubase-api-prod.azurewebsites.net/edubase/downloads/public
SOLID_QUEUE_IN_PUMA=trueCreate and seed the database:
bin/setup
# or
bundle exec rails db:create db:migrate db:seedInstall JS and CSS dependencies and build once:
yarn install
bin/rails assets:precompile # optional for dev; `bin/dev` will build on the flyFor local development we recommend bin/dev (Procfile-based) which runs Rails, JS and CSS watchers together:
bin/devThe app should be available at http://localhost:3000.
We use DfE Sign-in via OmniAuth OpenID Connect. Currently we sign in with personas in development; DfE-Sign in will be implemented in future.
We use RSpec with Capybara and Selenium WebDriver for system tests.
bundle exec rspecUseful notes:
- Screenshots on failure are saved by
capybara-screenshot. - Time-sensitive tests use
timecop. - External requests are stubbed with
webmock. - Controller specs are enabled via
rails-controller-testing.
- Ruby style:
rubocop-rails-omakase - ERB templates:
erb_lint - Security scanner:
brakeman
bin/lintWe bundle solargraph + solargraph-rails. After installing, index your bundle:
bin/bundle exec yard gemsVS Code setting:
{
"solargraph.useBundler": true
}We keep Architecture Decision Records in /adr. Generate a new ADR with:
bin/bundle exec rladr new "Title of the decision"The scheduled workflow defined in .github/workflows/validate-infra.yml runs Terraform plan validations for the AKS cluster plus domains infrastructure/environment each day at 07:00 UTC against production only. Failures and drift notifications are sent to the SD Infra alerts Teams channel via the TEAMS_WEBHOOK_URL_INFRA secret.