plutonium 0.50.0 → 0.51.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 +4 -4
- data/.claude/skills/plutonium/SKILL.md +85 -102
- data/.claude/skills/plutonium-app/SKILL.md +572 -0
- data/.claude/skills/plutonium-auth/SKILL.md +163 -300
- data/.claude/skills/plutonium-behavior/SKILL.md +838 -0
- data/.claude/skills/plutonium-resource/SKILL.md +1176 -0
- data/.claude/skills/plutonium-tenancy/SKILL.md +655 -0
- data/.claude/skills/plutonium-testing/SKILL.md +6 -5
- data/.claude/skills/plutonium-ui/SKILL.md +900 -0
- data/CHANGELOG.md +27 -2
- data/Rakefile +2 -1
- data/app/assets/plutonium.css +1 -11
- data/app/assets/plutonium.js +1009 -1214
- data/app/assets/plutonium.js.map +3 -3
- data/app/assets/plutonium.min.js +52 -51
- data/app/assets/plutonium.min.js.map +3 -3
- data/docs/.vitepress/config.ts +37 -27
- data/docs/getting-started/index.md +22 -29
- data/docs/getting-started/installation.md +37 -80
- data/docs/getting-started/tutorial/index.md +4 -5
- data/docs/guides/adding-resources.md +66 -377
- data/docs/guides/authentication.md +94 -463
- data/docs/guides/authorization.md +124 -370
- data/docs/guides/creating-packages.md +94 -296
- data/docs/guides/custom-actions.md +121 -441
- data/docs/guides/index.md +22 -42
- data/docs/guides/multi-tenancy.md +116 -187
- data/docs/guides/nested-resources.md +103 -431
- data/docs/guides/search-filtering.md +123 -240
- data/docs/guides/testing.md +5 -4
- data/docs/guides/theming.md +157 -407
- data/docs/guides/troubleshooting.md +5 -3
- data/docs/guides/user-invites.md +106 -425
- data/docs/guides/user-profile.md +76 -243
- data/docs/index.md +1 -1
- data/docs/reference/app/generators.md +517 -0
- data/docs/reference/app/index.md +158 -0
- data/docs/reference/app/packages.md +146 -0
- data/docs/reference/app/portals.md +377 -0
- data/docs/reference/auth/accounts.md +230 -0
- data/docs/reference/auth/index.md +88 -0
- data/docs/reference/auth/profile.md +185 -0
- data/docs/reference/behavior/controllers.md +395 -0
- data/docs/reference/behavior/index.md +22 -0
- data/docs/reference/behavior/interactions.md +341 -0
- data/docs/reference/behavior/policies.md +417 -0
- data/docs/reference/index.md +56 -49
- data/docs/reference/resource/actions.md +423 -0
- data/docs/reference/resource/definition.md +508 -0
- data/docs/reference/resource/index.md +50 -0
- data/docs/reference/resource/model.md +348 -0
- data/docs/reference/resource/query.md +305 -0
- data/docs/reference/tenancy/entity-scoping.md +361 -0
- data/docs/reference/tenancy/index.md +36 -0
- data/docs/reference/tenancy/invites.md +393 -0
- data/docs/reference/tenancy/nested-resources.md +267 -0
- data/docs/reference/testing/index.md +287 -0
- data/docs/reference/ui/assets.md +400 -0
- data/docs/reference/ui/components.md +165 -0
- data/docs/reference/ui/displays.md +104 -0
- data/docs/reference/ui/forms.md +284 -0
- data/docs/reference/ui/index.md +30 -0
- data/docs/reference/ui/layouts.md +106 -0
- data/docs/reference/ui/pages.md +189 -0
- data/docs/reference/ui/tables.md +117 -0
- data/docs/superpowers/specs/2026-05-09-typeahead-endpoint-design.md +203 -0
- data/docs/superpowers/specs/2026-05-12-skill-compaction-design.md +99 -0
- data/docs/superpowers/specs/2026-05-13-docs-restructure-design.md +186 -0
- data/gemfiles/rails_7.gemfile.lock +1 -1
- data/gemfiles/rails_8.0.gemfile.lock +1 -1
- data/gemfiles/rails_8.1.gemfile.lock +1 -1
- data/lib/generators/pu/core/update/update_generator.rb +0 -20
- data/lib/generators/pu/invites/install_generator.rb +1 -0
- data/lib/plutonium/definition/base.rb +1 -1
- data/lib/plutonium/definition/{views.rb → index_views.rb} +21 -20
- data/lib/plutonium/helpers/turbo_helper.rb +11 -0
- data/lib/plutonium/helpers/turbo_stream_actions_helper.rb +14 -0
- data/lib/plutonium/resource/controller.rb +1 -0
- data/lib/plutonium/resource/controllers/crud_actions.rb +19 -1
- data/lib/plutonium/resource/controllers/typeahead.rb +180 -0
- data/lib/plutonium/resource/policy.rb +7 -0
- data/lib/plutonium/routing/mapper_extensions.rb +15 -0
- data/lib/plutonium/ui/component/methods.rb +4 -0
- data/lib/plutonium/ui/form/base.rb +6 -2
- data/lib/plutonium/ui/form/components/json.rb +58 -0
- data/lib/plutonium/ui/form/components/resource_select.rb +62 -8
- data/lib/plutonium/ui/form/components/secure_association.rb +98 -22
- data/lib/plutonium/ui/form/concerns/typeahead_attributes.rb +83 -0
- data/lib/plutonium/ui/form/resource.rb +0 -4
- data/lib/plutonium/ui/grid/resource.rb +1 -1
- data/lib/plutonium/ui/layout/base.rb +1 -0
- data/lib/plutonium/ui/page/base.rb +0 -7
- data/lib/plutonium/ui/page/index.rb +4 -4
- data/lib/plutonium/ui/table/resource.rb +1 -1
- data/lib/plutonium/version.rb +1 -1
- data/lib/plutonium.rb +8 -0
- data/lib/tasks/release.rake +15 -1
- data/package.json +10 -10
- data/src/css/slim_select.css +4 -0
- data/src/js/controllers/slim_select_controller.js +61 -0
- data/src/js/turbo/turbo_actions.js +33 -0
- data/yarn.lock +553 -543
- metadata +44 -33
- data/.claude/skills/plutonium-assets/SKILL.md +0 -512
- data/.claude/skills/plutonium-controller/SKILL.md +0 -396
- data/.claude/skills/plutonium-create-resource/SKILL.md +0 -303
- data/.claude/skills/plutonium-definition/SKILL.md +0 -1223
- data/.claude/skills/plutonium-entity-scoping/SKILL.md +0 -317
- data/.claude/skills/plutonium-forms/SKILL.md +0 -465
- data/.claude/skills/plutonium-installation/SKILL.md +0 -331
- data/.claude/skills/plutonium-interaction/SKILL.md +0 -413
- data/.claude/skills/plutonium-invites/SKILL.md +0 -408
- data/.claude/skills/plutonium-model/SKILL.md +0 -440
- data/.claude/skills/plutonium-nested-resources/SKILL.md +0 -360
- data/.claude/skills/plutonium-package/SKILL.md +0 -198
- data/.claude/skills/plutonium-policy/SKILL.md +0 -456
- data/.claude/skills/plutonium-portal/SKILL.md +0 -410
- data/.claude/skills/plutonium-views/SKILL.md +0 -651
- data/docs/reference/assets/index.md +0 -496
- data/docs/reference/controller/index.md +0 -412
- data/docs/reference/definition/actions.md +0 -462
- data/docs/reference/definition/fields.md +0 -383
- data/docs/reference/definition/index.md +0 -326
- data/docs/reference/definition/query.md +0 -351
- data/docs/reference/generators/index.md +0 -648
- data/docs/reference/interaction/index.md +0 -449
- data/docs/reference/model/features.md +0 -248
- data/docs/reference/model/index.md +0 -218
- data/docs/reference/policy/index.md +0 -456
- data/docs/reference/portal/index.md +0 -379
- data/docs/reference/views/forms.md +0 -411
- data/docs/reference/views/index.md +0 -544
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
# Generators
|
|
2
|
+
|
|
3
|
+
Plutonium's `pu:*` CLI generators. Discoverable via `rails g pu:<tab>`. Always pass `--dest=` to skip prompts.
|
|
4
|
+
|
|
5
|
+
## Catalog
|
|
6
|
+
|
|
7
|
+
| Generator | Purpose |
|
|
8
|
+
|---|---|
|
|
9
|
+
| [`pu:core:install`](#pu-core-install) | Initial Plutonium setup (base classes, config, layouts) |
|
|
10
|
+
| [`pu:core:assets`](#pu-core-assets) | Set up custom Tailwind + Stimulus toolchain |
|
|
11
|
+
| [`pu:res:scaffold`](#pu-res-scaffold) | New resource (model, migration, controller, policy, definition) |
|
|
12
|
+
| [`pu:res:conn`](#pu-res-conn) | Connect a resource to a portal |
|
|
13
|
+
| [`pu:pkg:package`](#pu-pkg-package) | New feature package |
|
|
14
|
+
| [`pu:pkg:portal`](#pu-pkg-portal) | New portal package |
|
|
15
|
+
| [`pu:rodauth:install`](#pu-rodauth-install) | Install Rodauth base |
|
|
16
|
+
| [`pu:rodauth:account`](#pu-rodauth-account) | Basic Rodauth account |
|
|
17
|
+
| [`pu:rodauth:admin`](#pu-rodauth-admin) | Hardened admin account (2FA, lockout, audit) |
|
|
18
|
+
| [`pu:saas:setup`](#pu-saas-setup) | **Meta** — user + entity + membership + portal + profile + welcome + invites |
|
|
19
|
+
| [`pu:saas:user`](#individual-saas-generators) | Individual: SaaS user account |
|
|
20
|
+
| [`pu:saas:entity`](#individual-saas-generators) | Individual: entity model |
|
|
21
|
+
| [`pu:saas:membership`](#individual-saas-generators) | Individual: membership join model |
|
|
22
|
+
| [`pu:saas:portal`](#individual-saas-generators) | Individual: entity-scoped portal |
|
|
23
|
+
| [`pu:saas:welcome`](#individual-saas-generators) | Individual: onboarding / select-entity flow |
|
|
24
|
+
| [`pu:saas:api_client`](#pu-saas-api-client) | API client for M2M auth |
|
|
25
|
+
| [`pu:profile:install`](#pu-profile-install) | Profile resource + security section |
|
|
26
|
+
| [`pu:profile:setup`](#pu-profile-setup) | Meta — `pu:profile:install` + `pu:profile:conn` |
|
|
27
|
+
| [`pu:profile:conn`](#pu-profile-conn) | Connect profile to a portal as a singular resource |
|
|
28
|
+
| [`pu:invites:install`](#pu-invites-install) | User invitations package |
|
|
29
|
+
| [`pu:invites:invitable`](#pu-invites-invitable) | Mark a model as invitable |
|
|
30
|
+
| [`pu:eject:layout`](#pu-eject-layout) | Eject base layout for customization |
|
|
31
|
+
| [`pu:eject:shell`](#pu-eject-shell) | Eject topbar/sidebar partials |
|
|
32
|
+
| [`pu:test:install`](#pu-test-install) | Install `Plutonium::Testing` scaffolding |
|
|
33
|
+
| [`pu:test:scaffold`](#pu-test-scaffold) | Scaffold integration tests per (resource × portal) |
|
|
34
|
+
| [`pu:core:update`](#pu-core-update) | Update plutonium gem + npm package |
|
|
35
|
+
| [`pu:skills:sync`](#pu-skills-sync) | Sync Plutonium Claude skills into the project |
|
|
36
|
+
|
|
37
|
+
::: tip Unattended mode
|
|
38
|
+
All generators block on prompts by default. Pass `--dest=`, `--auth=`, `--force`, `--skip-bundle`, `--quiet` etc. to run in scripts/CI. See [App › Unattended execution](./index#unattended-execution).
|
|
39
|
+
:::
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Resource generators
|
|
44
|
+
|
|
45
|
+
### `pu:res:scaffold`
|
|
46
|
+
|
|
47
|
+
Generate a complete resource: model, migration, controller, policy, definition.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
rails g pu:res:scaffold Post user:belongs_to title:string 'content:text?' --dest=main_app
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
| Option | Description |
|
|
54
|
+
|---|---|
|
|
55
|
+
| `--dest=NAME` | Destination package (`main_app` or `<package>`) — required for unattended runs |
|
|
56
|
+
| `--no-model` | Skip model file (for existing models) |
|
|
57
|
+
| `--no-migration` | Skip migration (use with `--no-model` for existing schema) |
|
|
58
|
+
|
|
59
|
+
Field type syntax — full reference in [Resource › Model](/reference/resource/model). Quick recap:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
'name:string' # required string
|
|
63
|
+
'name:string?' # nullable
|
|
64
|
+
'company:belongs_to' # association
|
|
65
|
+
'parent:belongs_to?' # nullable association
|
|
66
|
+
'email:string:uniq' # with unique index
|
|
67
|
+
'amount:decimal{10,2}' # decimal with precision
|
|
68
|
+
'status:string{default:draft}' # with default value
|
|
69
|
+
'metadata:jsonb{default:{}}' # JSON default
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Quote any field containing `?` or `{}` to prevent shell expansion.
|
|
73
|
+
|
|
74
|
+
### `pu:res:conn`
|
|
75
|
+
|
|
76
|
+
Connect a resource to a portal. Generates portal-specific controller, policy, definition + route registration.
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
rails g pu:res:conn Post Comment Tag --dest=admin_portal
|
|
80
|
+
rails g pu:res:conn Blogging::Post --dest=admin_portal # namespaced
|
|
81
|
+
rails g pu:res:conn Profile --dest=customer_portal --singular # singleton
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
| Option | Description |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `--dest=PORTAL` | Target portal (required) |
|
|
87
|
+
| `--singular` | Register as singular resource (`/profile`, no `:id`, no index) |
|
|
88
|
+
|
|
89
|
+
::: tip Run after migrations
|
|
90
|
+
The generator reads model columns to seed the policy's `permitted_attributes_for_*`.
|
|
91
|
+
:::
|
|
92
|
+
|
|
93
|
+
See [Portals › Connecting resources](./portals#connecting-resources-pu-res-conn) for full details.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Package generators
|
|
98
|
+
|
|
99
|
+
### `pu:pkg:package`
|
|
100
|
+
|
|
101
|
+
Feature package — models, policies, definitions, interactions.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
rails g pu:pkg:package blogging
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
See [Packages › Feature packages](./packages#feature-packages).
|
|
108
|
+
|
|
109
|
+
### `pu:pkg:portal`
|
|
110
|
+
|
|
111
|
+
Portal package — controllers, views, routes, auth.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
rails g pu:pkg:portal admin --auth=user
|
|
115
|
+
rails g pu:pkg:portal admin --auth=admin --scope=Organization
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
| Option | Description |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `--auth=NAME` | Rodauth account to authenticate with |
|
|
121
|
+
| `--public` | Public access (no auth) |
|
|
122
|
+
| `--byo` | Bring your own auth |
|
|
123
|
+
| `--scope=CLASS` | Entity class for multi-tenancy |
|
|
124
|
+
|
|
125
|
+
See [Portals › Creating a portal](./portals#creating-a-portal).
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Authentication generators
|
|
130
|
+
|
|
131
|
+
### `pu:rodauth:install`
|
|
132
|
+
|
|
133
|
+
Install the Rodauth base — Roda app, base plugin, controller, layout, PostgreSQL extension migration.
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
rails g pu:rodauth:install
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### `pu:rodauth:account`
|
|
140
|
+
|
|
141
|
+
Basic Rodauth account with configurable features.
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
rails g pu:rodauth:account user # interactive
|
|
145
|
+
rails g pu:rodauth:account user --defaults # standard features
|
|
146
|
+
rails g pu:rodauth:account user --kitchen_sink # ALL features
|
|
147
|
+
rails g pu:rodauth:account api_user --api_only --jwt --jwt_refresh
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
For full option tables (features, defaults, individual feature flags) see [Auth › Accounts](/reference/auth/accounts).
|
|
151
|
+
|
|
152
|
+
### `pu:rodauth:admin`
|
|
153
|
+
|
|
154
|
+
Hardened admin account — pre-configured with multi-phase login, required TOTP, recovery codes, lockout, active sessions, audit logging, role-based access, invite interaction, and **no public signup**.
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
rails g pu:rodauth:admin admin
|
|
158
|
+
rails g pu:rodauth:admin admin --roles=super_admin,admin,viewer
|
|
159
|
+
rails g pu:rodauth:admin admin --extra-attributes=name:string,department:string
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
| Option | Default | Description |
|
|
163
|
+
|---|---|---|
|
|
164
|
+
| `--roles` | `super_admin,admin` | Comma-separated roles (positional enum, index 0 most privileged) |
|
|
165
|
+
| `--extra_attributes` | | Additional model attributes |
|
|
166
|
+
|
|
167
|
+
Creates a rake task for account creation:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
rails rodauth_admin:create[admin@example.com,password123]
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## SaaS generators
|
|
176
|
+
|
|
177
|
+
### `pu:saas:setup`
|
|
178
|
+
|
|
179
|
+
**Meta-generator.** Creates the user + entity + membership trio AND runs:
|
|
180
|
+
|
|
181
|
+
- `pu:saas:portal` → entity-scoped `{Entity}Portal`
|
|
182
|
+
- `pu:profile:setup` → profile model + association
|
|
183
|
+
- `pu:saas:welcome` → onboarding / select-entity flow
|
|
184
|
+
- `pu:invites:install` → invitations package
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
rails g pu:saas:setup --user Customer --entity Organization
|
|
188
|
+
rails g pu:saas:setup --user Customer --entity Organization --roles=member,admin
|
|
189
|
+
rails g pu:saas:setup --user Customer --entity Organization --no-allow-signup
|
|
190
|
+
rails g pu:saas:setup --user Customer --entity Organization \
|
|
191
|
+
--user-attributes=name:string --entity-attributes=slug:string
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
| Option | Default | Description |
|
|
195
|
+
|---|---|---|
|
|
196
|
+
| `--user=NAME` | (required) | User account model name |
|
|
197
|
+
| `--entity=NAME` | (required) | Entity model name |
|
|
198
|
+
| `--allow-signup` | `true` | Allow public registration |
|
|
199
|
+
| `--roles` | `admin,member` | Additional roles — **`owner` always prepended as index 0** |
|
|
200
|
+
| `--skip-entity` | | Skip entity model generation |
|
|
201
|
+
| `--skip-membership` | | Skip membership model generation |
|
|
202
|
+
| `--user-attributes` | | Additional user model attributes |
|
|
203
|
+
| `--entity-attributes` | | Additional entity model attributes |
|
|
204
|
+
| `--membership-attributes` | | Additional membership model attributes |
|
|
205
|
+
| `--api_client=NAME` | | Also generate an API client model |
|
|
206
|
+
| `--api_client_roles` | `read_only,write,admin` | API client roles |
|
|
207
|
+
|
|
208
|
+
::: warning Don't re-run pieces manually
|
|
209
|
+
`pu:saas:setup` chains four other generators. Don't re-run portal / profile / welcome / invites separately after this. Pass `--force` to re-run the whole thing.
|
|
210
|
+
:::
|
|
211
|
+
|
|
212
|
+
### Individual SaaS generators
|
|
213
|
+
|
|
214
|
+
For when you don't want the full `pu:saas:setup` meta-generator:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
rails g pu:saas:user Customer
|
|
218
|
+
rails g pu:saas:entity Organization --extra-attributes=slug:string
|
|
219
|
+
rails g pu:saas:membership --user Customer --entity Organization --roles=member,admin
|
|
220
|
+
rails g pu:saas:portal customer --entity Organization
|
|
221
|
+
rails g pu:saas:welcome --user Customer --entity Organization
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### `pu:saas:api_client`
|
|
225
|
+
|
|
226
|
+
API client account for machine-to-machine authentication. HTTP Basic Auth with auto-generated password.
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
rails g pu:saas:api_client ApiClient
|
|
230
|
+
rails g pu:saas:api_client ApiClient --entity=Organization
|
|
231
|
+
rails g pu:saas:api_client ApiClient --entity=Organization --roles=read_only,write,admin
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
| Option | Default | Description |
|
|
235
|
+
|---|---|---|
|
|
236
|
+
| `--entity=NAME` | | Entity to scope API clients to |
|
|
237
|
+
| `--roles` | `read_only,write,admin` | Available roles |
|
|
238
|
+
| `--extra_attributes` | | Additional model attributes |
|
|
239
|
+
| `--dest` | `main_app` | Destination package |
|
|
240
|
+
|
|
241
|
+
CLI creation:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
rake api_clients:create LOGIN=my-service
|
|
245
|
+
rake api_clients:create LOGIN=my-service ORGANIZATION=acme ROLE=write
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
::: warning Credentials shown once
|
|
249
|
+
The auto-generated password is displayed once at creation and cannot be retrieved later (`SecureRandom.base64(32)`).
|
|
250
|
+
:::
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Profile generators
|
|
255
|
+
|
|
256
|
+
See [Auth › Profile](/reference/auth/profile) for the full profile feature.
|
|
257
|
+
|
|
258
|
+
### `pu:profile:install`
|
|
259
|
+
|
|
260
|
+
Generate the profile resource (model, migration, controller, policy, definition) and modify the user model to add `has_one :profile`.
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
rails g pu:profile:install bio:text avatar:attachment 'timezone:string?' --dest=customer
|
|
264
|
+
rails g pu:profile:install AccountSettings bio:text --dest=main_app # custom resource name
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
| Option | Default | Description |
|
|
268
|
+
|---|---|---|
|
|
269
|
+
| `--dest=DEST` | (prompts) | Target package or `main_app` |
|
|
270
|
+
| `--user-model=NAME` | `User` | Rodauth user model |
|
|
271
|
+
|
|
272
|
+
### `pu:profile:setup`
|
|
273
|
+
|
|
274
|
+
Meta — runs `pu:profile:install` + `pu:profile:conn` in one shot.
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
rails g pu:profile:setup date_of_birth:date bio:text \
|
|
278
|
+
--dest=competition \
|
|
279
|
+
--portal=competition_portal
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### `pu:profile:conn`
|
|
283
|
+
|
|
284
|
+
Connect the profile resource to a portal as a **singular** resource (registers `/profile` and the `profile_url` helper).
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
rails g pu:profile:conn --dest=customer_portal
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Invite generators
|
|
293
|
+
|
|
294
|
+
See [Tenancy › Invites](/reference/tenancy/invites) for the full invitation system.
|
|
295
|
+
|
|
296
|
+
### `pu:invites:install`
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
rails g pu:invites:install
|
|
300
|
+
rails g pu:invites:install --entity-model=Organization --user-model=Customer --invite-model=OrganizationInvite
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
| Option | Default | Description |
|
|
304
|
+
|---|---|---|
|
|
305
|
+
| `--entity-model=NAME` | `Entity` | Entity model name |
|
|
306
|
+
| `--user-model=NAME` | `User` | User model name |
|
|
307
|
+
| `--invite-model=NAME` | `<EntityModel><UserModel>Invite` | Invite class name |
|
|
308
|
+
| `--membership-model=NAME` | `EntityUser` | Membership join model |
|
|
309
|
+
| `--roles` | `member,admin` | Comma-separated |
|
|
310
|
+
| `--rodauth=NAME` | `user` | Rodauth configuration for signup |
|
|
311
|
+
| `--enforce-domain` | `false` | Require email domain to match entity |
|
|
312
|
+
|
|
313
|
+
Multiple invite flows are supported — run `pu:invites:install` once per flow.
|
|
314
|
+
|
|
315
|
+
### `pu:invites:invitable`
|
|
316
|
+
|
|
317
|
+
Mark an app model as invitable (gets notified when an invite is accepted via `on_invite_accepted`).
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
rails g pu:invites:invitable Tenant
|
|
321
|
+
rails g pu:invites:invitable TeamMember --role=member
|
|
322
|
+
rails g pu:invites:invitable Tenant --dest=my_package
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
| Option | Default | Description |
|
|
326
|
+
|---|---|---|
|
|
327
|
+
| `--role=ROLE` | `member` | Role to assign to invited users |
|
|
328
|
+
| `--user-model=NAME` | `User` | User model |
|
|
329
|
+
| `--membership-model=NAME` | `EntityUser` | Membership join model |
|
|
330
|
+
| `--dest=PACKAGE` | `main_app` | Destination package |
|
|
331
|
+
| `--[no-]email-templates` | `true` | Generate custom email templates |
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Core generators
|
|
336
|
+
|
|
337
|
+
### `pu:core:install`
|
|
338
|
+
|
|
339
|
+
Initial Plutonium setup. Creates base classes, config initializer, layouts. Run once per app.
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
rails g pu:core:install
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### `pu:core:assets`
|
|
346
|
+
|
|
347
|
+
Set up the custom Tailwind + Stimulus toolchain. Installs npm packages, creates `tailwind.config.js`, imports Plutonium CSS, registers Stimulus controllers.
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
rails g pu:core:assets
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
See [UI › Assets](/reference/ui/assets) for what gets configured.
|
|
354
|
+
|
|
355
|
+
### `pu:core:update`
|
|
356
|
+
|
|
357
|
+
Update the plutonium gem and npm package together.
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
rails g pu:core:update
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## Eject generators
|
|
366
|
+
|
|
367
|
+
### `pu:eject:layout`
|
|
368
|
+
|
|
369
|
+
Copy the base layout template into your portal for direct editing.
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
rails g pu:eject:layout
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### `pu:eject:shell`
|
|
376
|
+
|
|
377
|
+
Copy the topbar/sidebar partials into your portal for direct editing.
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
rails g pu:eject:shell --dest=admin_portal
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
See [UI › Layouts](/reference/ui/layouts).
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Test generators
|
|
388
|
+
|
|
389
|
+
See [Testing](/reference/testing/) for the full testing toolkit.
|
|
390
|
+
|
|
391
|
+
### `pu:test:install`
|
|
392
|
+
|
|
393
|
+
Install the testing scaffolding (require `plutonium/testing` in `test_helper.rb`; create `test/support/plutonium_testing.rb` for overrides). Run once.
|
|
394
|
+
|
|
395
|
+
```bash
|
|
396
|
+
rails g pu:test:install
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### `pu:test:scaffold`
|
|
400
|
+
|
|
401
|
+
Scaffold integration tests — one file per (resource × portal) pairing.
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
rails g pu:test:scaffold Blogging::Post --portals=admin,org
|
|
405
|
+
rails g pu:test:scaffold Blogging::Post --portals=admin --concerns=crud,policy,definition
|
|
406
|
+
rails g pu:test:scaffold Blogging::Post --portals=org --parent=organization --dest=blogging
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
| Option | Default | Description |
|
|
410
|
+
|---|---|---|
|
|
411
|
+
| `--portals=admin,org` | (required) | Emit one file per portal |
|
|
412
|
+
| `--concerns=...` | `crud,policy,definition` | Concerns to include (full list: `crud,policy,definition,nested,model,interaction,portal_access`) |
|
|
413
|
+
| `--parent=organization` | | Wires the `NestedResource` parent |
|
|
414
|
+
| `--dest=...` | `main_app` | Output destination |
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## Skill generators
|
|
419
|
+
|
|
420
|
+
### `pu:skills:sync`
|
|
421
|
+
|
|
422
|
+
Sync Plutonium's Claude Code skills into the project (`.claude/skills/`). Run when upgrading the gem.
|
|
423
|
+
|
|
424
|
+
```bash
|
|
425
|
+
rails g pu:skills:sync
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
## Common workflows
|
|
431
|
+
|
|
432
|
+
### Full app setup
|
|
433
|
+
|
|
434
|
+
```bash
|
|
435
|
+
# 1. Plutonium template (greenfield) — does all initial setup
|
|
436
|
+
rails new myapp -a propshaft -j esbuild -c tailwind \
|
|
437
|
+
-m https://radioactive-labs.github.io/plutonium-core/templates/plutonium.rb
|
|
438
|
+
|
|
439
|
+
# Or for existing app:
|
|
440
|
+
rails generate pu:core:install
|
|
441
|
+
rails generate pu:rodauth:install
|
|
442
|
+
|
|
443
|
+
# 2. Account types
|
|
444
|
+
rails generate pu:rodauth:admin admin
|
|
445
|
+
# (or pu:saas:setup for multi-tenant)
|
|
446
|
+
|
|
447
|
+
# 3. Resources
|
|
448
|
+
rails generate pu:res:scaffold Post title:string body:text --dest=main_app
|
|
449
|
+
rails generate pu:res:scaffold Comment body:text post:belongs_to --dest=main_app
|
|
450
|
+
|
|
451
|
+
# 4. Portal
|
|
452
|
+
rails generate pu:pkg:portal admin --auth=admin
|
|
453
|
+
|
|
454
|
+
# 5. Connect resources
|
|
455
|
+
rails generate pu:res:conn Post Comment --dest=admin_portal
|
|
456
|
+
|
|
457
|
+
# 6. Migrate
|
|
458
|
+
rails db:migrate
|
|
459
|
+
|
|
460
|
+
# 7. Create the first admin
|
|
461
|
+
rails rodauth_admin:create[admin@example.com,password123]
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Adding a new resource
|
|
465
|
+
|
|
466
|
+
```bash
|
|
467
|
+
rails g pu:res:scaffold Product name:string price_cents:integer --dest=main_app
|
|
468
|
+
rails db:migrate
|
|
469
|
+
rails g pu:res:conn Product --dest=admin_portal
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### Adding a new portal
|
|
473
|
+
|
|
474
|
+
```bash
|
|
475
|
+
rails g pu:pkg:portal customer --auth=user --scope=Organization
|
|
476
|
+
rails g pu:res:conn Order --dest=customer_portal
|
|
477
|
+
rails db:migrate
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
## Undoing generators
|
|
481
|
+
|
|
482
|
+
```bash
|
|
483
|
+
rails destroy pu:res:scaffold Post
|
|
484
|
+
rails destroy pu:pkg:portal admin
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
## Troubleshooting
|
|
488
|
+
|
|
489
|
+
### Generator not found
|
|
490
|
+
|
|
491
|
+
Ensure Plutonium is installed and bundle is up to date:
|
|
492
|
+
|
|
493
|
+
```ruby
|
|
494
|
+
# Gemfile
|
|
495
|
+
gem "plutonium"
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
bundle install
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### Package not found
|
|
503
|
+
|
|
504
|
+
Generators run from Rails root. Package names are case-sensitive.
|
|
505
|
+
|
|
506
|
+
### Migration already exists
|
|
507
|
+
|
|
508
|
+
If a migration with the same timestamp exists, wait a second and retry — Rails generates timestamps to one-second resolution.
|
|
509
|
+
|
|
510
|
+
## Related
|
|
511
|
+
|
|
512
|
+
- [Packages](./packages) — feature vs portal package structure
|
|
513
|
+
- [Portals](./portals) — portal configuration and resource connection
|
|
514
|
+
- [Resource › Model](/reference/resource/model) — field-type syntax for `pu:res:scaffold`
|
|
515
|
+
- [Auth](/reference/auth/) — account type configuration
|
|
516
|
+
- [Tenancy](/reference/tenancy/) — multi-tenancy and invitations
|
|
517
|
+
- [Testing](/reference/testing/) — test scaffolding
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# App Reference
|
|
2
|
+
|
|
3
|
+
How a Plutonium app is assembled: installation, the package system (feature vs portal), portal engines, route registration, and connecting resources to portals.
|
|
4
|
+
|
|
5
|
+
## Sub-pages
|
|
6
|
+
|
|
7
|
+
- [Packages](./packages) — feature vs portal packages, structure, namespacing, package loading
|
|
8
|
+
- [Portals](./portals) — portal engines, mounting, controller concerns, `register_resource` (including singular and custom routes), connecting resources via `pu:res:conn`
|
|
9
|
+
- [Generators](./generators) — full `pu:*` generator catalog
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
### New Rails app (recommended)
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
rails new myapp -a propshaft -j esbuild -c tailwind \
|
|
17
|
+
-m https://radioactive-labs.github.io/plutonium-core/templates/plutonium.rb
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Configures Rails + Propshaft + esbuild + TailwindCSS + Plutonium in one shot.
|
|
21
|
+
|
|
22
|
+
### Existing Rails app
|
|
23
|
+
|
|
24
|
+
::: danger Existing app → `base.rb`, not `plutonium.rb`
|
|
25
|
+
The `plutonium.rb` template re-runs the full app bootstrap (dotenv, annotate, solid_*, asset config) and creates generic "initial commit" commits that clobber history. For any pre-existing app, always use `base.rb`.
|
|
26
|
+
:::
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Template
|
|
30
|
+
bin/rails app:template \
|
|
31
|
+
LOCATION=https://radioactive-labs.github.io/plutonium-core/templates/base.rb
|
|
32
|
+
|
|
33
|
+
# Or manual — add `gem "plutonium"` to Gemfile, then:
|
|
34
|
+
bundle install
|
|
35
|
+
rails generate pu:core:install
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Full setup workflow
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# 1. Core install — base controllers, policies, definitions, layouts
|
|
42
|
+
rails generate pu:core:install
|
|
43
|
+
|
|
44
|
+
# 2. Auth (if needed)
|
|
45
|
+
rails generate pu:rodauth:install
|
|
46
|
+
rails generate pu:rodauth:account user
|
|
47
|
+
|
|
48
|
+
# 3. Portal
|
|
49
|
+
rails generate pu:pkg:portal admin --auth=user
|
|
50
|
+
|
|
51
|
+
# 4. First resource
|
|
52
|
+
rails generate pu:res:scaffold Post user:belongs_to title:string 'content:text?' --dest=main_app
|
|
53
|
+
rails db:migrate
|
|
54
|
+
|
|
55
|
+
# 5. Connect resource to portal
|
|
56
|
+
rails generate pu:res:conn Post --dest=admin_portal
|
|
57
|
+
|
|
58
|
+
# 6. Mount portal in config/routes.rb
|
|
59
|
+
# mount AdminPortal::Engine, at: "/admin"
|
|
60
|
+
|
|
61
|
+
# 7. Start
|
|
62
|
+
bin/dev # uses Procfile to run Rails + CSS watcher
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Visit `http://localhost:3000/admin`.
|
|
66
|
+
|
|
67
|
+
## What `pu:core:install` creates
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
app/
|
|
71
|
+
├── controllers/
|
|
72
|
+
│ ├── plutonium_controller.rb # non-resource base
|
|
73
|
+
│ └── resource_controller.rb # CRUD base — see Behavior › Controllers
|
|
74
|
+
├── definitions/resource_definition.rb
|
|
75
|
+
├── interactions/resource_interaction.rb
|
|
76
|
+
├── models/resource_record.rb # abstract model — includes Plutonium::Resource::Record
|
|
77
|
+
├── policies/resource_policy.rb
|
|
78
|
+
└── views/layouts/resource.html.erb
|
|
79
|
+
|
|
80
|
+
config/
|
|
81
|
+
├── initializers/plutonium.rb
|
|
82
|
+
└── packages.rb # auto-loads packages/**/lib/engine.rb
|
|
83
|
+
|
|
84
|
+
packages/.keep
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The base classes (`ResourceController`, `ResourcePolicy`, `ResourceDefinition`, `ResourceRecord`, `ResourceInteraction`) are where you put app-wide defaults; resource-specific subclasses come from `pu:res:scaffold`.
|
|
88
|
+
|
|
89
|
+
## Configuration
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
# config/initializers/plutonium.rb
|
|
93
|
+
Plutonium.configure do |config|
|
|
94
|
+
config.load_defaults 1.0
|
|
95
|
+
|
|
96
|
+
# Hot reloading (defaults to true in development)
|
|
97
|
+
# config.enable_hotreload = true
|
|
98
|
+
|
|
99
|
+
# Cache discovery (defaults to true in production, false in development)
|
|
100
|
+
# config.cache_discovery = false
|
|
101
|
+
|
|
102
|
+
# Page chrome. Default :modern (topbar + icon rail).
|
|
103
|
+
# :classic preserves the legacy header + sidebar (only when upgrading).
|
|
104
|
+
# config.shell = :classic
|
|
105
|
+
|
|
106
|
+
# Custom assets — see UI › Assets
|
|
107
|
+
# config.assets.stylesheet = "custom_stylesheet"
|
|
108
|
+
# config.assets.script = "custom_script"
|
|
109
|
+
# config.assets.logo = "custom_logo.png"
|
|
110
|
+
# config.assets.favicon = "custom_favicon.ico"
|
|
111
|
+
end
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Converting an existing model to a resource
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
# 1. Include the module on your model
|
|
118
|
+
class Post < ApplicationRecord
|
|
119
|
+
include Plutonium::Resource::Record
|
|
120
|
+
end
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# 2. Generate supporting files (skips model + migration)
|
|
125
|
+
rails g pu:res:scaffold Post --no-migration --dest=main_app
|
|
126
|
+
|
|
127
|
+
# 3. Connect to portal
|
|
128
|
+
rails g pu:res:conn Post --dest=admin_portal
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Verifying installation
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
rails runner "puts Plutonium::VERSION"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Unattended execution
|
|
138
|
+
|
|
139
|
+
Plutonium generators are interactive by default. For scripts, agents, or CI, pass:
|
|
140
|
+
|
|
141
|
+
| Flag | Generators | Purpose |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| `--dest=main_app` / `--dest=<package>` | `pu:res:scaffold`, `pu:res:conn`, package-targeted generators | Skip "select destination" prompt |
|
|
144
|
+
| `--force` | any | Overwrite conflicting files (required when re-running `pu:saas:setup` or meta-generators) |
|
|
145
|
+
| `--auth=<account>` / `--public` / `--byo` | `pu:pkg:portal` | Skip auth-type prompt |
|
|
146
|
+
| `--skip-bundle` | gem-installing generators | Avoid mid-run `bundle install` |
|
|
147
|
+
| `--quiet` | most | Reduce output noise |
|
|
148
|
+
|
|
149
|
+
Meta-generators (`pu:saas:setup`) propagate these flags to the generators they chain. Always pass `--force` when re-running a meta-generator on an app that already has some of its outputs.
|
|
150
|
+
|
|
151
|
+
## Related
|
|
152
|
+
|
|
153
|
+
- [Packages](./packages) — feature vs portal package structure
|
|
154
|
+
- [Portals](./portals) — portal engines, routing, resource connection
|
|
155
|
+
- [Generators](./generators) — full generator reference
|
|
156
|
+
- [Auth](/reference/auth/) — Rodauth setup and account types
|
|
157
|
+
- [UI › Assets](/reference/ui/assets) — Tailwind, Stimulus, design tokens
|
|
158
|
+
- [Tutorial](/getting-started/tutorial/) — step-by-step walkthrough
|