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,396 @@
1
+ # Forms Reference
2
+
3
+ Complete reference for form customization using Phlexi::Form.
4
+
5
+ ## Overview
6
+
7
+ Plutonium forms are built on [Phlexi::Form](https://github.com/radioactive-labs/phlexi-form), providing a Ruby-first approach to form building.
8
+
9
+ ## Form Class Hierarchy
10
+
11
+ ```
12
+ Phlexi::Form::Base
13
+ └── Plutonium::UI::Form::Base # Base with Plutonium components
14
+ ├── Plutonium::UI::Form::Resource # Resource CRUD forms
15
+ │ └── Plutonium::UI::Form::Interaction # Interactive action forms
16
+ └── Plutonium::UI::Form::Query # Search/filter forms
17
+ ```
18
+
19
+ ## Custom Form Template
20
+
21
+ Override form rendering in your definition:
22
+
23
+ ```ruby
24
+ class PostDefinition < ResourceDefinition
25
+ class Form < Form
26
+ def form_template
27
+ # Your custom layout
28
+ render_fields
29
+ render_actions
30
+ end
31
+ end
32
+ end
33
+ ```
34
+
35
+ ### Form Template Methods
36
+
37
+ | Method | Description |
38
+ |--------|-------------|
39
+ | `form_template` | Main template method to override |
40
+ | `render_fields` | Render all permitted fields |
41
+ | `render_resource_field(name)` | Render a single field by name |
42
+ | `render_actions` | Render submit buttons |
43
+ | `fields_wrapper { }` | Wrapper div for field grid |
44
+ | `actions_wrapper { }` | Wrapper div for buttons |
45
+
46
+ ### Form Attributes
47
+
48
+ | Attribute | Description |
49
+ |-----------|-------------|
50
+ | `object` / `record` | The form object being edited |
51
+ | `resource_fields` | Array of permitted field names |
52
+ | `resource_definition` | The definition instance |
53
+
54
+ ## Custom Layouts
55
+
56
+ ### Sectioned Form
57
+
58
+ ```ruby
59
+ class PostDefinition < ResourceDefinition
60
+ class Form < Form
61
+ def form_template
62
+ section("Basic Information") {
63
+ render_resource_field :title
64
+ render_resource_field :slug
65
+ }
66
+
67
+ section("Content") {
68
+ render_resource_field :content
69
+ render_resource_field :excerpt
70
+ }
71
+
72
+ render_actions
73
+ end
74
+
75
+ private
76
+
77
+ def section(title, &)
78
+ div(class: "mb-8") {
79
+ h3(class: "text-lg font-semibold mb-4") { title }
80
+ fields_wrapper(&)
81
+ }
82
+ end
83
+ end
84
+ end
85
+ ```
86
+
87
+ ### Two-Column Layout
88
+
89
+ ```ruby
90
+ class Form < Form
91
+ def form_template
92
+ div(class: "grid grid-cols-1 lg:grid-cols-3 gap-6") {
93
+ # Main content - 2 columns
94
+ div(class: "lg:col-span-2") {
95
+ fields_wrapper {
96
+ render_resource_field :title
97
+ render_resource_field :content
98
+ }
99
+ }
100
+
101
+ # Sidebar - 1 column
102
+ div(class: "space-y-4") {
103
+ Panel {
104
+ h4(class: "font-medium mb-2") { "Settings" }
105
+ render_resource_field :status
106
+ render_resource_field :visibility
107
+ }
108
+ }
109
+ }
110
+
111
+ render_actions
112
+ end
113
+ end
114
+ ```
115
+
116
+ ## Field Builder
117
+
118
+ When using `render_resource_field`, Plutonium applies definition configuration. For custom rendering, use the `field` method directly.
119
+
120
+ ### Basic Field Usage
121
+
122
+ ```ruby
123
+ def form_template
124
+ # Using field builder directly
125
+ render field(:title).wrapped { |f| f.input_tag }
126
+ render field(:content).wrapped { |f| f.easymde_tag }
127
+ render field(:published).wrapped { |f| f.checkbox_tag }
128
+
129
+ render_actions
130
+ end
131
+ ```
132
+
133
+ ### Standard Tag Methods
134
+
135
+ | Method | Input Type |
136
+ |--------|------------|
137
+ | `f.input_tag` | Text input (auto-detects type) |
138
+ | `f.string_tag` | Text input |
139
+ | `f.text_tag` | Textarea |
140
+ | `f.number_tag` | Number input |
141
+ | `f.email_tag` | Email input |
142
+ | `f.password_tag` | Password input |
143
+ | `f.url_tag` | URL input |
144
+ | `f.phone_tag` | Telephone input |
145
+ | `f.hidden_tag` | Hidden input |
146
+ | `f.date_tag` | Date input |
147
+ | `f.time_tag` | Time input |
148
+ | `f.datetime_tag` | Datetime input |
149
+ | `f.checkbox_tag` | Checkbox |
150
+ | `f.boolean_tag` | Checkbox (themed as boolean) |
151
+ | `f.select_tag` | Select dropdown |
152
+ | `f.radio_button_tag` | Radio button |
153
+ | `f.collection_radio_buttons_tag` | Radio button collection |
154
+ | `f.collection_checkboxes_tag` | Checkbox collection |
155
+ | `f.range_tag` | Range slider |
156
+ | `f.file_input_tag` | File input |
157
+
158
+ ### Plutonium-Enhanced Tags
159
+
160
+ Plutonium extends the form builder with additional tags. See the [Form::Base::Builder source](https://github.com/radioactive-labs/plutonium-core/blob/master/lib/plutonium/ui/form/base.rb) for the current list.
161
+
162
+ Common ones include:
163
+ - `f.easymde_tag` / `f.markdown_tag` - Markdown editor
164
+ - `f.slim_select_tag` - Enhanced select
165
+ - `f.flatpickr_tag` - Date/time picker
166
+ - `f.uppy_tag` / `f.file_tag` - File upload
167
+ - `f.secure_association_tag` - Association with SGID
168
+
169
+ ### Field with Options
170
+
171
+ ```ruby
172
+ # Select with choices
173
+ render field(:status).wrapped { |f|
174
+ f.select_tag(choices: %w[draft published archived])
175
+ }
176
+
177
+ # Date picker with options
178
+ render field(:published_at).wrapped { |f|
179
+ f.flatpickr_tag(min_date: Date.today, enable_time: true)
180
+ }
181
+
182
+ # File upload with restrictions
183
+ render field(:avatar).wrapped { |f|
184
+ f.uppy_tag(
185
+ allowed_file_types: %w[.jpg .png .gif],
186
+ max_file_size: 5.megabytes
187
+ )
188
+ }
189
+ ```
190
+
191
+ ### Wrapped vs Unwrapped
192
+
193
+ ```ruby
194
+ # Wrapped - includes label, hint, errors
195
+ render field(:title).wrapped { |f| f.input_tag }
196
+
197
+ # Unwrapped - just the input element
198
+ render field(:title).input_tag
199
+
200
+ # Custom wrapper options
201
+ render field(:title).wrapped(class: "col-span-full") { |f|
202
+ f.input_tag
203
+ }
204
+ ```
205
+
206
+ ## Input Configuration in Definitions
207
+
208
+ Define inputs in the definition, render them in the form:
209
+
210
+ ```ruby
211
+ class PostDefinition < ResourceDefinition
212
+ # Configure inputs
213
+ input :title, hint: "Be descriptive", placeholder: "Enter title"
214
+ input :content, as: :markdown
215
+ input :status, as: :select, choices: %w[draft published]
216
+ input :published_at, as: :flatpickr
217
+
218
+ # Custom input with block
219
+ input :category do |f|
220
+ choices = Category.active.pluck(:name, :id)
221
+ f.select_tag(choices: choices)
222
+ end
223
+
224
+ class Form < Form
225
+ def form_template
226
+ # render_resource_field uses the input configuration
227
+ render_resource_field :title
228
+ render_resource_field :content
229
+ render_resource_field :status
230
+ render_resource_field :published_at
231
+ render_resource_field :category
232
+
233
+ render_actions
234
+ end
235
+ end
236
+ end
237
+ ```
238
+
239
+ ## Nested Forms
240
+
241
+ For `has_many` / `has_one` associations with `accepts_nested_attributes_for`:
242
+
243
+ ### Model Setup
244
+
245
+ ```ruby
246
+ class Post < ResourceRecord
247
+ has_many :comments
248
+ accepts_nested_attributes_for :comments, allow_destroy: true
249
+ end
250
+ ```
251
+
252
+ ### Definition Setup
253
+
254
+ ```ruby
255
+ class PostDefinition < ResourceDefinition
256
+ nested_input :comments do |n|
257
+ n.input :author_name
258
+ n.input :body, as: :text
259
+ end
260
+
261
+ # Or reference another definition
262
+ nested_input :comments, using: CommentDefinition, fields: %i[author_name body]
263
+ end
264
+ ```
265
+
266
+ ### Rendering
267
+
268
+ ```ruby
269
+ class Form < Form
270
+ def form_template
271
+ render_resource_field :title
272
+ render_resource_field :content
273
+
274
+ # Nested fields are automatically handled
275
+ render_resource_field :comments
276
+
277
+ render_actions
278
+ end
279
+ end
280
+ ```
281
+
282
+ ## Dynamic Forms (pre_submit)
283
+
284
+ Fields with `pre_submit: true` trigger form re-rendering on change:
285
+
286
+ ```ruby
287
+ class PostDefinition < ResourceDefinition
288
+ input :post_type, as: :select,
289
+ choices: %w[article video podcast],
290
+ pre_submit: true
291
+
292
+ input :video_url,
293
+ condition: -> { object.post_type == "video" }
294
+
295
+ input :podcast_url,
296
+ condition: -> { object.post_type == "podcast" }
297
+ end
298
+ ```
299
+
300
+ When `post_type` changes, the form re-renders via Turbo and shows/hides conditional fields.
301
+
302
+ ## Form Actions
303
+
304
+ ### Default Actions
305
+
306
+ ```ruby
307
+ def render_actions
308
+ actions_wrapper {
309
+ render submit_button
310
+ }
311
+ end
312
+ ```
313
+
314
+ ### Custom Actions
315
+
316
+ ```ruby
317
+ def render_actions
318
+ actions_wrapper {
319
+ # Cancel link
320
+ a(href: resource_url_for(resource_class), class: "btn btn-secondary") {
321
+ "Cancel"
322
+ }
323
+
324
+ # Save as draft
325
+ button(type: :submit, name: "draft", value: "1", class: "btn") {
326
+ "Save Draft"
327
+ }
328
+
329
+ # Primary submit
330
+ render submit_button
331
+ }
332
+ end
333
+ ```
334
+
335
+ ## Form Context
336
+
337
+ Inside form templates:
338
+
339
+ ```ruby
340
+ class Form < Form
341
+ def form_template
342
+ # Form object
343
+ object # The record
344
+ record # Alias for object
345
+ object.new_record? # Check if creating
346
+
347
+ # Request context
348
+ current_user
349
+ current_parent
350
+ request
351
+ params
352
+
353
+ # Definition
354
+ resource_definition
355
+ resource_fields # Permitted fields
356
+
357
+ # URL helpers
358
+ resource_url_for(object)
359
+ resource_url_for(Post, action: :new)
360
+
361
+ # Rails helpers
362
+ helpers.link_to(...)
363
+ end
364
+ end
365
+ ```
366
+
367
+ ## Interaction Forms
368
+
369
+ Forms for interactive actions:
370
+
371
+ ```ruby
372
+ class PublishPostInteraction < ResourceInteraction
373
+ attribute :publish_date, :date
374
+ attribute :notify_subscribers, :boolean, default: true
375
+
376
+ input :publish_date, as: :flatpickr
377
+ input :notify_subscribers
378
+
379
+ # Custom form (optional)
380
+ class Form < Plutonium::UI::Form::Interaction
381
+ def form_template
382
+ div(class: "space-y-4") {
383
+ render_resource_field :publish_date
384
+ render_resource_field :notify_subscribers
385
+ }
386
+ render_actions
387
+ end
388
+ end
389
+ end
390
+ ```
391
+
392
+ ## Related
393
+
394
+ - [Fields Reference](/reference/definition/fields) - Input configuration
395
+ - [Views Reference](./index) - Custom page classes
396
+ - [Theming Guide](/guides/theming) - TailwindCSS and styling