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,420 @@
1
+ # Adding Resources
2
+
3
+ This guide covers creating new resources and connecting them to portals.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ # Generate a resource in the main app
9
+ rails g pu:res:scaffold Product name:string 'price:decimal{10,2}' --dest=main_app
10
+
11
+ # Generate a resource in a feature package
12
+ rails g pu:res:scaffold Product name:string 'price:decimal{10,2}' --dest=inventory
13
+
14
+ # Connect to a portal
15
+ rails g pu:res:conn Product --dest=admin_portal
16
+ ```
17
+
18
+ ## The Resource Generator
19
+
20
+ ### Basic Usage
21
+
22
+ ```bash
23
+ rails g pu:res:scaffold ModelName field:type field:type --dest=DESTINATION
24
+ ```
25
+
26
+ **Always specify `--dest`** to avoid interactive prompts:
27
+ - `--dest=main_app` for resources in the main application
28
+ - `--dest=package_name` for resources in a feature package
29
+
30
+ ### Field Types
31
+
32
+ Format: `name:type:index_type`
33
+
34
+ | Type | Example | Description |
35
+ |------|---------|-------------|
36
+ | `string` | `title:string` | Short text (required) |
37
+ | `'title:string?'` | `'title:string?'` | Short text (nullable) |
38
+ | `text` | `body:text` | Long text |
39
+ | `integer` | `quantity:integer` | Whole numbers |
40
+ | `decimal` | `'price:decimal{10,2}'` | Decimal with precision |
41
+ | `float` | `rating:float` | Floating point |
42
+ | `boolean` | `active:boolean` | True/false |
43
+ | `date` | `published_on:date` | Date only |
44
+ | `datetime` | `published_at:datetime` | Date and time |
45
+ | `time` | `starts_at:time` | Time only |
46
+ | `json` | `metadata:json` | JSON data |
47
+
48
+ ### Nullable Fields
49
+
50
+ Append `?` to make a field nullable. **Quote fields with special characters**:
51
+
52
+ ```bash
53
+ 'name:string?' # Nullable string
54
+ 'description:text?' # Nullable text
55
+ 'published_at:datetime?' # Nullable datetime
56
+ ```
57
+
58
+ ### Decimal Precision
59
+
60
+ Use `{precision,scale}` syntax for decimal fields:
61
+
62
+ ```bash
63
+ 'price:decimal{10,2}' # precision: 10, scale: 2
64
+ 'latitude:decimal{11,8}' # precision: 11, scale: 8
65
+ 'amount:decimal?{15,2}' # nullable with precision
66
+ ```
67
+
68
+ ### Associations
69
+
70
+ ```bash
71
+ # Required belongs_to
72
+ user:belongs_to
73
+ company:references # Same as belongs_to
74
+
75
+ # Nullable belongs_to
76
+ 'parent:belongs_to?' # Creates: null: true, optional: true
77
+
78
+ # Cross-package reference
79
+ blogging/post:belongs_to
80
+ ```
81
+
82
+ ### Indexes
83
+
84
+ Add index type as the third segment:
85
+
86
+ ```bash
87
+ email:string:index # Regular index
88
+ email:string:uniq # Unique index
89
+ ```
90
+
91
+ ### Special Types
92
+
93
+ ```bash
94
+ password_digest # has_secure_password
95
+ auth_token:token # has_secure_token (auto unique index)
96
+ content:rich_text # has_rich_text (Action Text)
97
+ avatar:attachment # has_one_attached (Active Storage)
98
+ photos:attachments # has_many_attached
99
+ price_cents:integer # has_cents (money field)
100
+ ```
101
+
102
+ ### Generator Options
103
+
104
+ ```bash
105
+ # Skip model generation (use existing model)
106
+ rails g pu:res:scaffold Post --no-model --dest=main_app
107
+
108
+ # Skip migration generation
109
+ rails g pu:res:scaffold Post --no-migration --dest=main_app
110
+
111
+ # Both (for existing models with Plutonium::Resource::Record)
112
+ rails g pu:res:scaffold Post --no-model --no-migration --dest=main_app
113
+ ```
114
+
115
+ ## Generated Files
116
+
117
+ ### For Main App Resources
118
+
119
+ ```
120
+ app/
121
+ ├── models/post.rb
122
+ ├── controllers/posts_controller.rb
123
+ ├── definitions/post_definition.rb
124
+ └── policies/post_policy.rb
125
+ db/migrate/xxx_create_posts.rb
126
+ ```
127
+
128
+ ### For Packaged Resources
129
+
130
+ ```
131
+ packages/blogging/
132
+ ├── app/
133
+ │ ├── models/blogging/post.rb
134
+ │ ├── controllers/blogging/posts_controller.rb
135
+ │ ├── definitions/blogging/post_definition.rb
136
+ │ └── policies/blogging/post_policy.rb
137
+ db/migrate/xxx_create_blogging_posts.rb
138
+ ```
139
+
140
+ ### Model
141
+
142
+ ```ruby
143
+ class Post < ResourceRecord
144
+ include Plutonium::Resource::Record
145
+ end
146
+ ```
147
+
148
+ ### Definition
149
+
150
+ ```ruby
151
+ class PostDefinition < ResourceDefinition
152
+ # Fields auto-detected from model
153
+ end
154
+ ```
155
+
156
+ ### Policy
157
+
158
+ ```ruby
159
+ class PostPolicy < ResourcePolicy
160
+ def permitted_attributes_for_create
161
+ %i[title content user_id]
162
+ end
163
+
164
+ def permitted_attributes_for_read
165
+ %i[title content user_id created_at updated_at]
166
+ end
167
+ end
168
+ ```
169
+
170
+ ## Connecting to Portals
171
+
172
+ Resources must be connected to a portal to be accessible via the web.
173
+
174
+ ### Using the Generator
175
+
176
+ ```bash
177
+ rails g pu:res:conn Post --dest=admin_portal
178
+ ```
179
+
180
+ This:
181
+ 1. Registers the resource in portal routes
182
+ 2. Creates a portal-specific controller
183
+ 3. Creates portal-specific policy and definition (if base versions don't exist)
184
+
185
+ ### Connecting Multiple Resources
186
+
187
+ ```bash
188
+ rails g pu:res:conn Post Comment Tag --dest=admin_portal
189
+ ```
190
+
191
+ ### Connecting Namespaced Resources
192
+
193
+ Use the full class name for packaged resources:
194
+
195
+ ```bash
196
+ rails g pu:res:conn Blogging::Post Blogging::Comment --dest=admin_portal
197
+ ```
198
+
199
+ ### What Gets Generated
200
+
201
+ ```
202
+ packages/admin_portal/
203
+ ├── app/
204
+ │ ├── controllers/admin_portal/posts_controller.rb
205
+ │ ├── policies/admin_portal/post_policy.rb
206
+ │ └── definitions/admin_portal/post_definition.rb
207
+ └── config/routes.rb # Updated with register_resource
208
+ ```
209
+
210
+ ## Portal-Specific Customization
211
+
212
+ ### Portal Controller
213
+
214
+ ```ruby
215
+ # packages/admin_portal/app/controllers/admin_portal/posts_controller.rb
216
+ class AdminPortal::PostsController < ::PostsController
217
+ include AdminPortal::Concerns::Controller
218
+
219
+ private
220
+
221
+ def build_resource
222
+ super.tap do |post|
223
+ post.user = current_user
224
+ end
225
+ end
226
+ end
227
+ ```
228
+
229
+ ### Portal Definition
230
+
231
+ ```ruby
232
+ # packages/admin_portal/app/definitions/admin_portal/post_definition.rb
233
+ class AdminPortal::PostDefinition < ::PostDefinition
234
+ # Add admin-only fields
235
+ field :internal_notes
236
+
237
+ # Customize existing fields
238
+ field :status, as: :select, collection: %w[draft published archived]
239
+ end
240
+ ```
241
+
242
+ ### Portal Policy
243
+
244
+ ```ruby
245
+ # packages/admin_portal/app/policies/admin_portal/post_policy.rb
246
+ class AdminPortal::PostPolicy < ::PostPolicy
247
+ include AdminPortal::ResourcePolicy
248
+
249
+ # Admins can do everything
250
+ def destroy?
251
+ true
252
+ end
253
+
254
+ def permitted_attributes_for_create
255
+ super + [:internal_notes]
256
+ end
257
+ end
258
+ ```
259
+
260
+ ## Multiple Portals
261
+
262
+ Connect the same resource to multiple portals:
263
+
264
+ ```bash
265
+ rails g pu:res:conn Post --dest=admin_portal
266
+ rails g pu:res:conn Post --dest=author_portal
267
+ ```
268
+
269
+ Each portal can have different customizations.
270
+
271
+ ## From Existing Models
272
+
273
+ If you have existing Rails models you want to convert to Plutonium resources:
274
+
275
+ ### Option 1: Model already includes Plutonium::Resource::Record
276
+
277
+ ```bash
278
+ rails g pu:res:scaffold Post --no-model --no-migration --dest=main_app
279
+ ```
280
+
281
+ This generates only the definition, policy, and controller.
282
+
283
+ ### Option 2: Let the generator update the model
284
+
285
+ ```bash
286
+ rails g pu:res:scaffold Post --dest=main_app
287
+ ```
288
+
289
+ Run without attributes to auto-import fields from the model's content columns.
290
+
291
+ ### Required Model Setup
292
+
293
+ Your model must include `Plutonium::Resource::Record`:
294
+
295
+ ```ruby
296
+ class Post < ApplicationRecord
297
+ include Plutonium::Resource::Record
298
+ end
299
+ ```
300
+
301
+ ## Adding Fields After Creation
302
+
303
+ ### 1. Create Migration
304
+
305
+ ```bash
306
+ rails g migration AddStatusToPosts status:string
307
+ ```
308
+
309
+ ### 2. Update Model (if needed)
310
+
311
+ ```ruby
312
+ class Post < ResourceRecord
313
+ validates :status, inclusion: { in: %w[draft published] }
314
+ end
315
+ ```
316
+
317
+ ### 3. Fields Auto-Detected
318
+
319
+ New columns automatically appear in forms. To customize:
320
+
321
+ ```ruby
322
+ # In definition
323
+ field :status, as: :select, collection: %w[draft published]
324
+ ```
325
+
326
+ ## Migration Customizations
327
+
328
+ Always review and customize generated migrations:
329
+
330
+ ### Inline Indexes (preferred)
331
+
332
+ ```ruby
333
+ create_table :posts do |t|
334
+ t.belongs_to :user, null: false, foreign_key: true
335
+ t.string :title, null: false
336
+
337
+ t.timestamps
338
+
339
+ t.index :title
340
+ t.index [:user_id, :title], unique: true
341
+ end
342
+ ```
343
+
344
+ ### Cascade Delete
345
+
346
+ ```ruby
347
+ t.belongs_to :user, null: false, foreign_key: {on_delete: :cascade}
348
+ ```
349
+
350
+ ### Default Values
351
+
352
+ ```ruby
353
+ t.boolean :is_active, default: true
354
+ t.integer :status, default: 0
355
+ ```
356
+
357
+ ## Removing Resources
358
+
359
+ ### Remove from Portal
360
+
361
+ 1. Remove `register_resource` from portal routes
362
+ 2. Delete portal-specific files in `packages/portal_name/app/`
363
+
364
+ ### Remove Entirely
365
+
366
+ ```bash
367
+ # Remove files (main app example)
368
+ rm app/models/post.rb
369
+ rm app/controllers/posts_controller.rb
370
+ rm app/definitions/post_definition.rb
371
+ rm app/policies/post_policy.rb
372
+
373
+ # Create migration to drop table
374
+ rails g migration DropPosts
375
+ ```
376
+
377
+ ## Best Practices
378
+
379
+ ### 1. Always Specify `--dest`
380
+ Avoids interactive prompts and makes commands reproducible.
381
+
382
+ ### 2. Quote Special Characters
383
+ Fields with `?` or `{}` must be quoted to prevent shell expansion:
384
+ ```bash
385
+ rails g pu:res:scaffold Post 'content:text?' 'price:decimal{10,2}' --dest=main_app
386
+ ```
387
+
388
+ ### 3. Run Migrations Before Connecting
389
+ ```bash
390
+ rails g pu:res:scaffold Post title:string --dest=main_app
391
+ rails db:migrate
392
+ rails g pu:res:conn Post --dest=admin_portal
393
+ ```
394
+
395
+ ### 4. Review Generated Migrations
396
+ Add cascade deletes, composite indexes, and default values as needed.
397
+
398
+ ## Troubleshooting
399
+
400
+ ### Resource Not Found
401
+
402
+ Ensure the resource is connected to the portal with `register_resource`.
403
+
404
+ ### Fields Not Showing
405
+
406
+ Check that the migration has run and the policy includes the field in `permitted_attributes_for_read`.
407
+
408
+ ### Policy Denying Access
409
+
410
+ Check the policy's permission methods (`index?`, `show?`, `create?`, etc.) return `true`.
411
+
412
+ ### Connection Generator Fails
413
+
414
+ Ensure migrations have run - the generator reads model columns to build policy attributes.
415
+
416
+ ## Related
417
+
418
+ - [Creating Packages](./creating-packages)
419
+ - [Nested Resources](./nested-resources)
420
+ - [Custom Actions](./custom-actions)