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.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/plutonium/SKILL.md +85 -102
  3. data/.claude/skills/plutonium-app/SKILL.md +572 -0
  4. data/.claude/skills/plutonium-auth/SKILL.md +163 -300
  5. data/.claude/skills/plutonium-behavior/SKILL.md +838 -0
  6. data/.claude/skills/plutonium-resource/SKILL.md +1176 -0
  7. data/.claude/skills/plutonium-tenancy/SKILL.md +655 -0
  8. data/.claude/skills/plutonium-testing/SKILL.md +6 -5
  9. data/.claude/skills/plutonium-ui/SKILL.md +900 -0
  10. data/CHANGELOG.md +27 -2
  11. data/Rakefile +2 -1
  12. data/app/assets/plutonium.css +1 -11
  13. data/app/assets/plutonium.js +1009 -1214
  14. data/app/assets/plutonium.js.map +3 -3
  15. data/app/assets/plutonium.min.js +52 -51
  16. data/app/assets/plutonium.min.js.map +3 -3
  17. data/docs/.vitepress/config.ts +37 -27
  18. data/docs/getting-started/index.md +22 -29
  19. data/docs/getting-started/installation.md +37 -80
  20. data/docs/getting-started/tutorial/index.md +4 -5
  21. data/docs/guides/adding-resources.md +66 -377
  22. data/docs/guides/authentication.md +94 -463
  23. data/docs/guides/authorization.md +124 -370
  24. data/docs/guides/creating-packages.md +94 -296
  25. data/docs/guides/custom-actions.md +121 -441
  26. data/docs/guides/index.md +22 -42
  27. data/docs/guides/multi-tenancy.md +116 -187
  28. data/docs/guides/nested-resources.md +103 -431
  29. data/docs/guides/search-filtering.md +123 -240
  30. data/docs/guides/testing.md +5 -4
  31. data/docs/guides/theming.md +157 -407
  32. data/docs/guides/troubleshooting.md +5 -3
  33. data/docs/guides/user-invites.md +106 -425
  34. data/docs/guides/user-profile.md +76 -243
  35. data/docs/index.md +1 -1
  36. data/docs/reference/app/generators.md +517 -0
  37. data/docs/reference/app/index.md +158 -0
  38. data/docs/reference/app/packages.md +146 -0
  39. data/docs/reference/app/portals.md +377 -0
  40. data/docs/reference/auth/accounts.md +230 -0
  41. data/docs/reference/auth/index.md +88 -0
  42. data/docs/reference/auth/profile.md +185 -0
  43. data/docs/reference/behavior/controllers.md +395 -0
  44. data/docs/reference/behavior/index.md +22 -0
  45. data/docs/reference/behavior/interactions.md +341 -0
  46. data/docs/reference/behavior/policies.md +417 -0
  47. data/docs/reference/index.md +56 -49
  48. data/docs/reference/resource/actions.md +423 -0
  49. data/docs/reference/resource/definition.md +508 -0
  50. data/docs/reference/resource/index.md +50 -0
  51. data/docs/reference/resource/model.md +348 -0
  52. data/docs/reference/resource/query.md +305 -0
  53. data/docs/reference/tenancy/entity-scoping.md +361 -0
  54. data/docs/reference/tenancy/index.md +36 -0
  55. data/docs/reference/tenancy/invites.md +393 -0
  56. data/docs/reference/tenancy/nested-resources.md +267 -0
  57. data/docs/reference/testing/index.md +287 -0
  58. data/docs/reference/ui/assets.md +400 -0
  59. data/docs/reference/ui/components.md +165 -0
  60. data/docs/reference/ui/displays.md +104 -0
  61. data/docs/reference/ui/forms.md +284 -0
  62. data/docs/reference/ui/index.md +30 -0
  63. data/docs/reference/ui/layouts.md +106 -0
  64. data/docs/reference/ui/pages.md +189 -0
  65. data/docs/reference/ui/tables.md +117 -0
  66. data/docs/superpowers/specs/2026-05-09-typeahead-endpoint-design.md +203 -0
  67. data/docs/superpowers/specs/2026-05-12-skill-compaction-design.md +99 -0
  68. data/docs/superpowers/specs/2026-05-13-docs-restructure-design.md +186 -0
  69. data/gemfiles/rails_7.gemfile.lock +1 -1
  70. data/gemfiles/rails_8.0.gemfile.lock +1 -1
  71. data/gemfiles/rails_8.1.gemfile.lock +1 -1
  72. data/lib/generators/pu/core/update/update_generator.rb +0 -20
  73. data/lib/generators/pu/invites/install_generator.rb +1 -0
  74. data/lib/plutonium/definition/base.rb +1 -1
  75. data/lib/plutonium/definition/{views.rb → index_views.rb} +21 -20
  76. data/lib/plutonium/helpers/turbo_helper.rb +11 -0
  77. data/lib/plutonium/helpers/turbo_stream_actions_helper.rb +14 -0
  78. data/lib/plutonium/resource/controller.rb +1 -0
  79. data/lib/plutonium/resource/controllers/crud_actions.rb +19 -1
  80. data/lib/plutonium/resource/controllers/typeahead.rb +180 -0
  81. data/lib/plutonium/resource/policy.rb +7 -0
  82. data/lib/plutonium/routing/mapper_extensions.rb +15 -0
  83. data/lib/plutonium/ui/component/methods.rb +4 -0
  84. data/lib/plutonium/ui/form/base.rb +6 -2
  85. data/lib/plutonium/ui/form/components/json.rb +58 -0
  86. data/lib/plutonium/ui/form/components/resource_select.rb +62 -8
  87. data/lib/plutonium/ui/form/components/secure_association.rb +98 -22
  88. data/lib/plutonium/ui/form/concerns/typeahead_attributes.rb +83 -0
  89. data/lib/plutonium/ui/form/resource.rb +0 -4
  90. data/lib/plutonium/ui/grid/resource.rb +1 -1
  91. data/lib/plutonium/ui/layout/base.rb +1 -0
  92. data/lib/plutonium/ui/page/base.rb +0 -7
  93. data/lib/plutonium/ui/page/index.rb +4 -4
  94. data/lib/plutonium/ui/table/resource.rb +1 -1
  95. data/lib/plutonium/version.rb +1 -1
  96. data/lib/plutonium.rb +8 -0
  97. data/lib/tasks/release.rake +15 -1
  98. data/package.json +10 -10
  99. data/src/css/slim_select.css +4 -0
  100. data/src/js/controllers/slim_select_controller.js +61 -0
  101. data/src/js/turbo/turbo_actions.js +33 -0
  102. data/yarn.lock +553 -543
  103. metadata +44 -33
  104. data/.claude/skills/plutonium-assets/SKILL.md +0 -512
  105. data/.claude/skills/plutonium-controller/SKILL.md +0 -396
  106. data/.claude/skills/plutonium-create-resource/SKILL.md +0 -303
  107. data/.claude/skills/plutonium-definition/SKILL.md +0 -1223
  108. data/.claude/skills/plutonium-entity-scoping/SKILL.md +0 -317
  109. data/.claude/skills/plutonium-forms/SKILL.md +0 -465
  110. data/.claude/skills/plutonium-installation/SKILL.md +0 -331
  111. data/.claude/skills/plutonium-interaction/SKILL.md +0 -413
  112. data/.claude/skills/plutonium-invites/SKILL.md +0 -408
  113. data/.claude/skills/plutonium-model/SKILL.md +0 -440
  114. data/.claude/skills/plutonium-nested-resources/SKILL.md +0 -360
  115. data/.claude/skills/plutonium-package/SKILL.md +0 -198
  116. data/.claude/skills/plutonium-policy/SKILL.md +0 -456
  117. data/.claude/skills/plutonium-portal/SKILL.md +0 -410
  118. data/.claude/skills/plutonium-views/SKILL.md +0 -651
  119. data/docs/reference/assets/index.md +0 -496
  120. data/docs/reference/controller/index.md +0 -412
  121. data/docs/reference/definition/actions.md +0 -462
  122. data/docs/reference/definition/fields.md +0 -383
  123. data/docs/reference/definition/index.md +0 -326
  124. data/docs/reference/definition/query.md +0 -351
  125. data/docs/reference/generators/index.md +0 -648
  126. data/docs/reference/interaction/index.md +0 -449
  127. data/docs/reference/model/features.md +0 -248
  128. data/docs/reference/model/index.md +0 -218
  129. data/docs/reference/policy/index.md +0 -456
  130. data/docs/reference/portal/index.md +0 -379
  131. data/docs/reference/views/forms.md +0 -411
  132. data/docs/reference/views/index.md +0 -544
@@ -1,410 +0,0 @@
1
- ---
2
- name: plutonium-portal
3
- description: Use BEFORE creating a portal, mounting a portal engine, running pu:pkg:portal, configuring entity strategies, or routing portal-specific resources. For tenancy mechanics, also load plutonium-entity-scoping.
4
- ---
5
-
6
- # Plutonium Portals
7
-
8
- ## 🚨 Critical (read first)
9
- - **Use `pu:pkg:portal`.** Never hand-craft a portal engine — the generator wires up the controller concerns, routes, and layout.
10
- - **Always use `pu:res:conn` to connect resources to portals** — resources are invisible until connected. Pass resources directly (not via `--src`) to skip prompts.
11
- - **Entity scoping is portal-level.** `scope_to_entity Entity, strategy: :path` in the portal engine; then every resource in that portal is scoped automatically. For mechanics, see `plutonium-entity-scoping`.
12
- - **Pass `--auth=<account>` / `--public` / `--byo`** to `pu:pkg:portal` for unattended runs.
13
- - **Related skills:** `plutonium-entity-scoping` (tenancy mechanics), `plutonium-auth` (Rodauth integration), `plutonium-package` (portal vs feature packages), `plutonium-policy` (portal-specific policies).
14
-
15
- Portals are Rails engines that provide web interfaces for specific user types.
16
-
17
- ## Quick checklist
18
-
19
- Creating a portal and connecting resources:
20
-
21
- 1. Run `rails g pu:pkg:portal <name> --auth=<account>` (or `--public` / `--byo`). Add `--scope=Entity` for multi-tenancy.
22
- 2. Mount the engine in `config/routes.rb`: `mount <Name>Portal::Engine, at: "/<name>"`.
23
- 3. For each resource, run `rails g pu:res:conn ResourceName --dest=<name>_portal`.
24
- 4. For singular resources (profile, settings), pass `--singular`.
25
- 5. Customize the portal's `Concerns::Controller` for auth / before_action hooks.
26
- 6. Override portal-specific policies/definitions as needed.
27
- 7. Verify: `bin/rails routes | grep <name>_portal`.
28
- 8. For multi-tenancy specifics, load `plutonium-entity-scoping`.
29
-
30
- ## Creating a Portal
31
-
32
- ```bash
33
- rails g pu:pkg:portal dashboard
34
- ```
35
-
36
- ### Generator Options
37
-
38
- | Option | Description |
39
- |--------|-------------|
40
- | `--auth=NAME` | Rodauth account to authenticate with (e.g., `--auth=user`) |
41
- | `--public` | Grant public access (no authentication) |
42
- | `--byo` | Bring your own authentication |
43
- | `--scope=CLASS` | Entity class to scope to for multi-tenancy (e.g., `--scope=Organization`) |
44
-
45
- ```bash
46
- # Non-interactive examples
47
- rails g pu:pkg:portal admin --auth=admin
48
- rails g pu:pkg:portal api --public
49
- rails g pu:pkg:portal custom --byo
50
-
51
- # With entity scoping (multi-tenancy)
52
- rails g pu:pkg:portal admin --auth=admin --scope=Organization
53
- ```
54
-
55
- Without flags, the generator prompts interactively.
56
-
57
- ## Portal Engine
58
-
59
- ```ruby
60
- # packages/dashboard_portal/lib/engine.rb
61
- module DashboardPortal
62
- class Engine < Rails::Engine
63
- include Plutonium::Portal::Engine
64
-
65
- config.after_initialize do
66
- # Optional: multi-tenancy
67
- scope_to_entity Organization, strategy: :path
68
- end
69
- end
70
- end
71
- ```
72
-
73
- ## Connecting Resources to Portals
74
-
75
- Resources must be connected to a portal to be accessible via its web interface.
76
-
77
- ### Command Syntax
78
-
79
- ```bash
80
- rails g pu:res:conn RESOURCE [RESOURCE...] --dest=PORTAL_NAME [--singular]
81
- ```
82
-
83
- **Always specify resources directly** - this avoids interactive prompts.
84
-
85
- ### Usage Patterns
86
-
87
- ```bash
88
- # Main app resources
89
- rails g pu:res:conn Post Comment Tag --dest=dashboard_portal
90
-
91
- # Namespaced resources (from a feature package)
92
- rails g pu:res:conn Blogging::Post Blogging::Comment --dest=admin_portal
93
-
94
- # Singular resources (profile, dashboard, settings)
95
- rails g pu:res:conn Profile --dest=customer_portal --singular
96
- ```
97
-
98
- ### What Gets Generated
99
-
100
- For a resource `Post` connected to `admin_portal`:
101
-
102
- ```
103
- packages/admin_portal/app/
104
- ├── controllers/admin_portal/posts_controller.rb # Portal controller
105
- ├── policies/admin_portal/post_policy.rb # Portal policy
106
- └── definitions/admin_portal/post_definition.rb # Portal definition
107
- ```
108
-
109
- Plus route registration in `packages/admin_portal/config/routes.rb`.
110
-
111
- ### Generated Controller
112
-
113
- ```ruby
114
- class AdminPortal::PostsController < ::PostsController
115
- include AdminPortal::Concerns::Controller
116
- end
117
- ```
118
-
119
- ### Generated Policy
120
-
121
- ```ruby
122
- class AdminPortal::PostPolicy < ::PostPolicy
123
- include AdminPortal::ResourcePolicy
124
-
125
- def permitted_attributes_for_create
126
- [:title, :content, :user_id]
127
- end
128
-
129
- def permitted_attributes_for_read
130
- [:title, :content, :user_id, :created_at, :updated_at]
131
- end
132
-
133
- def permitted_associations
134
- %i[]
135
- end
136
- end
137
- ```
138
-
139
- ### Route Registration
140
-
141
- ```ruby
142
- # In packages/admin_portal/config/routes.rb
143
- register_resource ::Post
144
- register_resource ::Profile, singular: true # With --singular
145
- ```
146
-
147
- ### Important Notes
148
-
149
- 1. **Always specify resources directly** - avoids prompts, no `--src` needed
150
- 2. **Always use the generator** - never manually connect resources
151
- 3. **Run after migrations** - the generator reads model columns for policy attributes
152
-
153
- ## Authentication
154
-
155
- ### Rodauth Integration
156
-
157
- ```ruby
158
- # packages/dashboard_portal/app/controllers/dashboard_portal/concerns/controller.rb
159
- module DashboardPortal
160
- module Concerns
161
- module Controller
162
- extend ActiveSupport::Concern
163
- include Plutonium::Portal::Controller
164
- include Plutonium::Auth::Rodauth(:user) # Use :user account
165
- end
166
- end
167
- end
168
- ```
169
-
170
- ### Public Access
171
-
172
- ```ruby
173
- module DashboardPortal
174
- module Concerns
175
- module Controller
176
- extend ActiveSupport::Concern
177
- include Plutonium::Portal::Controller
178
- include Plutonium::Auth::Public
179
- end
180
- end
181
- end
182
- ```
183
-
184
- ### Custom Authentication
185
-
186
- ```ruby
187
- module DashboardPortal
188
- module Concerns
189
- module Controller
190
- extend ActiveSupport::Concern
191
- include Plutonium::Portal::Controller
192
- include Plutonium::Auth::Public
193
-
194
- def current_user
195
- @current_user ||= User.find_by(api_key: request.headers["X-API-Key"])
196
- end
197
- end
198
- end
199
- end
200
- ```
201
-
202
- ## Entity Scoping (Multi-tenancy)
203
-
204
- Portals can scope all data to a parent entity via `scope_to_entity`:
205
-
206
- ```ruby
207
- module AdminPortal
208
- class Engine < Rails::Engine
209
- include Plutonium::Portal::Engine
210
-
211
- config.after_initialize do
212
- scope_to_entity Organization, strategy: :path
213
- end
214
- end
215
- end
216
- ```
217
-
218
- Strategies: `:path` (entity id in URL) or a custom method name on the portal controller concern.
219
-
220
- Access in controllers/views: `current_scoped_entity`, `scoped_to_entity?`. In policies: `entity_scope`.
221
-
222
- > **For the full entity scoping picture — the three model shapes, `associated_with` resolution, `default_relation_scope` rules, safe `relation_scope` overrides, and how parent scoping takes precedence — see the [plutonium-entity-scoping](../plutonium-entity-scoping/SKILL.md) skill. It is the single source of truth.**
223
-
224
- ## Routes
225
-
226
- ### Portal Routes
227
-
228
- ```ruby
229
- # packages/dashboard_portal/config/routes.rb
230
- DashboardPortal::Engine.routes.draw do
231
- root to: "dashboard#index"
232
-
233
- # Register resources
234
- register_resource ::Post
235
- register_resource Blogging::Comment
236
-
237
- # Custom routes
238
- get "settings", to: "settings#index"
239
- end
240
- ```
241
-
242
- ### Custom Routes on Resources
243
-
244
- Add member or collection routes with a block:
245
-
246
- ```ruby
247
- register_resource ::Post do
248
- member do
249
- get :preview
250
- get :analytics
251
- post :publish
252
- end
253
- collection do
254
- get :archived
255
- post :bulk_publish
256
- end
257
- end
258
- ```
259
-
260
- ### Mounting in Main App
261
-
262
- ```ruby
263
- # config/routes.rb
264
- Rails.application.routes.draw do
265
- # With authentication constraint
266
- constraints Rodauth::Rails.authenticate(:user) do
267
- mount DashboardPortal::Engine, at: "/dashboard"
268
- end
269
-
270
- # Or without
271
- mount PublicPortal::Engine, at: "/public"
272
- end
273
- ```
274
-
275
- ## Controller Hierarchy
276
-
277
- Portal controllers inherit from the feature package's controller if one exists (and include the portal's `Concerns::Controller`). If no feature package controller exists, they inherit from the portal's `ResourceController`.
278
-
279
- ```ruby
280
- # With feature package controller:
281
- class DashboardPortal::PostsController < ::PostsController
282
- include DashboardPortal::Concerns::Controller
283
- end
284
-
285
- # Without feature package controller:
286
- class DashboardPortal::PostsController < DashboardPortal::ResourceController
287
- end
288
- ```
289
-
290
- ### Portal ResourceController
291
-
292
- ```ruby
293
- # packages/dashboard_portal/app/controllers/dashboard_portal/resource_controller.rb
294
- module DashboardPortal
295
- class ResourceController < ::ResourceController
296
- include DashboardPortal::Concerns::Controller
297
- end
298
- end
299
- ```
300
-
301
- ### Non-Resource Controllers
302
-
303
- For portal pages not tied to a resource (dashboard, settings, etc.), inherit from `PlutoniumController`:
304
-
305
- ```ruby
306
- module DashboardPortal
307
- class DashboardController < PlutoniumController
308
- def index
309
- # Dashboard home page
310
- end
311
- end
312
- end
313
- ```
314
-
315
- ## Portal-Specific Overrides
316
-
317
- ### Override Definition
318
-
319
- ```ruby
320
- class DashboardPortal::PostDefinition < ::PostDefinition
321
- scope :my_posts, -> { where(user: current_user) }
322
- end
323
- ```
324
-
325
- ### Override Policy
326
-
327
- ```ruby
328
- class DashboardPortal::PostPolicy < ::PostPolicy
329
- include DashboardPortal::ResourcePolicy
330
-
331
- def destroy?
332
- false # No deletion in user portal
333
- end
334
-
335
- def permitted_attributes_for_create
336
- %i[title content] # Fewer fields than admin
337
- end
338
- end
339
- ```
340
-
341
- ### Override Controller
342
-
343
- ```ruby
344
- module DashboardPortal
345
- class PostsController < ResourceController
346
- private
347
-
348
- def preferred_action_after_submit
349
- "index"
350
- end
351
- end
352
- end
353
- ```
354
-
355
- ## Multiple Portals Example
356
-
357
- ```ruby
358
- # Admin portal - full access
359
- module AdminPortal
360
- class Engine < Rails::Engine
361
- include Plutonium::Portal::Engine
362
-
363
- config.after_initialize do
364
- scope_to_entity Organization, strategy: :path
365
- end
366
- end
367
- end
368
-
369
- # User dashboard - limited access
370
- module DashboardPortal
371
- class Engine < Rails::Engine
372
- include Plutonium::Portal::Engine
373
-
374
- config.after_initialize do
375
- scope_to_entity Organization, strategy: :path
376
- end
377
- end
378
- end
379
-
380
- # Public portal - read-only, no auth
381
- module PublicPortal
382
- class Engine < Rails::Engine
383
- include Plutonium::Portal::Engine
384
- end
385
- end
386
- ```
387
-
388
- ## Typical Workflow
389
-
390
- ```bash
391
- # 1. Create portal
392
- rails g pu:pkg:portal admin --auth=admin --scope=Organization
393
-
394
- # 2. Create resources
395
- rails g pu:res:scaffold Post user:belongs_to title:string 'content:text?' --dest=main_app
396
- rails db:migrate
397
-
398
- # 3. Connect resources to portal
399
- rails g pu:res:conn Post --dest=admin_portal
400
-
401
- # 4. Customize portal-specific definitions/policies as needed
402
- ```
403
-
404
- ## Related Skills
405
-
406
- - `plutonium-package` - Package overview (features vs portals)
407
- - `plutonium-auth` - Authentication setup and configuration
408
- - `plutonium-policy` - Portal-specific policies
409
- - `plutonium-definition` - Portal-specific definitions
410
- - `plutonium-controller` - Portal-specific controllers