plutonium 0.33.1 → 0.34.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 (143) hide show
  1. checksums.yaml +4 -4
  2. data/# Plutonium: The pre-alpha demo.md +4 -2
  3. data/.claude/skills/assets/SKILL.md +416 -0
  4. data/.claude/skills/connect-resource/SKILL.md +112 -0
  5. data/.claude/skills/controller/SKILL.md +302 -0
  6. data/.claude/skills/create-resource/SKILL.md +240 -0
  7. data/.claude/skills/definition/SKILL.md +218 -0
  8. data/.claude/skills/definition-actions/SKILL.md +386 -0
  9. data/.claude/skills/definition-fields/SKILL.md +474 -0
  10. data/.claude/skills/definition-query/SKILL.md +334 -0
  11. data/.claude/skills/forms/SKILL.md +439 -0
  12. data/.claude/skills/installation/SKILL.md +300 -0
  13. data/.claude/skills/interaction/SKILL.md +382 -0
  14. data/.claude/skills/model/SKILL.md +267 -0
  15. data/.claude/skills/model-features/SKILL.md +286 -0
  16. data/.claude/skills/nested-resources/SKILL.md +274 -0
  17. data/.claude/skills/package/SKILL.md +191 -0
  18. data/.claude/skills/policy/SKILL.md +352 -0
  19. data/.claude/skills/portal/SKILL.md +400 -0
  20. data/.claude/skills/resource/SKILL.md +281 -0
  21. data/.claude/skills/rodauth/SKILL.md +452 -0
  22. data/.claude/skills/views/SKILL.md +563 -0
  23. data/Appraisals +46 -4
  24. data/CHANGELOG.md +32 -1
  25. data/app/assets/plutonium.css +2 -2
  26. data/config/brakeman.ignore +239 -0
  27. data/config/initializers/action_policy.rb +1 -1
  28. data/docs/.vitepress/config.ts +132 -47
  29. data/docs/concepts/architecture.md +226 -0
  30. data/docs/concepts/auto-detection.md +254 -0
  31. data/docs/concepts/index.md +61 -0
  32. data/docs/concepts/packages-portals.md +304 -0
  33. data/docs/concepts/resources.md +224 -0
  34. data/docs/cookbook/blog.md +412 -0
  35. data/docs/cookbook/index.md +289 -0
  36. data/docs/cookbook/saas.md +481 -0
  37. data/docs/getting-started/index.md +56 -0
  38. data/docs/getting-started/installation.md +146 -0
  39. data/docs/getting-started/tutorial/01-setup.md +118 -0
  40. data/docs/getting-started/tutorial/02-first-resource.md +180 -0
  41. data/docs/getting-started/tutorial/03-authentication.md +246 -0
  42. data/docs/getting-started/tutorial/04-authorization.md +170 -0
  43. data/docs/getting-started/tutorial/05-custom-actions.md +202 -0
  44. data/docs/getting-started/tutorial/06-nested-resources.md +147 -0
  45. data/docs/getting-started/tutorial/07-customizing-ui.md +254 -0
  46. data/docs/getting-started/tutorial/index.md +64 -0
  47. data/docs/guides/adding-resources.md +420 -0
  48. data/docs/guides/authentication.md +551 -0
  49. data/docs/guides/authorization.md +468 -0
  50. data/docs/guides/creating-packages.md +380 -0
  51. data/docs/guides/custom-actions.md +523 -0
  52. data/docs/guides/index.md +45 -0
  53. data/docs/guides/multi-tenancy.md +302 -0
  54. data/docs/guides/nested-resources.md +411 -0
  55. data/docs/guides/search-filtering.md +266 -0
  56. data/docs/guides/theming.md +321 -0
  57. data/docs/index.md +67 -26
  58. data/docs/public/CLAUDE.md +64 -21
  59. data/docs/reference/assets/index.md +496 -0
  60. data/docs/reference/controller/index.md +363 -0
  61. data/docs/reference/definition/actions.md +400 -0
  62. data/docs/reference/definition/fields.md +350 -0
  63. data/docs/reference/definition/index.md +252 -0
  64. data/docs/reference/definition/query.md +342 -0
  65. data/docs/reference/generators/index.md +469 -0
  66. data/docs/reference/index.md +49 -0
  67. data/docs/reference/interaction/index.md +445 -0
  68. data/docs/reference/model/features.md +248 -0
  69. data/docs/reference/model/index.md +219 -0
  70. data/docs/reference/policy/index.md +385 -0
  71. data/docs/reference/portal/index.md +382 -0
  72. data/docs/reference/views/forms.md +396 -0
  73. data/docs/reference/views/index.md +479 -0
  74. data/gemfiles/rails_7.gemfile +9 -2
  75. data/gemfiles/rails_7.gemfile.lock +146 -111
  76. data/gemfiles/rails_8.0.gemfile +20 -0
  77. data/gemfiles/rails_8.0.gemfile.lock +417 -0
  78. data/gemfiles/rails_8.1.gemfile +20 -0
  79. data/gemfiles/rails_8.1.gemfile.lock +419 -0
  80. data/lib/generators/pu/gem/dotenv/templates/.env +2 -0
  81. data/lib/generators/pu/gem/dotenv/templates/config/initializers/001_ensure_required_env.rb +3 -1
  82. data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +13 -16
  83. data/lib/generators/pu/pkg/portal/USAGE +65 -0
  84. data/lib/generators/pu/pkg/portal/portal_generator.rb +22 -9
  85. data/lib/generators/pu/res/conn/USAGE +71 -0
  86. data/lib/generators/pu/res/model/USAGE +106 -110
  87. data/lib/generators/pu/res/model/templates/model.rb.tt +6 -2
  88. data/lib/generators/pu/res/scaffold/USAGE +85 -0
  89. data/lib/generators/pu/rodauth/install_generator.rb +2 -6
  90. data/lib/generators/pu/rodauth/templates/config/initializers/url_options.rb +17 -0
  91. data/lib/generators/pu/skills/sync/USAGE +14 -0
  92. data/lib/generators/pu/skills/sync/sync_generator.rb +66 -0
  93. data/lib/plutonium/action_policy/sti_policy_lookup.rb +1 -1
  94. data/lib/plutonium/core/controller.rb +2 -2
  95. data/lib/plutonium/interaction/base.rb +1 -0
  96. data/lib/plutonium/package/engine.rb +2 -2
  97. data/lib/plutonium/query/adhoc_block.rb +6 -2
  98. data/lib/plutonium/query/model_scope.rb +1 -1
  99. data/lib/plutonium/railtie.rb +4 -0
  100. data/lib/plutonium/resource/controllers/crud_actions/index_action.rb +1 -1
  101. data/lib/plutonium/resource/query_object.rb +38 -8
  102. data/lib/plutonium/ui/table/components/scopes_bar.rb +39 -34
  103. data/lib/plutonium/version.rb +1 -1
  104. data/lib/tasks/release.rake +19 -4
  105. data/package.json +1 -1
  106. metadata +76 -39
  107. data/brakeman.ignore +0 -28
  108. data/docs/api-examples.md +0 -49
  109. data/docs/guide/claude-code-guide.md +0 -74
  110. data/docs/guide/deep-dive/authorization.md +0 -189
  111. data/docs/guide/deep-dive/multitenancy.md +0 -256
  112. data/docs/guide/deep-dive/resources.md +0 -390
  113. data/docs/guide/getting-started/01-installation.md +0 -165
  114. data/docs/guide/index.md +0 -28
  115. data/docs/guide/introduction/01-what-is-plutonium.md +0 -211
  116. data/docs/guide/introduction/02-core-concepts.md +0 -440
  117. data/docs/guide/tutorial/01-project-setup.md +0 -75
  118. data/docs/guide/tutorial/02-creating-a-feature-package.md +0 -45
  119. data/docs/guide/tutorial/03-defining-resources.md +0 -90
  120. data/docs/guide/tutorial/04-creating-a-portal.md +0 -101
  121. data/docs/guide/tutorial/05-customizing-the-ui.md +0 -128
  122. data/docs/guide/tutorial/06-adding-custom-actions.md +0 -101
  123. data/docs/guide/tutorial/07-implementing-authorization.md +0 -90
  124. data/docs/markdown-examples.md +0 -85
  125. data/docs/modules/action.md +0 -244
  126. data/docs/modules/authentication.md +0 -236
  127. data/docs/modules/configuration.md +0 -599
  128. data/docs/modules/controller.md +0 -443
  129. data/docs/modules/core.md +0 -316
  130. data/docs/modules/definition.md +0 -1308
  131. data/docs/modules/display.md +0 -759
  132. data/docs/modules/form.md +0 -495
  133. data/docs/modules/generator.md +0 -400
  134. data/docs/modules/index.md +0 -167
  135. data/docs/modules/interaction.md +0 -642
  136. data/docs/modules/package.md +0 -151
  137. data/docs/modules/policy.md +0 -176
  138. data/docs/modules/portal.md +0 -710
  139. data/docs/modules/query.md +0 -297
  140. data/docs/modules/resource_record.md +0 -618
  141. data/docs/modules/routing.md +0 -690
  142. data/docs/modules/table.md +0 -301
  143. data/docs/modules/ui.md +0 -631
@@ -0,0 +1,363 @@
1
+ # Controller Reference
2
+
3
+ Complete reference for resource controllers.
4
+
5
+ ## Overview
6
+
7
+ Controllers handle HTTP requests and responses. Plutonium provides a controller module with CRUD actions built-in. You rarely need to customize controllers - definitions handle UI configuration and policies handle authorization.
8
+
9
+ ## Base Class
10
+
11
+ ```ruby
12
+ # app/controllers/resource_controller.rb (generated during install)
13
+ class ResourceController < ApplicationController
14
+ include Plutonium::Resource::Controller
15
+ end
16
+
17
+ # app/controllers/posts_controller.rb (generated per resource)
18
+ class PostsController < ::ResourceController
19
+ # Empty - all CRUD actions inherited
20
+ end
21
+ ```
22
+
23
+ For portals:
24
+
25
+ ```ruby
26
+ # packages/admin_portal/app/controllers/admin_portal/resource_controller.rb
27
+ module AdminPortal
28
+ class ResourceController < ::ResourceController
29
+ include AdminPortal::Concerns::Controller
30
+ end
31
+ end
32
+
33
+ # packages/admin_portal/app/controllers/admin_portal/posts_controller.rb
34
+ module AdminPortal
35
+ class PostsController < ResourceController
36
+ # Portal-specific customizations
37
+ end
38
+ end
39
+ ```
40
+
41
+ ## Built-in Actions
42
+
43
+ | Action | HTTP Method | Path | Purpose |
44
+ |--------|-------------|------|---------|
45
+ | `index` | GET | `/posts` | List with pagination, search, filters, sorting |
46
+ | `show` | GET | `/posts/:id` | Show record |
47
+ | `new` | GET | `/posts/new` | New record form |
48
+ | `create` | POST | `/posts` | Create record |
49
+ | `edit` | GET | `/posts/:id/edit` | Edit record form |
50
+ | `update` | PATCH/PUT | `/posts/:id` | Update record |
51
+ | `destroy` | DELETE | `/posts/:id` | Delete record |
52
+
53
+ Plus interactive action routes for custom operations defined in definitions.
54
+
55
+ ## Key Methods
56
+
57
+ ### Resource Access
58
+
59
+ ```ruby
60
+ resource_class # The model class (e.g., Post)
61
+ resource_record! # Current record (raises RecordNotFound if not found)
62
+ resource_record? # Current record (nil if not found)
63
+ resource_params # Permitted params for create/update
64
+ current_parent # Parent record for nested routes
65
+ ```
66
+
67
+ ### Authorization
68
+
69
+ ```ruby
70
+ authorize_current!(record, to: :action?) # Check permission
71
+ current_policy # Policy for current resource
72
+ permitted_attributes # Allowed attributes for action
73
+ current_authorized_scope # Scoped records user can access
74
+ ```
75
+
76
+ ### Definition Access
77
+
78
+ ```ruby
79
+ current_definition # Definition for current resource
80
+ ```
81
+
82
+ ### UI Building
83
+
84
+ ```ruby
85
+ build_form # Build form component
86
+ build_detail # Build show/detail component
87
+ build_collection # Build table component
88
+ ```
89
+
90
+ ### URL Generation
91
+
92
+ ```ruby
93
+ resource_url_for(@post) # URL for record
94
+ resource_url_for(@post, action: :edit) # Edit URL
95
+ resource_url_for(Post) # Index URL
96
+ resource_url_for(Post, parent: @user) # Nested index URL
97
+ ```
98
+
99
+ ## Customization Hooks
100
+
101
+ All customization is done by overriding private methods.
102
+
103
+ ### Redirect Hooks
104
+
105
+ ```ruby
106
+ class PostsController < ::ResourceController
107
+ private
108
+
109
+ # Where to go after create/update: "show" (default), "edit", "new", "index"
110
+ def preferred_action_after_submit
111
+ "edit"
112
+ end
113
+
114
+ # Custom URL after create/update (overrides preferred_action_after_submit)
115
+ def redirect_url_after_submit
116
+ resource_url_for(resource_class)
117
+ end
118
+
119
+ # Custom URL after destroy
120
+ def redirect_url_after_destroy
121
+ resource_url_for(resource_class)
122
+ end
123
+ end
124
+ ```
125
+
126
+ ### Parameter Hooks
127
+
128
+ ```ruby
129
+ class PostsController < ::ResourceController
130
+ private
131
+
132
+ # Modify params before create/update
133
+ def resource_params
134
+ params = super
135
+ params[:tags] = params[:tags].split(",") if params[:tags].is_a?(String)
136
+ params
137
+ end
138
+ end
139
+ ```
140
+
141
+ ### Query Hooks
142
+
143
+ ```ruby
144
+ class PostsController < ::ResourceController
145
+ private
146
+
147
+ # Customize the index query
148
+ def filtered_resource_collection
149
+ base = current_authorized_scope
150
+ base = base.featured if params[:featured]
151
+ current_query_object.apply(base, raw_resource_query_params)
152
+ end
153
+ end
154
+ ```
155
+
156
+ ### Presentation Hooks
157
+
158
+ Control whether parent/entity fields appear in forms and displays:
159
+
160
+ ```ruby
161
+ class PostsController < ::ResourceController
162
+ private
163
+
164
+ # Show parent field in displays (default: false)
165
+ def present_parent?
166
+ true
167
+ end
168
+
169
+ # Include parent field in forms (default: same as present_parent?)
170
+ def submit_parent?
171
+ true
172
+ end
173
+
174
+ # Show scoped entity in displays (default: false)
175
+ def present_scoped_entity?
176
+ true
177
+ end
178
+
179
+ # Include scoped entity in forms (default: same as present_scoped_entity?)
180
+ def submit_scoped_entity?
181
+ true
182
+ end
183
+ end
184
+ ```
185
+
186
+ ## Lifecycle Callbacks
187
+
188
+ Use standard Rails callbacks:
189
+
190
+ ```ruby
191
+ class PostsController < ::ResourceController
192
+ before_action :check_quota, only: [:create]
193
+
194
+ private
195
+
196
+ def check_quota
197
+ if current_user.posts.count >= 100
198
+ redirect_to resource_url_for(resource_class), alert: "Post limit reached"
199
+ end
200
+ end
201
+ end
202
+ ```
203
+
204
+ ## Custom Actions
205
+
206
+ For most custom operations, use Interactive Actions in definitions. When you need a custom controller action:
207
+
208
+ ```ruby
209
+ class PostsController < ::ResourceController
210
+ def publish
211
+ authorize_current!(resource_record!, to: :publish?)
212
+ resource_record!.update!(published: true)
213
+ redirect_to resource_url_for(resource_record!), notice: "Published!"
214
+ end
215
+ end
216
+ ```
217
+
218
+ ### Routes for Custom Actions
219
+
220
+ ```ruby
221
+ # In portal routes or config/routes.rb
222
+ register_resource Post do
223
+ member do
224
+ post :publish
225
+ end
226
+ end
227
+ ```
228
+
229
+ ## Authorization
230
+
231
+ ### Automatic Authorization
232
+
233
+ Authorization is checked automatically for standard CRUD actions via `authorize_current!`.
234
+
235
+ ### Authorization Verification
236
+
237
+ Controllers verify authorization was performed after every action:
238
+
239
+ ```ruby
240
+ # These run after every action
241
+ verify_authorize_current # Ensures authorize_current! was called
242
+ verify_current_authorized_scope # Ensures scope was loaded (except new/create)
243
+ ```
244
+
245
+ ### Skip Authorization Verification
246
+
247
+ ```ruby
248
+ class PostsController < ::ResourceController
249
+ skip_verify_authorize_current only: [:preview]
250
+ skip_verify_current_authorized_scope only: [:preview]
251
+
252
+ def preview
253
+ # Handle authorization manually or skip it
254
+ end
255
+ end
256
+ ```
257
+
258
+ ## Nested Resources
259
+
260
+ Parent records are automatically resolved:
261
+
262
+ ```ruby
263
+ # Route: /users/:user_id/posts/:id
264
+ class PostsController < ::ResourceController
265
+ # current_parent returns the User
266
+ # resource_record! returns the Post scoped to that User
267
+ end
268
+ ```
269
+
270
+ Parent fields are automatically excluded from forms/displays. Override with presentation hooks.
271
+
272
+ ### Parent-Related Methods
273
+
274
+ ```ruby
275
+ current_parent # The parent record (e.g., User)
276
+ parent_route_param # The route param key (e.g., :user_id)
277
+ parent_input_param # The association name (e.g., :user)
278
+ ```
279
+
280
+ ## Entity Scoping (Multi-tenancy)
281
+
282
+ When a portal is scoped to an entity:
283
+
284
+ ```ruby
285
+ # packages/customer_portal/lib/engine.rb
286
+ module CustomerPortal
287
+ class Engine < Rails::Engine
288
+ include Plutonium::Portal::Engine
289
+
290
+ config.after_initialize do
291
+ scope_to_entity Organization
292
+ end
293
+ end
294
+ end
295
+ ```
296
+
297
+ Controllers automatically:
298
+ - Scope all queries to the entity
299
+ - Exclude entity field from forms
300
+ - Provide `current_scoped_entity` method
301
+
302
+ ## Specifying Resource Class
303
+
304
+ The resource class is inferred from the controller name. Override if needed:
305
+
306
+ ```ruby
307
+ class LegacyPostsController < ::ResourceController
308
+ controller_for Post
309
+ end
310
+ ```
311
+
312
+ ## Response Formats
313
+
314
+ Controllers respond to multiple formats:
315
+
316
+ - HTML (default)
317
+ - JSON (via RABL templates)
318
+ - Turbo Stream (for Hotwire)
319
+
320
+ ## Error Handling
321
+
322
+ ```ruby
323
+ class PostsController < ::ResourceController
324
+ rescue_from ActiveRecord::RecordNotFound do
325
+ redirect_to resource_url_for(resource_class), alert: "Post not found"
326
+ end
327
+
328
+ rescue_from ActionPolicy::Unauthorized do
329
+ redirect_to resource_url_for(resource_class), alert: "Not authorized"
330
+ end
331
+ end
332
+ ```
333
+
334
+ ## Portal-Specific Controllers
335
+
336
+ Each portal can have its own controller override:
337
+
338
+ ```ruby
339
+ # packages/admin_portal/app/controllers/admin_portal/posts_controller.rb
340
+ module AdminPortal
341
+ class PostsController < ResourceController
342
+ private
343
+
344
+ def preferred_action_after_submit
345
+ "index" # Admin prefers list view
346
+ end
347
+ end
348
+ end
349
+ ```
350
+
351
+ ## Best Practices
352
+
353
+ 1. **Keep controllers thin** - Use definitions for UI, policies for auth, interactions for logic
354
+ 2. **Don't override CRUD actions** - Customize via hooks (`resource_params`, `redirect_url_after_submit`)
355
+ 3. **Use interactive actions** - For custom operations, define in definition with interaction
356
+ 4. **Let authorization work** - Don't skip verification without good reason
357
+ 5. **Trust the framework** - Most customization belongs in definitions or policies
358
+
359
+ ## Related
360
+
361
+ - [Definition Reference](/reference/definition/) - UI configuration
362
+ - [Policy Reference](/reference/policy/) - Authorization
363
+ - [Actions Reference](/reference/definition/actions) - Interactive actions