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,380 @@
1
+ # Creating Packages
2
+
3
+ This guide covers creating and organizing Feature Packages and Portal Packages.
4
+
5
+ ## Package Types
6
+
7
+ | Type | Purpose | Generator |
8
+ |------|---------|-----------|
9
+ | **Feature Package** | Business logic (models, definitions, policies) | `rails g pu:pkg:package NAME` |
10
+ | **Portal Package** | Web interface (routes, auth, UI) | `rails g pu:pkg:portal NAME` |
11
+
12
+ ## Creating a Feature Package
13
+
14
+ ### Using the Generator
15
+
16
+ ```bash
17
+ rails g pu:pkg:package blogging
18
+ ```
19
+
20
+ ### Generated Structure
21
+
22
+ ```
23
+ packages/blogging/
24
+ ├── app/
25
+ │ ├── controllers/blogging/
26
+ │ │ └── resource_controller.rb
27
+ │ ├── definitions/blogging/
28
+ │ │ └── resource_definition.rb
29
+ │ ├── interactions/blogging/
30
+ │ │ └── resource_interaction.rb
31
+ │ ├── models/blogging/
32
+ │ │ └── resource_record.rb
33
+ │ ├── policies/blogging/
34
+ │ │ └── resource_policy.rb
35
+ │ └── views/blogging/
36
+ └── lib/
37
+ └── engine.rb
38
+ ```
39
+
40
+ ### Engine Configuration
41
+
42
+ ```ruby
43
+ # packages/blogging/lib/engine.rb
44
+ module Blogging
45
+ class Engine < Rails::Engine
46
+ include Plutonium::Package::Engine
47
+ end
48
+ end
49
+ ```
50
+
51
+ ### Namespacing
52
+
53
+ All classes are auto-namespaced:
54
+ - `app/models/blogging/post.rb` → `Blogging::Post`
55
+ - `app/policies/blogging/post_policy.rb` → `Blogging::PostPolicy`
56
+
57
+ ## Creating a Portal Package
58
+
59
+ ### Using the Generator
60
+
61
+ ```bash
62
+ rails g pu:pkg:portal admin
63
+ ```
64
+
65
+ ### Generator Options
66
+
67
+ | Option | Description |
68
+ |--------|-------------|
69
+ | `--auth=NAME` | Rodauth account to authenticate with |
70
+ | `--public` | Grant public access (no authentication) |
71
+ | `--byo` | Bring your own authentication |
72
+
73
+ ```bash
74
+ # Non-interactive examples
75
+ rails g pu:pkg:portal admin --auth=admin
76
+ rails g pu:pkg:portal api --public
77
+ rails g pu:pkg:portal custom --byo
78
+ ```
79
+
80
+ Without flags, the generator prompts interactively.
81
+
82
+ ### Generated Structure
83
+
84
+ ```
85
+ packages/admin_portal/
86
+ ├── app/
87
+ │ ├── controllers/admin_portal/
88
+ │ │ ├── concerns/controller.rb
89
+ │ │ ├── dashboard_controller.rb
90
+ │ │ ├── plutonium_controller.rb
91
+ │ │ └── resource_controller.rb
92
+ │ ├── definitions/admin_portal/
93
+ │ │ └── resource_definition.rb
94
+ │ ├── policies/admin_portal/
95
+ │ │ └── resource_policy.rb
96
+ │ └── views/admin_portal/
97
+ │ └── dashboard/index.html.erb
98
+ ├── config/
99
+ │ └── routes.rb
100
+ └── lib/
101
+ └── engine.rb
102
+ ```
103
+
104
+ ### Portal Engine
105
+
106
+ ```ruby
107
+ # packages/admin_portal/lib/engine.rb
108
+ module AdminPortal
109
+ class Engine < Rails::Engine
110
+ include Plutonium::Portal::Engine
111
+
112
+ config.after_initialize do
113
+ # Multi-tenancy (optional)
114
+ scope_to_entity Organization, strategy: :path
115
+ end
116
+ end
117
+ end
118
+ ```
119
+
120
+ ### Portal Authentication
121
+
122
+ Authentication is configured in the controller concern based on generator options:
123
+
124
+ ```ruby
125
+ # packages/admin_portal/app/controllers/admin_portal/concerns/controller.rb
126
+ module AdminPortal
127
+ module Concerns
128
+ module Controller
129
+ extend ActiveSupport::Concern
130
+ include Plutonium::Portal::Controller
131
+ include Plutonium::Auth::Rodauth(:admin)
132
+ end
133
+ end
134
+ end
135
+ ```
136
+
137
+ For public access:
138
+
139
+ ```ruby
140
+ include Plutonium::Auth::Public
141
+ ```
142
+
143
+ For custom authentication:
144
+
145
+ ```ruby
146
+ included do
147
+ helper_method :current_user
148
+ end
149
+
150
+ def current_user
151
+ # Your authentication logic
152
+ @current_user ||= User.find_by(api_key: request.headers["X-API-Key"])
153
+ end
154
+ ```
155
+
156
+ ## Portal Routes
157
+
158
+ The portal generator creates routes and auto-mounts to the main app:
159
+
160
+ ```ruby
161
+ # packages/admin_portal/config/routes.rb
162
+ AdminPortal::Engine.routes.draw do
163
+ root to: "dashboard#index"
164
+
165
+ # Register resources here
166
+ register_resource ::Post
167
+ register_resource Blogging::Comment
168
+ end
169
+
170
+ # Also adds to main app routes:
171
+ # config/routes.rb (auto-generated)
172
+ Rails.application.routes.draw do
173
+ constraints Rodauth::Rails.authenticate(:admin) do
174
+ mount AdminPortal::Engine, at: "/admin"
175
+ end
176
+ end
177
+ ```
178
+
179
+ ### Custom Routes on Resources
180
+
181
+ Add member or collection routes with a block:
182
+
183
+ ```ruby
184
+ register_resource ::Post do
185
+ member do
186
+ get :preview
187
+ post :publish
188
+ end
189
+ collection do
190
+ get :archived
191
+ end
192
+ end
193
+ ```
194
+
195
+ ## Package Loading
196
+
197
+ Packages are loaded via `config/packages.rb` (generated during install):
198
+
199
+ ```ruby
200
+ # config/packages.rb
201
+ Dir.glob(File.expand_path("../packages/**/lib/engine.rb", __dir__)) do |package|
202
+ load package
203
+ end
204
+ ```
205
+
206
+ This is automatically required in `config/application.rb`.
207
+
208
+ ## Adding Resources to Packages
209
+
210
+ ```bash
211
+ # Add to main app
212
+ rails g pu:res:scaffold Post title:string --dest=main_app
213
+
214
+ # Add to a feature package
215
+ rails g pu:res:scaffold Post title:string --dest=blogging
216
+ ```
217
+
218
+ Resources are namespaced:
219
+
220
+ ```ruby
221
+ # packages/blogging/app/models/blogging/post.rb
222
+ module Blogging
223
+ class Post < Blogging::ResourceRecord
224
+ # Model code
225
+ end
226
+ end
227
+ ```
228
+
229
+ ## Connecting Resources to Portals
230
+
231
+ Resources must be connected to portals to be accessible:
232
+
233
+ ```bash
234
+ # Connect main app resource
235
+ rails g pu:res:conn Post --dest=admin_portal
236
+
237
+ # Connect namespaced resource
238
+ rails g pu:res:conn Blogging::Post --dest=admin_portal
239
+ ```
240
+
241
+ ## Entity Scoping (Multi-tenancy)
242
+
243
+ Automatically scope all data to a parent entity:
244
+
245
+ ### Path Strategy
246
+
247
+ Entity ID in URL path:
248
+
249
+ ```ruby
250
+ # packages/admin_portal/lib/engine.rb
251
+ config.after_initialize do
252
+ scope_to_entity Organization, strategy: :path
253
+ end
254
+ ```
255
+
256
+ Routes become: `/organizations/:organization_id/posts`
257
+
258
+ ### Custom Strategy
259
+
260
+ Implement your own lookup method:
261
+
262
+ ```ruby
263
+ config.after_initialize do
264
+ scope_to_entity Organization, strategy: :current_organization
265
+ end
266
+
267
+ # In controller concern
268
+ def current_organization
269
+ @current_organization ||= Organization.find_by!(subdomain: request.subdomain)
270
+ end
271
+ ```
272
+
273
+ ## Package Best Practices
274
+
275
+ ### 1. Single Responsibility
276
+ Each feature package should handle one domain:
277
+ - `blogging` - Posts, comments, categories
278
+ - `inventory` - Products, stock, warehouses
279
+ - `billing` - Invoices, payments, subscriptions
280
+
281
+ ### 2. Clear Naming
282
+ - Feature packages: domain nouns (`blogging`, `billing`)
283
+ - Portal packages: role + portal (`admin_portal`, `api_portal`)
284
+
285
+ ### 3. Minimal Cross-Dependencies
286
+ Limit dependencies between feature packages. If two packages are tightly coupled, consider merging them.
287
+
288
+ ### 4. Portal Customization
289
+ Put UI customizations in portal packages, not feature packages:
290
+
291
+ ```ruby
292
+ # Good: Portal-specific definition
293
+ # packages/admin_portal/app/definitions/admin_portal/post_definition.rb
294
+
295
+ # Bad: Feature package with portal-specific code
296
+ # packages/blogging/app/definitions/blogging/admin_post_definition.rb
297
+ ```
298
+
299
+ ## Multiple Portals Pattern
300
+
301
+ Common pattern for different user types:
302
+
303
+ ```
304
+ packages/
305
+ ├── blogging/ # Feature: blog functionality
306
+ ├── billing/ # Feature: payment/invoicing
307
+ ├── admin_portal/ # Portal: admin interface
308
+ ├── dashboard_portal/ # Portal: user dashboard
309
+ └── public_portal/ # Portal: public read-only
310
+ ```
311
+
312
+ Each portal can:
313
+ - Have different authentication
314
+ - Show different fields
315
+ - Allow different actions
316
+ - Use different layouts
317
+
318
+ ## Portal-Specific Overrides
319
+
320
+ ### Override Definition
321
+
322
+ ```ruby
323
+ # packages/admin_portal/app/definitions/admin_portal/post_definition.rb
324
+ class AdminPortal::PostDefinition < ::PostDefinition
325
+ # Add portal-specific scopes
326
+ scope :my_posts, -> { where(user: current_user) }
327
+ end
328
+ ```
329
+
330
+ ### Override Policy
331
+
332
+ ```ruby
333
+ # packages/admin_portal/app/policies/admin_portal/post_policy.rb
334
+ class AdminPortal::PostPolicy < ::PostPolicy
335
+ include AdminPortal::ResourcePolicy
336
+
337
+ def destroy?
338
+ true # Admins can delete
339
+ end
340
+
341
+ def permitted_attributes_for_create
342
+ %i[title content featured internal_notes] # More fields
343
+ end
344
+ end
345
+ ```
346
+
347
+ ### Override Controller
348
+
349
+ ```ruby
350
+ # packages/admin_portal/app/controllers/admin_portal/posts_controller.rb
351
+ module AdminPortal
352
+ class PostsController < ResourceController
353
+ private
354
+
355
+ def preferred_action_after_submit
356
+ "index"
357
+ end
358
+ end
359
+ end
360
+ ```
361
+
362
+ ## Controller Hierarchy
363
+
364
+ ```
365
+ ::PlutoniumController (app-wide base)
366
+
367
+ ::ResourceController (resource handling)
368
+
369
+ AdminPortal::ResourceController (portal base)
370
+
371
+ AdminPortal::PostsController (resource-specific)
372
+ ```
373
+
374
+ Controllers are auto-created if not defined.
375
+
376
+ ## Related
377
+
378
+ - [Adding Resources](./adding-resources)
379
+ - [Authentication](./authentication)
380
+ - [Multi-tenancy](./multi-tenancy)