|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## What this repository is |
| 6 | + |
| 7 | +A Rails Application Template (https://guides.rubyonrails.org/rails_application_templates.html) — not a Rails app itself. The output is consumed by `rails new <app> -m template.rb` to scaffold a Nimble-flavored Rails 7.1 / Ruby 3.3.1 project. There is no `app/`, no database, and no server to run here. The "running" version of this project is the *generated app* produced from it. |
| 8 | + |
| 9 | +Two variants are produced from the same template: |
| 10 | +- **Web variant** (default) — full-stack with Node 22 / Yarn assets |
| 11 | +- **API variant** — `rails new --api`, JSON-only |
| 12 | + |
| 13 | +Both share a common base; variant-specific code lives under `.template/variants/{api,web}/`. |
| 14 | + |
| 15 | +## Repository layout (load-bearing pieces) |
| 16 | + |
| 17 | +- `template.rb` — entry point invoked by `rails new -m`. Reads ENV/options, asks the user about optional addons, applies base files, default addons, optional addons, then variant. Can also be invoked with `ADDON=<name>` to apply a single addon to an existing app. |
| 18 | +- `Gemfile.tt`, `Makefile.tt`, `README.md.tt`, `.ruby-version.tt`, etc. — root-level Thor templates copied into the generated app. The `.tt` extension matters: Thor processes ERB inside them. |
| 19 | +- `bin/template.rb`, `config/template.rb`, `spec/template.rb`, `.gitignore.rb` — sub-templates `apply`-ed by the root `template.rb`. They each `use_source_path __dir__` and copy the files in their own folder into the generated app. |
| 20 | +- `.template/` — everything that isn't part of the base scaffold: |
| 21 | + - `addons/{docker,heroku,devise,bootstrap,slim,nginx,phrase,openapi,github,hotwire,svgeez,crud,custom_cops}/template.rb` — each is a Thor sub-template applied conditionally. `docker` and `heroku` are default; the rest are prompted (or auto-included by `crud`). |
| 22 | + - `variants/{api,web}/template.rb` — branch the generated app based on `--api` flag. |
| 23 | + - `hooks/before_complete/{fix_rubocop,report}.rb` — run last, after all addons. |
| 24 | + - `lib/template.rb` + `lib/thor_utils.rb` — shared helpers (`Template::Messages`, `Template::Errors`, `ThorUtils.ignore_tt` for copying `.tt` files verbatim). |
| 25 | + - `spec/` — RSpec + serverspec tests that run **inside the generated Docker container** to verify the generated app is correctly structured. Specs live under `spec/{base,addons/base,addons/variants/{api,web},variants/{api,web}}/`. |
| 26 | + - `Gemfile` — gems for the test harness only (`rspec`, `rspec-wait`, `serverspec`, `docker-api`, `rubocop`). |
| 27 | + - `.rubocop.yml` — linter config for the template files themselves (looser than the rules shipped to the generated app). |
| 28 | +- `rubocop/custom_cops/{required_inverse_of_relations,class_template}.rb` — custom cops shipped to generated apps via the `custom_cops` addon. Required by the root `.rubocop.yml` so they also lint *this* repo. The `rubocop/` folder has its own Gemfile for cop-development workflow. |
| 29 | +- `spec/` (top-level) — copied verbatim into the generated app as its starter test suite (rails_helper, fabricators, support, etc.). Not the test suite for *this* repo. |
| 30 | + |
| 31 | +## Architecture: how the template runs |
| 32 | + |
| 33 | +1. `template.rb` is loaded by `rails new -m` (or `rails app:template`). When invoked via URL, `remote_repository` clones the repo into a tmpdir and uses that as `template_root`. |
| 34 | +2. `apply_template!` orchestrates the order: base files → `bin/`, `config/`, `spec/`, `.gitignore` sub-templates → `after_bundle` block (runs `spring stop`, applies `spec/template.rb`) → default addons (docker, heroku) → prompt for optional addons → apply optional addons → variant template → custom cops → before-complete hooks. |
| 35 | +3. `use_source_path` prepends a directory to Thor's `@source_paths` so `template`/`copy_file`/`directory` calls resolve relative to the right folder. Sub-templates always start with `use_source_path __dir__`. |
| 36 | +4. The CRUD addon is special: it implies Devise + Bootstrap + Slim, and is applied near the end (after variants). |
| 37 | +5. `ENV['ADDON']=<name>` short-circuits the full flow and applies just one addon — used by `rails app:template ADDON=...` for retrofitting existing apps. |
| 38 | + |
| 39 | +When adding a new addon: create `.template/addons/<name>/template.rb`, add the prompt in `ask_for_optional_addons`, dispatch in `apply_optional_addons`, and add specs under `.template/spec/addons/base/<name>/` (or under variants if it differs per variant). |
| 40 | + |
| 41 | +## Common commands |
| 42 | + |
| 43 | +Generate an app from this checkout (for manual testing): |
| 44 | +```sh |
| 45 | +make create_web APP_NAME=my-app # answers Y to all prompts |
| 46 | +make create_api APP_NAME=my-app # api variant |
| 47 | +make cleanup APP_NAME=my-app # rm -rf the generated app |
| 48 | +``` |
| 49 | +`OPTIONS=` can be passed through, e.g. `make create_web APP_NAME=foo OPTIONS="--skip-git"`. |
| 50 | + |
| 51 | +Run the template's test suite (must be done **after** generating an app — the specs run against the generated container): |
| 52 | +```sh |
| 53 | +make build APP_NAME=my-app # builds docker-compose.test.yml images |
| 54 | +make test_template APP_NAME=my-app VARIANT=web # or VARIANT=api |
| 55 | +make test_variant_app APP_NAME=my-app # runs the generated app's own rspec inside docker |
| 56 | +``` |
| 57 | +The `test_template` target spins up `db` + `redis`, starts the generated app's container, and runs serverspec tests against it from `.template/`. It auto-selects spec patterns based on `VARIANT`. |
| 58 | + |
| 59 | +Lint the template Ruby itself: |
| 60 | +```sh |
| 61 | +bundle install # in repo root |
| 62 | +bundle exec rubocop --config .template/.rubocop.yml --parallel |
| 63 | +``` |
| 64 | +This is what CI (`.github/workflows/test_template.yml`) runs. Note the **two** rubocop configs: |
| 65 | +- `.rubocop.yml` (root) — linter config that gets copied to the generated app |
| 66 | +- `.template/.rubocop.yml` — linter config for the template's own Ruby code (`template.rb`, addon `template.rb` files, hooks) |
| 67 | + |
| 68 | +Custom cops have their own setup: |
| 69 | +```sh |
| 70 | +cd rubocop && bundle install && bundle exec rspec |
| 71 | +``` |
| 72 | + |
| 73 | +Run a single template spec (after building the container): |
| 74 | +```sh |
| 75 | +cd .template && bundle exec rspec spec/base/template_spec.rb |
| 76 | +cd .template && bundle exec rspec spec/addons/base/devise/template_spec.rb -e 'when something' |
| 77 | +``` |
| 78 | + |
| 79 | +## Conventions worth knowing |
| 80 | + |
| 81 | +- **`.tt` files** are Thor ERB templates; **`.rb` files inside `.template/`** named `template.rb` (or `Gemfile.rb`, `package.json.rb`, etc.) are sub-templates `apply`-ed from the parent. Don't confuse the two. |
| 82 | +- Files that need to land in the generated app *with* a `.tt` extension preserved (e.g. `.ruby-version.tt`, `.tool-versions.tt`) are handled via `ThorUtils.ignore_tt { copy_file ... }` — see `lib/thor_utils.rb`. |
| 83 | +- Custom cop `CustomCops/ClassTemplate` enforces a class layout convention; `CustomCops/RequiredInverseOfRelations` enforces `inverse_of:` on Rails associations. Both ship to generated apps. |
| 84 | +- Rubocop `Layout/LineLength` is 130; spec files are exempt from line-length and block-length limits. |
| 85 | +- `RSpec/ContextWording` requires `when` or `given` prefixes (no `with`/`without`). |
| 86 | +- The generated app uses Postgres 16.3 + Redis 7.0.9 (versions pinned in `template.rb` constants). |
0 commit comments