tsykvas_rails_template 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +200 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +589 -0
- data/Rakefile +17 -0
- data/lib/generators/tsykvas_rails_template/companions/companions_generator.rb +273 -0
- data/lib/generators/tsykvas_rails_template/concept/concept_generator.rb +145 -0
- data/lib/generators/tsykvas_rails_template/concept/templates/component/edit.html.slim.tt +5 -0
- data/lib/generators/tsykvas_rails_template/concept/templates/component/edit.rb.tt +11 -0
- data/lib/generators/tsykvas_rails_template/concept/templates/component/index.html.slim.tt +5 -0
- data/lib/generators/tsykvas_rails_template/concept/templates/component/index.rb.tt +11 -0
- data/lib/generators/tsykvas_rails_template/concept/templates/component/new.html.slim.tt +5 -0
- data/lib/generators/tsykvas_rails_template/concept/templates/component/new.rb.tt +11 -0
- data/lib/generators/tsykvas_rails_template/concept/templates/component/show.html.slim.tt +4 -0
- data/lib/generators/tsykvas_rails_template/concept/templates/component/show.rb.tt +11 -0
- data/lib/generators/tsykvas_rails_template/concept/templates/controller.rb.tt +45 -0
- data/lib/generators/tsykvas_rails_template/concept/templates/operation/create.rb.tt +31 -0
- data/lib/generators/tsykvas_rails_template/concept/templates/operation/destroy.rb.tt +13 -0
- data/lib/generators/tsykvas_rails_template/concept/templates/operation/edit.rb.tt +10 -0
- data/lib/generators/tsykvas_rails_template/concept/templates/operation/index.rb.tt +9 -0
- data/lib/generators/tsykvas_rails_template/concept/templates/operation/new.rb.tt +10 -0
- data/lib/generators/tsykvas_rails_template/concept/templates/operation/show.rb.tt +10 -0
- data/lib/generators/tsykvas_rails_template/concept/templates/operation/update.rb.tt +31 -0
- data/lib/generators/tsykvas_rails_template/install/bootstrap_installer.rb +225 -0
- data/lib/generators/tsykvas_rails_template/install/install_generator.rb +298 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/agents/buddy.md +157 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/agents/code-reviewer.md +117 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/agents/security-reviewer.md +113 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/agents/tech-lead.md +150 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/commands/check.md +51 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/commands/code-review.md +60 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/commands/docs-create.md +102 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/commands/pr-review.md +81 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/commands/pushit.md +160 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/commands/refactor.md +132 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/commands/task-sum.md +47 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/commands/tests.md +67 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/commands/tsykvas-claude.md +262 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/commands/update-docs.md +78 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/commands/update-rules.md +102 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/commands/update-tests.md +135 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/architecture.md +315 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/authentication.md +96 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/background-jobs.md +135 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/code-style.md +101 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/commands.md +34 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/companions.md +128 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/concepts-refactoring.md +194 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/database.md +135 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/deployment.md +138 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/design-system.md +322 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/documentation.md +89 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/forms.md +174 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/i18n.md +165 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/routing-and-namespaces.md +114 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/security.md +122 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/stimulus-controllers.md +166 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/testing-examples.md +180 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/testing.md +117 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/tsykvas_rails_template.md +280 -0
- data/lib/generators/tsykvas_rails_template/install/templates/.claude/docs/ui-components.md +196 -0
- data/lib/generators/tsykvas_rails_template/install/templates/CLAUDE.md.tt +81 -0
- data/lib/generators/tsykvas_rails_template/install/templates/app/concepts/base/component/base.rb +6 -0
- data/lib/generators/tsykvas_rails_template/install/templates/app/concepts/base/operation/base.rb +124 -0
- data/lib/generators/tsykvas_rails_template/install/templates/app/concepts/base/operation/result.rb +56 -0
- data/lib/generators/tsykvas_rails_template/install/templates/app/concepts/home/component/index.html.slim +49 -0
- data/lib/generators/tsykvas_rails_template/install/templates/app/concepts/home/component/index.rb +11 -0
- data/lib/generators/tsykvas_rails_template/install/templates/app/concepts/home/operation/index.rb +17 -0
- data/lib/generators/tsykvas_rails_template/install/templates/app/controllers/concerns/operations_methods.rb +148 -0
- data/lib/generators/tsykvas_rails_template/install/templates/app/controllers/home_controller.rb +10 -0
- data/lib/generators/tsykvas_rails_template/install/templates/app/policies/application_policy.rb +33 -0
- data/lib/generators/tsykvas_rails_template/install/templates/app/policies/home_policy.rb +8 -0
- data/lib/tasks/tsykvas.rake +11 -0
- data/lib/tsykvas_rails_template/probe.rb +236 -0
- data/lib/tsykvas_rails_template/railtie.rb +13 -0
- data/lib/tsykvas_rails_template/version.rb +5 -0
- data/lib/tsykvas_rails_template.rb +18 -0
- metadata +183 -0
data/README.md
ADDED
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
# tsykvas_rails_template
|
|
2
|
+
|
|
3
|
+
Opinionated Rails skeleton + Claude Code tooling, shipped as a gem with three
|
|
4
|
+
generators. Drop it into a fresh `rails new` (or an existing app), run the
|
|
5
|
+
install generator, and you get the same architectural baseline across every
|
|
6
|
+
project: thin controllers, plain-Ruby Operation/Component pattern, ViewComponent
|
|
7
|
+
+ Slim, Pundit, and a pre-loaded `.claude/` directory tailored to the host
|
|
8
|
+
stack on first run.
|
|
9
|
+
|
|
10
|
+
## What it ships
|
|
11
|
+
|
|
12
|
+
Four pillars:
|
|
13
|
+
|
|
14
|
+
1. **Thin-controller / `endpoint` DSL.** Controllers become one-liners:
|
|
15
|
+
`endpoint Crm::Property::Operation::Index, Crm::Property::Component::Index`.
|
|
16
|
+
The DSL handles HTML / JS / JSON / `format.any` dispatch, flash, redirects,
|
|
17
|
+
and Pundit authorization-check enforcement.
|
|
18
|
+
2. **`Base::Operation::Base` + `Base::Operation::Result`.** Plain-Ruby
|
|
19
|
+
alternative to Trailblazer. `authorize!` / `policy_scope` / `notice` /
|
|
20
|
+
`redirect_path=` / `model=` / `run_operation` baked in.
|
|
21
|
+
3. **`app/concepts/<feature>/{operation,component}/` layout.** Generators
|
|
22
|
+
scaffold this for you; `config.autoload_paths` is wired automatically.
|
|
23
|
+
`<Concept>::Form` documented for complex forms (virtual attributes,
|
|
24
|
+
sub-operation calls, multi-record submits).
|
|
25
|
+
4. **`.claude/` payload.** 4 subagents (`buddy`, `code-reviewer`,
|
|
26
|
+
`security-reviewer`, `tech-lead`), 12 slash commands (including
|
|
27
|
+
`/tsykvas-claude` which audits the host with a deterministic Ruby
|
|
28
|
+
probe and refreshes probe-driven sections in CLAUDE.md and the
|
|
29
|
+
architecture docs), and **20 architecture docs** that ship at install
|
|
30
|
+
under `.claude/docs/` (including the gem-canonical
|
|
31
|
+
`tsykvas_rails_template.md`, `forms.md`, and `companions.md`, plus 17
|
|
32
|
+
stack-tailoring references — `architecture`, `authentication`,
|
|
33
|
+
`background-jobs`, `code-style`, `commands`, `concepts-refactoring`,
|
|
34
|
+
`database`, `deployment`, `design-system`, `documentation`, `i18n`,
|
|
35
|
+
`routing-and-namespaces`, `security`, `stimulus-controllers`,
|
|
36
|
+
`testing`, `testing-examples`, `ui-components`).
|
|
37
|
+
|
|
38
|
+
## Compatibility
|
|
39
|
+
|
|
40
|
+
| Component | Required version |
|
|
41
|
+
|------------------|------------------|
|
|
42
|
+
| Ruby | `>= 3.2.0` |
|
|
43
|
+
| Rails | `>= 7.1` |
|
|
44
|
+
| Pundit | `>= 2.3` |
|
|
45
|
+
| view_component | `>= 3.0` |
|
|
46
|
+
| slim-rails | `>= 3.6` |
|
|
47
|
+
| bootstrap | `~> 5.3` |
|
|
48
|
+
| dartsass-rails | `>= 0.5` |
|
|
49
|
+
|
|
50
|
+
The CI matrix exercises Ruby 3.2 / 3.3 / 3.4 against Rails 7.1 / 7.2 / 8.0
|
|
51
|
+
(9 cells, `fail-fast: false`), plus a smoke job that generates a fresh
|
|
52
|
+
Rails app, installs the gem from path, and exercises every generator.
|
|
53
|
+
|
|
54
|
+
## Quickstart
|
|
55
|
+
|
|
56
|
+
From a fresh `rails new` to a Bootstrap-styled home page in five commands.
|
|
57
|
+
|
|
58
|
+
### 1. Create a fresh Rails app
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
rails new myapp
|
|
62
|
+
cd myapp
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Verify Ruby (`>= 3.2.0`) and Rails (`>= 7.1`) versions:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
bin/rails -v
|
|
69
|
+
ruby -v
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
You also need PostgreSQL running locally — the install generator swaps `sqlite3` → `pg` by default. (Pass `--keep-sqlite` to `:install` to opt out.) `pg_isready` should return success; if not, `brew services start postgresql@16` (or your version).
|
|
73
|
+
|
|
74
|
+
### 2. Add the gem
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
bundle add tsykvas_rails_template
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
For local-path iteration on the gem itself:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
bundle add tsykvas_rails_template --path "/abs/path/to/tsykvas_rails_template"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`bundle add` edits `Gemfile`, validates the source, and runs `bundle install` in one step. Don't `echo >> Gemfile` — it duplicates entries silently.
|
|
87
|
+
|
|
88
|
+
### 3. Run the install generator
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
bin/rails g tsykvas_rails_template:install
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
This runs `bundle install` (pulls Bootstrap, dartsass-rails, etc.), `bundle update bootstrap dartsass-rails` (lifts older lock-file pins to latest), `gem install foreman` system-wide if missing (so `bin/dev` works), and `bin/rails dartsass:build` (precompiles Bootstrap to `app/assets/builds/application.css`). The shipped `config/initializers/dartsass.rb` includes `--quiet-deps` and `--silence-deprecation=import` flags, so SCSS warnings don't pollute your terminal.
|
|
95
|
+
|
|
96
|
+
What lands in your app:
|
|
97
|
+
|
|
98
|
+
- `app/concepts/{base,home}/...` — base operation/component classes + a working Home example
|
|
99
|
+
- `app/controllers/application_controller.rb` — `include Pundit::Authorization` + `include OperationsMethods`
|
|
100
|
+
- `app/controllers/home_controller.rb` — one-liner `endpoint Home::Operation::Index, Home::Component::Index`
|
|
101
|
+
- `app/policies/{application,home}_policy.rb` — Pundit baseline + Home example
|
|
102
|
+
- `config/routes.rb` — `root "home#index"`
|
|
103
|
+
- `.claude/{agents,commands,docs}/` (4 subagents, 12 slash commands, **20 docs**) + `CLAUDE.md` (≤ 100 lines, fenced)
|
|
104
|
+
- `Gemfile` + `config/database.yml` — `sqlite3` → `pg` swap (use `--keep-sqlite` to opt out)
|
|
105
|
+
- Bootstrap 5.3: `bootstrap` + `dartsass-rails` gems, `app/assets/stylesheets/application.bootstrap.scss`, `config/initializers/dartsass.rb` build map, importmap pins for `bootstrap` + `@popperjs/core`, `Procfile.dev` with the SCSS watcher line, and `app/assets/builds/application.css` precompiled. Use `--skip-bootstrap` to opt out.
|
|
106
|
+
|
|
107
|
+
### 4. Create the database
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
bin/rails db:create
|
|
111
|
+
bin/rails db:migrate # nothing to migrate yet, but smoke-checks the connection
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 5. Boot the server
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
bin/rails server
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Open <http://localhost:3000>. **Verify it worked:**
|
|
121
|
+
|
|
122
|
+
- Centered Bootstrap card with shadow.
|
|
123
|
+
- Green success alert: "Bootstrap 5.3 is installed and configured…".
|
|
124
|
+
- Blue "Documentation" button (`.btn-primary`) + outline secondary "Open a Bootstrap modal".
|
|
125
|
+
- Click the modal button → modal slides in, centered. This proves `window.bootstrap` is loaded via importmap.
|
|
126
|
+
|
|
127
|
+
If the page is unstyled, check:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
ls app/assets/builds/application.css # should be ~230 KB
|
|
131
|
+
bin/rails dartsass:build # silent — re-runs the compile
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
For live SCSS reload during development, use `bin/dev` (Procfile.dev runs Puma + the dartsass watcher in parallel).
|
|
135
|
+
|
|
136
|
+
### Optional next steps
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
bin/rails g tsykvas_rails_template:companions # devise + simple_form + rspec stack + ...
|
|
140
|
+
bin/rails g tsykvas_rails_template:concept Crm::Property --controller
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Then in Claude Code, run `/tsykvas-claude` to refresh probe-driven sections in `CLAUDE.md` and `.claude/docs/*` against your actual stack (concept folders, gem versions, default branch, locale config). All 20 docs already shipped at install — `/tsykvas-claude` only swaps placeholders for real values; it does not regenerate or trim content.
|
|
144
|
+
|
|
145
|
+
## Installation
|
|
146
|
+
|
|
147
|
+
Use `bundle add` — it edits `Gemfile`, validates the source, and runs
|
|
148
|
+
`bundle install` in one step. Don't append the line with `echo >> Gemfile`;
|
|
149
|
+
that doesn't dedupe and silently produces a broken Gemfile if you re-run.
|
|
150
|
+
|
|
151
|
+
**Released gem (after `gem push`):**
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
bundle add tsykvas_rails_template
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Local-path during development:**
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
bundle add tsykvas_rails_template --path "/absolute/path/to/tsykvas_rails_template"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
If you see `the gem tsykvas_rails_template (>= 0) more than once` from
|
|
164
|
+
`bundle install`, you have a duplicate `gem "..."` line in your `Gemfile`
|
|
165
|
+
— delete the extra one and re-run `bundle install`.
|
|
166
|
+
|
|
167
|
+
Then run the install generator:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
bin/rails g tsykvas_rails_template:install
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The install generator will:
|
|
174
|
+
|
|
175
|
+
- Copy `app/concepts/base/operation/{base,result}.rb` and
|
|
176
|
+
`app/concepts/base/component/base.rb` into the host
|
|
177
|
+
- Copy `app/controllers/concerns/operations_methods.rb` (the `endpoint` DSL)
|
|
178
|
+
- Patch `config/application.rb` with
|
|
179
|
+
`config.autoload_paths += %W[#{config.root}/app/concepts]`
|
|
180
|
+
- Wire `include Pundit::Authorization` + `include OperationsMethods` into
|
|
181
|
+
`ApplicationController` unconditionally (the `endpoint` DSL falls back to
|
|
182
|
+
`try(:current_user)`, so it works on apps that haven't added Devise yet).
|
|
183
|
+
- Generate `app/policies/application_policy.rb` if missing
|
|
184
|
+
- Scaffold a Home example concept (`HomeController#index` one-liner +
|
|
185
|
+
`Home::Operation::Index` + `Home::Component::Index` + `HomePolicy` +
|
|
186
|
+
`root "home#index"` route) showing the canonical pattern end-to-end
|
|
187
|
+
- Drop the full `.claude/{agents,commands,docs}/` payload and a fenced
|
|
188
|
+
`CLAUDE.md` scaffold (≤ 100 lines, token-economy hard cap)
|
|
189
|
+
|
|
190
|
+
### Install-time flags
|
|
191
|
+
|
|
192
|
+
| Flag | Effect |
|
|
193
|
+
|-------------------------------|--------------------------------------------------------------|
|
|
194
|
+
| `--skip-application-policy` | Don't create `app/policies/application_policy.rb` if absent |
|
|
195
|
+
| `--skip-autoload-paths` | Don't patch `config/application.rb` |
|
|
196
|
+
| `--skip-home-example` | Don't scaffold the Home concept + `root "home#index"` route |
|
|
197
|
+
| `--keep-sqlite` | Don't swap `gem "sqlite3"` for `gem "pg"` in the Gemfile |
|
|
198
|
+
| `--skip-bootstrap` | Don't add `bootstrap` + `dartsass-rails`, SCSS entry, importmap pins, JS import, or `Procfile.dev` watcher |
|
|
199
|
+
| `--skip-claude` | Don't drop `.claude/` payload or `CLAUDE.md` |
|
|
200
|
+
|
|
201
|
+
## Troubleshooting
|
|
202
|
+
|
|
203
|
+
### `bundle install` says "the gem ... more than once"
|
|
204
|
+
|
|
205
|
+
Duplicate `gem "..."` line in `Gemfile`. Delete the dupe and re-run `bundle install`. This usually happens when `gem "..."` was appended via `echo >> Gemfile` instead of `bundle add`.
|
|
206
|
+
|
|
207
|
+
### `bin/dev` says "foreman: command not found"
|
|
208
|
+
|
|
209
|
+
The install generator auto-installs `foreman` system-wide via `gem install foreman --no-document` if missing. If install couldn't reach rubygems.org (sandboxed env, no internet), run that command manually.
|
|
210
|
+
|
|
211
|
+
### dartsass-rails prints `@import` deprecation warnings
|
|
212
|
+
|
|
213
|
+
The shipped `config/initializers/dartsass.rb` passes `--quiet-deps` (silences warnings from gem load paths — Bootstrap 5.3.x's internal SCSS) and `--silence-deprecation=import` (silences the lone warning on the user's own `@import "bootstrap"`). If you regenerated the initializer manually and lost those flags, re-run `bin/rails g tsykvas_rails_template:install --force` — it canonical-rewrites the initializer.
|
|
214
|
+
|
|
215
|
+
If Rails fails to boot with `SyntaxError` in the dartsass initializer, the file was left in a half-written state. Delete it (`rm config/initializers/dartsass.rb`) and re-run `:install`.
|
|
216
|
+
|
|
217
|
+
### `bin/rails db:create` fails with "could not connect to server"
|
|
218
|
+
|
|
219
|
+
The install swaps `sqlite3` → `pg` in `Gemfile` and `config/database.yml`. Either start PostgreSQL (`brew services start postgresql@16`) or pass `--keep-sqlite` to `:install`.
|
|
220
|
+
|
|
221
|
+
### `bin/rails zeitwerk:check` fails after install
|
|
222
|
+
|
|
223
|
+
Most likely an existing `app/concepts/<name>` folder doesn't autoload-clean. Either rename the folder so its constant matches Zeitwerk's expectations or remove the autoload patch from `config/application.rb`.
|
|
224
|
+
|
|
225
|
+
### `/tsykvas-claude` says probe rake task missing
|
|
226
|
+
|
|
227
|
+
Run `bin/rails g tsykvas_rails_template:install` first — the probe is provided by the gem's railtie, registered when the gem is in `Gemfile`. The slash command also requires `bundle exec rake tsykvas:probe` to be runnable from the host repo root.
|
|
228
|
+
|
|
229
|
+
### Bootstrap layout doesn't load on first request
|
|
230
|
+
|
|
231
|
+
Run `bin/rails dartsass:build` once before `bin/rails server`, OR use `bin/dev` (`Procfile.dev` runs the dartsass watcher alongside Puma so SCSS recompiles whenever you edit `application.bootstrap.scss`).
|
|
232
|
+
|
|
233
|
+
### Live changes to SCSS / JS aren't visible in dev (stale `public/assets/`)
|
|
234
|
+
|
|
235
|
+
Propshaft serves files from `public/assets/` whenever `public/assets/.manifest.json` exists, completely bypassing live `app/assets/builds/` and `app/javascript/`. A stale directory from a prior `rails assets:precompile` (perhaps run accidentally, or as part of a deploy rehearsal) silently freezes the dev environment.
|
|
236
|
+
|
|
237
|
+
Fix: delete it. The directory is `.gitignored` by default in Rails 8, so it's safe.
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
rm -rf public/assets
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
The install generator does this automatically when it sees `public/assets/` listed in `.gitignore`. If your project has the directory but doesn't `.gitignore` it, the generator leaves it alone (assumed checked-in content).
|
|
244
|
+
|
|
245
|
+
### Home page renders but Bootstrap modal doesn't open
|
|
246
|
+
|
|
247
|
+
`window.bootstrap` is wired via importmap (`pin "bootstrap"`) plus an explicit `import * as bootstrap from "bootstrap"; window.bootstrap = bootstrap` block in `app/javascript/application.js`. If you removed the block, the modal trigger button has nothing to call. Re-run `:install --force` to restore it, or add the import manually.
|
|
248
|
+
|
|
249
|
+
## Usage
|
|
250
|
+
|
|
251
|
+
### Recommended companions generator
|
|
252
|
+
|
|
253
|
+
After `:install`, run this to add the recommended gem set
|
|
254
|
+
(devise + simple_form + rspec stack + mini_magick + mission_control-jobs +
|
|
255
|
+
dotenv-rails) and run their `:install` sub-generators:
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
bin/rails g tsykvas_rails_template:companions
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
What gets added (default — all groups):
|
|
262
|
+
|
|
263
|
+
| Group | Gems | Post-install |
|
|
264
|
+
|---|---|---|
|
|
265
|
+
| `auth` | `devise`, `omniauth-rails_csrf_protection` | `rails g devise:install` (no User model — run `rails g devise User` yourself when ready) |
|
|
266
|
+
| `forms` | `simple_form` | `rails g simple_form:install` (with `--bootstrap` if Probe sees Bootstrap) |
|
|
267
|
+
| `images` | `mini_magick` | none (you must `brew install imagemagick` system-wide) |
|
|
268
|
+
| `jobs-ui` (gated on `:solid_queue`) | `mission_control-jobs` | mounts `MissionControl::Jobs::Engine` at `/jobs` with admin-only constraint (`User#admin?` via Warden) |
|
|
269
|
+
| `test` | `rspec-rails`, `factory_bot_rails`, `faker`, `shoulda-matchers`, `webmock` | `rails g rspec:install`, appends shoulda-matchers + WebMock config to `spec/rails_helper.rb` |
|
|
270
|
+
| `dev` | `dotenv-rails` | appends `.env` rules to `.gitignore` |
|
|
271
|
+
|
|
272
|
+
Opt-out flags: `--skip-auth`, `--skip-forms`, `--skip-images`,
|
|
273
|
+
`--skip-jobs-ui`, `--skip-test`, `--skip-dev`. Plus `--skip-bundle` (don't
|
|
274
|
+
run `bundle install`) and `--skip-post-install` (Gemfile edits only,
|
|
275
|
+
no sub-generators).
|
|
276
|
+
|
|
277
|
+
Idempotent: re-running won't duplicate Gemfile entries, re-run
|
|
278
|
+
sub-generators, or duplicate config injections.
|
|
279
|
+
|
|
280
|
+
If your stack is incompatible (Tailwind instead of Bootstrap with simple_form,
|
|
281
|
+
Minitest instead of RSpec, CanCanCan instead of Pundit), skip `:companions`
|
|
282
|
+
entirely. The gem's core (`:install` + `:concept`) is stack-agnostic.
|
|
283
|
+
|
|
284
|
+
Full per-gem reference: `.claude/docs/companions.md` (after install).
|
|
285
|
+
|
|
286
|
+
### Concept generator
|
|
287
|
+
|
|
288
|
+
Scaffold a new feature under `app/concepts/<path>/`:
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
bin/rails g tsykvas_rails_template:concept Crm::Property
|
|
292
|
+
bin/rails g tsykvas_rails_template:concept Property --controller
|
|
293
|
+
bin/rails g tsykvas_rails_template:concept Admin::User --actions index show
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Resulting tree:
|
|
297
|
+
|
|
298
|
+
```
|
|
299
|
+
app/concepts/crm/property/
|
|
300
|
+
├── operation/
|
|
301
|
+
│ ├── index.rb # Crm::Property::Operation::Index
|
|
302
|
+
│ ├── show.rb
|
|
303
|
+
│ ├── new.rb
|
|
304
|
+
│ ├── create.rb # raises NotImplementedError until you fill in `permit`
|
|
305
|
+
│ ├── edit.rb
|
|
306
|
+
│ ├── update.rb # same
|
|
307
|
+
│ └── destroy.rb
|
|
308
|
+
└── component/
|
|
309
|
+
├── index.rb # Crm::Property::Component::Index
|
|
310
|
+
├── index.html.slim
|
|
311
|
+
├── show.rb / show.html.slim
|
|
312
|
+
├── new.rb / new.html.slim
|
|
313
|
+
└── edit.rb / edit.html.slim
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
The scaffold deliberately raises `NotImplementedError` from the operation's
|
|
317
|
+
`_params` method — implement permitted attributes inline, or promote into a
|
|
318
|
+
`<Concept>::Form` object as documented in `.claude/docs/forms.md`. Empty
|
|
319
|
+
attribute lists fail loud, never silently save default values.
|
|
320
|
+
|
|
321
|
+
Input validation rejects empty, leading-`::`, or invalid-character names
|
|
322
|
+
with `Thor::Error` before any file is touched.
|
|
323
|
+
|
|
324
|
+
### Claude Code integration
|
|
325
|
+
|
|
326
|
+
Once `.claude/` is dropped, open the project in Claude Code and run:
|
|
327
|
+
|
|
328
|
+
```
|
|
329
|
+
/tsykvas-claude
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
That slash command:
|
|
333
|
+
|
|
334
|
+
1. Runs `bundle exec rake tsykvas:probe` to get a deterministic JSON inventory
|
|
335
|
+
of your stack (Ruby/Rails versions, default branch, template engine, auth,
|
|
336
|
+
authorization, API presence, Bootstrap presence, test framework, background
|
|
337
|
+
jobs, multi-DB setup, concept folders, ApplicationController includes,
|
|
338
|
+
API-only flag, Engine-host flag).
|
|
339
|
+
2. Reads existing `<!-- tsykvas-template:start ... -->` /
|
|
340
|
+
`<!-- tsykvas-template:end -->` fences in `CLAUDE.md` so re-runs only touch
|
|
341
|
+
gem-owned content; your hand-written sections outside fences are preserved.
|
|
342
|
+
3. Builds a refresh plan (placeholder substitutions, no regeneration of
|
|
343
|
+
content), shows you a unified diff (`/tsykvas-claude --dry-run` to
|
|
344
|
+
preview without writing).
|
|
345
|
+
4. Applies edits only after you confirm.
|
|
346
|
+
5. Verifies link integrity, fence balance, `bin/rails zeitwerk:check`, the
|
|
347
|
+
probe re-run match, and the **100-line cap on `CLAUDE.md`** (token-economy
|
|
348
|
+
hard gate — `CLAUDE.md` sits in every Claude session's context, so each
|
|
349
|
+
line is a per-prompt token cost; deep content lives in
|
|
350
|
+
`.claude/docs/<topic>.md`, linked from the routing table). If any check
|
|
351
|
+
fails, the run rolls back.
|
|
352
|
+
|
|
353
|
+
All 20 docs ship at install — `/tsykvas-claude` does **not** generate or
|
|
354
|
+
trim them. It only swaps placeholder values (e.g. `<your-app>` →
|
|
355
|
+
`todo_app`, `<app_name>_<env>` → `todo_app_<env>`) so docs reflect your
|
|
356
|
+
real stack. The shipped depth is intentional.
|
|
357
|
+
|
|
358
|
+
The other 11 slash commands (`/check`, `/code-review`, `/pr-review`,
|
|
359
|
+
`/refactor`, `/tests`, `/update-tests`, `/update-docs`, `/update-rules`,
|
|
360
|
+
`/pushit`, `/task-sum`, `/docs-create`) and 4 subagents are stack-agnostic
|
|
361
|
+
and ready to use immediately.
|
|
362
|
+
|
|
363
|
+
### Probing the host project
|
|
364
|
+
|
|
365
|
+
The `Probe` class is usable directly in scripts and CI:
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
bundle exec rake tsykvas:probe # JSON inventory of the host
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
```ruby
|
|
372
|
+
TsykvasRailsTemplate::Probe.run # returns a Hash (schema_version: 2)
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Sample output:
|
|
376
|
+
|
|
377
|
+
```json
|
|
378
|
+
{
|
|
379
|
+
"schema_version": 2,
|
|
380
|
+
"gem_version": "0.1.0",
|
|
381
|
+
"ruby_version": "3.4.7",
|
|
382
|
+
"rails_version": "8.0.2",
|
|
383
|
+
"default_branch": "main",
|
|
384
|
+
"api_only": false,
|
|
385
|
+
"engine_host": false,
|
|
386
|
+
"template_engine": "slim",
|
|
387
|
+
"auth": {
|
|
388
|
+
"devise": true,
|
|
389
|
+
"omniauth": false,
|
|
390
|
+
"omniauth_openid_connect": false,
|
|
391
|
+
"warden": false,
|
|
392
|
+
"jwt": false,
|
|
393
|
+
"basic_auth": false,
|
|
394
|
+
"custom_current_user": false,
|
|
395
|
+
"method": "devise"
|
|
396
|
+
},
|
|
397
|
+
"authorization": "pundit",
|
|
398
|
+
"has_api_v1": false,
|
|
399
|
+
"has_bootstrap": true,
|
|
400
|
+
"test_framework": "rspec",
|
|
401
|
+
"background_jobs": ["solid_queue"],
|
|
402
|
+
"databases": ["primary"],
|
|
403
|
+
"concept_folders": ["admin", "crm"],
|
|
404
|
+
"application_controller_includes": ["Pundit::Authorization", "OperationsMethods"]
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
## End-to-end workflow
|
|
409
|
+
|
|
410
|
+
The full pipeline from a fresh `rails new` to a productive feature loop:
|
|
411
|
+
|
|
412
|
+
```
|
|
413
|
+
rails new ──> bundle add ──> g install ──> g companions ──> g concept ──> /tsykvas-claude
|
|
414
|
+
↓ ↓
|
|
415
|
+
feature work ←── slash commands
|
|
416
|
+
↓
|
|
417
|
+
architecture evolves
|
|
418
|
+
↓
|
|
419
|
+
/update-rules
|
|
420
|
+
↓
|
|
421
|
+
/pushit
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
Step-by-step on a hypothetical `todo_app`:
|
|
425
|
+
|
|
426
|
+
1. **`rails new todo_app && cd todo_app`.**
|
|
427
|
+
2. **Add the gem:** `bundle add tsykvas_rails_template` (or
|
|
428
|
+
`bundle add tsykvas_rails_template --path "..."` during local development).
|
|
429
|
+
Pulls `pundit`, `view_component`, `slim-rails` as transitive deps and
|
|
430
|
+
runs `bundle install` for you.
|
|
431
|
+
3. **`bin/rails g tsykvas_rails_template:install`** — copies base classes,
|
|
432
|
+
patches `application.rb`, unconditionally wires `Pundit::Authorization` +
|
|
433
|
+
`OperationsMethods` into `ApplicationController` (the `endpoint` DSL
|
|
434
|
+
uses `try(:current_user)`, so it works with or without Devise), generates
|
|
435
|
+
`ApplicationPolicy` + `HomePolicy`, scaffolds the Home example concept +
|
|
436
|
+
`root "home#index"`, wires Bootstrap 5.3 + dartsass-rails (with silenced
|
|
437
|
+
SCSS deprecation flags) and pre-compiles the CSS bundle, auto-installs
|
|
438
|
+
`foreman` system-wide for `bin/dev`, cleans any stale `public/assets/`
|
|
439
|
+
that would shadow live asset reloads, and drops the full `.claude/`
|
|
440
|
+
payload (4 subagents, 12 slash commands, 20 architecture docs) plus a
|
|
441
|
+
fenced `CLAUDE.md` scaffold. `bin/rails zeitwerk:check` should pass
|
|
442
|
+
cleanly.
|
|
443
|
+
4. **`bin/rails db:create`** — install swapped `sqlite3` → `pg` in the
|
|
444
|
+
Gemfile and `config/database.yml`, so the host needs PostgreSQL running
|
|
445
|
+
locally and a database created. (Pass `--keep-sqlite` to `:install` if
|
|
446
|
+
you want to stay on SQLite — then this step is unnecessary.)
|
|
447
|
+
5. **`bin/rails server`** then visit `http://localhost:3000` — the Home
|
|
448
|
+
example renders the canonical `endpoint` flow end-to-end (controller →
|
|
449
|
+
operation → `HomePolicy#index?` → component → Slim template), styled
|
|
450
|
+
with Bootstrap (success alert + card + modal). Use `bin/dev` instead
|
|
451
|
+
when you want live SCSS reload (Procfile.dev runs Puma + dartsass:watch
|
|
452
|
+
in parallel).
|
|
453
|
+
6. **`bin/rails g tsykvas_rails_template:companions`** (optional) — adds
|
|
454
|
+
recommended gems (devise, simple_form, rspec stack, mini_magick,
|
|
455
|
+
mission_control-jobs, dotenv-rails) and runs their `:install`
|
|
456
|
+
sub-generators. Skip if your stack is incompatible.
|
|
457
|
+
7. **`bin/rails g tsykvas_rails_template:concept Crm::Property --controller`**
|
|
458
|
+
— scaffolds operations + components + Slim templates + thin controller.
|
|
459
|
+
8. **Open Claude Code and run `/tsykvas-claude`** — probe inventories
|
|
460
|
+
the project, fence-aware rewrite plan, dry-run diff preview, mandatory
|
|
461
|
+
verify phase. **Refreshes** placeholder values in `.claude/docs/` and
|
|
462
|
+
`CLAUDE.md` against the actual stack (concept folder names, gem
|
|
463
|
+
versions, branch names) — content depth is preserved, not regenerated.
|
|
464
|
+
9. **Feature work** with slash commands: `/check` before commit, `/refactor`
|
|
465
|
+
to keep code style, `/tests` to write missing specs, `/code-review`
|
|
466
|
+
before PR, `/pushit` to bundle the full pre-push pipeline.
|
|
467
|
+
10. **When base classes change:** `/update-rules` diffs them against `main`
|
|
468
|
+
and proposes targeted doc updates with a confirmation gate.
|
|
469
|
+
|
|
470
|
+
`.claude/docs/tsykvas_rails_template.md` (gem-canonical reference, ~210
|
|
471
|
+
lines) is the comprehensive guide Claude reads in any host project. Read
|
|
472
|
+
it whenever you want the full picture of how the gem is built.
|
|
473
|
+
|
|
474
|
+
## Architecture references
|
|
475
|
+
|
|
476
|
+
These docs ship with the gem; after `bin/rails g tsykvas_rails_template:install`
|
|
477
|
+
they appear under `.claude/docs/` in your project.
|
|
478
|
+
|
|
479
|
+
**Gem-canonical (kept verbatim by `/tsykvas-claude`):**
|
|
480
|
+
|
|
481
|
+
- **`tsykvas_rails_template.md`** — comprehensive 10-section gem reference (four pillars, generators, Probe, fences, `/tsykvas-claude` workflow, slash commands, gotchas, cross-doc index).
|
|
482
|
+
- **`forms.md`** — when to promote `_params` into `<Concept>::Form`.
|
|
483
|
+
- **`companions.md`** — per-gem rationale and opt-out matrix for `:companions`.
|
|
484
|
+
|
|
485
|
+
**Stack-tailoring references (refreshed by `/tsykvas-claude` against probe data):**
|
|
486
|
+
|
|
487
|
+
- **`architecture.md`** — Concepts Pattern, `endpoint` mechanics, Pundit, Operations / Components / Sortable.
|
|
488
|
+
- **`authentication.md`** — Devise wiring, custom registration flows, permitted parameters, redirects after sign-up.
|
|
489
|
+
- **`background-jobs.md`** — ActiveJob conventions, naming, recurring tasks, testing.
|
|
490
|
+
- **`code-style.md`** — Ruby / Rails style, I18n, git workflow, antipatterns.
|
|
491
|
+
- **`commands.md`** — dev / test / lint / deploy commands.
|
|
492
|
+
- **`concepts-refactoring.md`** — refactoring legacy controllers into the `endpoint` shape.
|
|
493
|
+
- **`database.md`** — multi-DB layout (primary / cache / queue / cable), migrations, enums, schema snapshot.
|
|
494
|
+
- **`deployment.md`** — Kamal config, secrets, SolidQueue topology, image / volume conventions.
|
|
495
|
+
- **`design-system.md`** — Bootstrap-default tokens, dark-mode switching, component catalog, antipatterns. Customise to your brand.
|
|
496
|
+
- **`documentation.md`** — documentation standards, what belongs where.
|
|
497
|
+
- **`i18n.md`** — locale-file structure, simple_form conventions, full-key rule, plural forms.
|
|
498
|
+
- **`routing-and-namespaces.md`** — REST + non-REST patterns, `crm_*_path` examples, route helpers vs controller fallbacks.
|
|
499
|
+
- **`security.md`** — strong parameters, CSP, Brakeman, bundler-audit, importmap audit, secrets.
|
|
500
|
+
- **`stimulus-controllers.md`** — Stimulus controller patterns, listener cleanup, Bootstrap-Stimulus integration.
|
|
501
|
+
- **`testing.md`** — RSpec setup, what NOT to test, factory traits.
|
|
502
|
+
- **`testing-examples.md`** — copy-paste spec templates (operation / component / model / policy / request).
|
|
503
|
+
- **`ui-components.md`** — `Base::Component::Btn`, `Table`, `TitleRow`, stat cards, forms, layouts.
|
|
504
|
+
|
|
505
|
+
## Frontend assumptions
|
|
506
|
+
|
|
507
|
+
The shipped `OperationsMethods` concern handles `format.js` for Bootstrap
|
|
508
|
+
modals (used in the new/edit flow). The Bootstrap JS is feature-checked
|
|
509
|
+
(`if (window.bootstrap && window.bootstrap.Modal) { ... }`) so apps without
|
|
510
|
+
Bootstrap globally available degrade silently — no crashes. If your stack
|
|
511
|
+
doesn't use Bootstrap modals at all, delete or replace the `format.js`
|
|
512
|
+
branch in your generated `app/controllers/concerns/operations_methods.rb`.
|
|
513
|
+
|
|
514
|
+
## Upgrade policy
|
|
515
|
+
|
|
516
|
+
The gem follows [SemVer](https://semver.org/):
|
|
517
|
+
|
|
518
|
+
- **MAJOR.0.0** — breaking changes to the *shape* of files the gem ships:
|
|
519
|
+
signatures of `Base::Operation::Base`, public API of the `endpoint` DSL,
|
|
520
|
+
the directory layout of `app/concepts/`, the schema of `Probe`, the fence
|
|
521
|
+
format in `CLAUDE.md.tt`. Per-version migration recipes will appear under
|
|
522
|
+
the `## Per-version migration` heading below.
|
|
523
|
+
- **0.MINOR.0** — new features, non-breaking. Existing host apps stay green
|
|
524
|
+
with no changes required.
|
|
525
|
+
- **0.0.PATCH** — bug fixes only.
|
|
526
|
+
|
|
527
|
+
### How to upgrade in general
|
|
528
|
+
|
|
529
|
+
1. Read the per-version notes below for the version you're moving to.
|
|
530
|
+
2. Bump the gem in your `Gemfile`: `gem "tsykvas_rails_template", "~> X.Y"`.
|
|
531
|
+
3. `bundle update tsykvas_rails_template`.
|
|
532
|
+
4. Re-run `bin/rails g tsykvas_rails_template:install` (it's idempotent —
|
|
533
|
+
won't duplicate `include` directives or `autoload_paths` entries).
|
|
534
|
+
5. Open Claude Code and run `/tsykvas-claude --dry-run` to preview any
|
|
535
|
+
doc changes inside fenced sections; apply if the diff looks right.
|
|
536
|
+
|
|
537
|
+
### Probe schema versions
|
|
538
|
+
|
|
539
|
+
| Gem version | Probe `schema_version` | What changed |
|
|
540
|
+
|---|---|---|
|
|
541
|
+
| 0.1.x (initial) | 1 | Initial schema. |
|
|
542
|
+
| 0.1.x (current) | 2 | Added `api_only`, `engine_host`, `databases`, broadened `auth` (now a Hash with `method` classification + `warden` / `jwt` / `basic_auth` / `custom_current_user` flags). |
|
|
543
|
+
|
|
544
|
+
### Per-version migration
|
|
545
|
+
|
|
546
|
+
#### → 0.1.0 (initial)
|
|
547
|
+
|
|
548
|
+
No prior version. Just install:
|
|
549
|
+
|
|
550
|
+
```ruby
|
|
551
|
+
# Gemfile
|
|
552
|
+
gem "tsykvas_rails_template", "~> 0.1"
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
```bash
|
|
556
|
+
bundle install
|
|
557
|
+
bin/rails g tsykvas_rails_template:install
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
#### → 0.2.0 (future)
|
|
561
|
+
|
|
562
|
+
When the first non-patch release ships, this section will list deprecation
|
|
563
|
+
warnings, method/file renames with sed-style migration commands,
|
|
564
|
+
fence-marker version bumps, and whether re-running the install generator
|
|
565
|
+
is mandatory or optional.
|
|
566
|
+
|
|
567
|
+
If you bump versions and something breaks that isn't covered here, open an
|
|
568
|
+
issue with the `before` and `after` `bundle exec rake tsykvas:probe` JSON,
|
|
569
|
+
the failing command, and the relevant `Gemfile.lock` diff.
|
|
570
|
+
|
|
571
|
+
## Development
|
|
572
|
+
|
|
573
|
+
```bash
|
|
574
|
+
bin/setup # install dev deps
|
|
575
|
+
bundle exec rake # rspec + rubocop
|
|
576
|
+
bundle exec rake build # package the gem
|
|
577
|
+
bundle exec rake audit # bundler-audit security check
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
## Contributing
|
|
581
|
+
|
|
582
|
+
Bug reports and pull requests are welcome. See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
|
|
583
|
+
|
|
584
|
+
Before opening a PR, run `bundle exec rake` locally — the same CI matrix
|
|
585
|
+
runs on every push.
|
|
586
|
+
|
|
587
|
+
## License
|
|
588
|
+
|
|
589
|
+
[MIT](LICENSE.txt). © Yurii Tsykvas.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "rspec/core/rake_task"
|
|
5
|
+
|
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
7
|
+
|
|
8
|
+
require "rubocop/rake_task"
|
|
9
|
+
|
|
10
|
+
RuboCop::RakeTask.new
|
|
11
|
+
|
|
12
|
+
desc "Audit Gemfile.lock against the public ruby-advisory-db for known CVEs"
|
|
13
|
+
task :audit do
|
|
14
|
+
sh "bundle exec bundle-audit check --update"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
task default: %i[spec rubocop]
|