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,400 @@
1
+ # Definition Actions
2
+
3
+ Complete reference for custom actions in definitions.
4
+
5
+ ## Overview
6
+
7
+ Actions add buttons beyond standard CRUD operations. Two types:
8
+
9
+ 1. **Simple Actions** - Navigate to URLs
10
+ 2. **Interactive Actions** - Execute Interactions with optional user input
11
+
12
+ ## Action Types
13
+
14
+ | Type | Shows In | Use Case |
15
+ |------|----------|----------|
16
+ | `resource_action` | Index page | Import, Export, Create |
17
+ | `record_action` | Show page | Edit, Delete, Archive |
18
+ | `collection_record_action` | Table rows | Quick actions per row |
19
+ | `bulk_action` | Selected records | Bulk operations |
20
+
21
+ ## Simple Actions
22
+
23
+ Simple actions link to existing routes. The target route must already exist.
24
+
25
+ ```ruby
26
+ class PostDefinition < Plutonium::Resource::Definition
27
+ # Link to external URL
28
+ action :documentation,
29
+ label: "Documentation",
30
+ route_options: {url: "https://docs.example.com"},
31
+ icon: Phlex::TablerIcons::Book,
32
+ resource_action: true
33
+
34
+ # Link to custom controller action
35
+ action :reports,
36
+ route_options: {action: :reports},
37
+ icon: Phlex::TablerIcons::ChartBar,
38
+ resource_action: true
39
+ end
40
+ ```
41
+
42
+ **Note:** For custom operations with business logic, use **Interactive Actions** with an Interaction class.
43
+
44
+ ## Interactive Actions
45
+
46
+ ```ruby
47
+ class PostDefinition < Plutonium::Resource::Definition
48
+ action :publish,
49
+ interaction: PublishInteraction,
50
+ icon: Phlex::TablerIcons::Send
51
+
52
+ action :archive,
53
+ interaction: ArchiveInteraction,
54
+ color: :danger,
55
+ category: :danger,
56
+ position: 1000,
57
+ confirmation: "Are you sure?"
58
+ end
59
+ ```
60
+
61
+ ## Action Options
62
+
63
+ ```ruby
64
+ action :name,
65
+ # Display
66
+ label: "Custom Label", # Button text (default: name.titleize)
67
+ description: "What it does", # Tooltip/description
68
+ icon: Phlex::TablerIcons::Star, # Icon component
69
+
70
+ # Styling
71
+ color: :danger, # :primary, :secondary, :danger
72
+
73
+ # Visibility (boolean flags)
74
+ resource_action: true, # Show on index page
75
+ record_action: true, # Show on show page
76
+ collection_record_action: true, # Show in table rows
77
+ bulk_action: true, # For selected records
78
+
79
+ # Grouping
80
+ category: :primary, # :primary, :secondary, :danger
81
+ position: 50, # Order (lower = first)
82
+
83
+ # Behavior
84
+ confirmation: "Are you sure?", # Confirmation dialog
85
+ turbo_frame: "_top", # Turbo frame target
86
+ return_to: "/custom/path", # Override return URL
87
+ route_options: {action: :foo} # Route configuration
88
+ ```
89
+
90
+ ## Route Options
91
+
92
+ Configure how the action's URL is generated:
93
+
94
+ ```ruby
95
+ # Simple route to controller action
96
+ action :preview,
97
+ route_options: {action: :preview},
98
+ record_action: true
99
+
100
+ # With HTTP method
101
+ action :archive,
102
+ route_options: {method: :post, action: :archive},
103
+ record_action: true
104
+
105
+ # External URL
106
+ action :docs,
107
+ route_options: {url: "https://docs.example.com"},
108
+ resource_action: true
109
+
110
+ # Custom URL resolver
111
+ action :create_deployment,
112
+ route_options: Plutonium::Action::RouteOptions.new(
113
+ url_resolver: ->(subject) {
114
+ resource_url_for(Deployment, action: :new, parent: subject)
115
+ }
116
+ ),
117
+ record_action: true
118
+ ```
119
+
120
+ ## Built-in CRUD Actions
121
+
122
+ These are defined by default:
123
+
124
+ ```ruby
125
+ action :new,
126
+ route_options: {action: :new},
127
+ resource_action: true,
128
+ category: :primary,
129
+ icon: Phlex::TablerIcons::Plus,
130
+ position: 10
131
+
132
+ action :show,
133
+ route_options: {action: :show},
134
+ collection_record_action: true,
135
+ icon: Phlex::TablerIcons::Eye,
136
+ position: 10
137
+
138
+ action :edit,
139
+ route_options: {action: :edit},
140
+ record_action: true,
141
+ collection_record_action: true,
142
+ icon: Phlex::TablerIcons::Edit,
143
+ position: 20
144
+
145
+ action :destroy,
146
+ route_options: {method: :delete},
147
+ record_action: true,
148
+ collection_record_action: true,
149
+ category: :danger,
150
+ icon: Phlex::TablerIcons::Trash,
151
+ position: 100,
152
+ confirmation: "Are you sure?",
153
+ turbo_frame: "_top"
154
+ ```
155
+
156
+ ### Customizing Built-in Actions
157
+
158
+ Override in your definition:
159
+
160
+ ```ruby
161
+ class PostDefinition < Plutonium::Resource::Definition
162
+ # Customize delete confirmation
163
+ action :destroy,
164
+ confirmation: "This will permanently delete the post and all comments.",
165
+ route_options: {method: :delete},
166
+ record_action: true,
167
+ collection_record_action: true,
168
+ category: :danger,
169
+ icon: Phlex::TablerIcons::Trash,
170
+ position: 100,
171
+ turbo_frame: "_top"
172
+ end
173
+ ```
174
+
175
+ ## Authorization
176
+
177
+ Actions are authorized via policies:
178
+
179
+ ```ruby
180
+ # app/policies/post_policy.rb
181
+ class PostPolicy < Plutonium::Resource::Policy
182
+ def publish?
183
+ user.admin? || record.author == user
184
+ end
185
+
186
+ def archive?
187
+ user.admin?
188
+ end
189
+ end
190
+ ```
191
+
192
+ The action only appears if the policy method returns `true`.
193
+
194
+ ## Immediate vs Form Actions
195
+
196
+ **Immediate** - Executes without showing a form (when interaction has no extra inputs):
197
+
198
+ ```ruby
199
+ class ArchiveInteraction < Plutonium::Resource::Interaction
200
+ attribute :resource # Only resource, no other inputs
201
+ # No input declarations
202
+
203
+ def execute
204
+ resource.archived!
205
+ succeed(resource)
206
+ end
207
+ end
208
+ ```
209
+
210
+ **Form** - Shows a form first (when interaction has additional inputs):
211
+
212
+ ```ruby
213
+ class InviteUserInteraction < Plutonium::Resource::Interaction
214
+ attribute :resource
215
+ attribute :email
216
+ attribute :role
217
+
218
+ input :email
219
+ input :role, as: :select, choices: %w[admin member]
220
+ # Has inputs = shows form first
221
+ end
222
+ ```
223
+
224
+ ## Interaction Reference
225
+
226
+ ### Basic Structure
227
+
228
+ ```ruby
229
+ class PublishInteraction < Plutonium::Resource::Interaction
230
+ presents label: "Publish",
231
+ icon: Phlex::TablerIcons::Send,
232
+ description: "Make this post public"
233
+
234
+ attribute :resource # The record being acted upon
235
+
236
+ private
237
+
238
+ def execute
239
+ resource.update!(published: true, published_at: Time.current)
240
+ succeed(resource).with_message("Published!")
241
+ rescue ActiveRecord::RecordInvalid => e
242
+ failed(e.record.errors)
243
+ end
244
+ end
245
+ ```
246
+
247
+ ### With User Input
248
+
249
+ ```ruby
250
+ class InviteUserInteraction < Plutonium::Resource::Interaction
251
+ presents label: "Invite User", icon: Phlex::TablerIcons::Mail
252
+
253
+ attribute :resource # The company
254
+ attribute :email
255
+ attribute :role
256
+
257
+ input :email, as: :email
258
+ input :role, as: :select, choices: %w[admin member viewer]
259
+
260
+ validates :email, presence: true, format: {with: URI::MailTo::EMAIL_REGEXP}
261
+ validates :role, presence: true
262
+
263
+ private
264
+
265
+ def execute
266
+ UserInvite.create!(
267
+ company: resource,
268
+ email: email,
269
+ role: role
270
+ )
271
+ succeed(resource).with_message("Invitation sent to #{email}.")
272
+ rescue ActiveRecord::RecordInvalid => e
273
+ failed(e.record.errors)
274
+ end
275
+ end
276
+ ```
277
+
278
+ ### Bulk Action
279
+
280
+ ```ruby
281
+ class BulkArchiveInteraction < Plutonium::Resource::Interaction
282
+ presents label: "Archive Selected", icon: Phlex::TablerIcons::Archive
283
+
284
+ attribute :resources # Array of records (note: plural)
285
+
286
+ private
287
+
288
+ def execute
289
+ count = 0
290
+ resources.each do |record|
291
+ record.archived!
292
+ count += 1
293
+ end
294
+ succeed(resources).with_message("#{count} records archived.")
295
+ end
296
+ end
297
+ ```
298
+
299
+ ### Resource Action (No Record)
300
+
301
+ ```ruby
302
+ class ImportInteraction < Plutonium::Resource::Interaction
303
+ presents label: "Import CSV", icon: Phlex::TablerIcons::Upload
304
+
305
+ # No :resource or :resources = resource action
306
+ attribute :file
307
+
308
+ input :file, as: :file
309
+
310
+ validates :file, presence: true
311
+
312
+ private
313
+
314
+ def execute
315
+ # Import logic...
316
+ succeed(nil).with_message("Import completed.")
317
+ end
318
+ end
319
+ ```
320
+
321
+ ## Interaction Responses
322
+
323
+ ```ruby
324
+ def execute
325
+ # Success with message
326
+ succeed(resource).with_message("Done!")
327
+
328
+ # Success with redirect
329
+ succeed(resource)
330
+ .with_redirect_response(resource_url_for(resource))
331
+ .with_message("Redirecting...")
332
+
333
+ # Failure with field errors
334
+ failed(resource.errors)
335
+
336
+ # Failure with custom message
337
+ failed("Something went wrong")
338
+ end
339
+ ```
340
+
341
+ ## Inherited Actions
342
+
343
+ Actions defined in `ResourceDefinition` are inherited by all definitions:
344
+
345
+ ```ruby
346
+ # app/definitions/resource_definition.rb
347
+ class ResourceDefinition < Plutonium::Resource::Definition
348
+ action :archive,
349
+ interaction: ArchiveInteraction,
350
+ color: :danger,
351
+ position: 1000
352
+ end
353
+
354
+ # All definitions inherit the archive action automatically
355
+ class PostDefinition < ResourceDefinition
356
+ end
357
+ ```
358
+
359
+ ## Portal-Specific Actions
360
+
361
+ Override actions for a specific portal:
362
+
363
+ ```ruby
364
+ # packages/admin_portal/app/definitions/admin_portal/post_definition.rb
365
+ class AdminPortal::PostDefinition < ::PostDefinition
366
+ # Add admin-only actions
367
+ action :feature, interaction: FeaturePostInteraction
368
+ action :bulk_publish, interaction: BulkPublishInteraction
369
+ end
370
+ ```
371
+
372
+ ## Common Patterns
373
+
374
+ ### Archive/Restore
375
+
376
+ ```ruby
377
+ action :archive,
378
+ interaction: ArchiveInteraction,
379
+ record_action: true,
380
+ color: :danger
381
+
382
+ action :restore,
383
+ interaction: RestoreInteraction,
384
+ record_action: true
385
+ ```
386
+
387
+ ### Export
388
+
389
+ ```ruby
390
+ action :export,
391
+ interaction: ExportInteraction,
392
+ resource_action: true,
393
+ icon: Phlex::TablerIcons::Download
394
+ ```
395
+
396
+ ## Related
397
+
398
+ - [Definition Reference](./index)
399
+ - [Fields Reference](./fields)
400
+ - [Query Reference](./query)