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,439 @@
1
+ ---
2
+ name: forms
3
+ description: Plutonium forms - custom templates, Phlex form components, field builders, and theming
4
+ ---
5
+
6
+ # Plutonium Forms
7
+
8
+ Plutonium forms are built on [Phlexi::Form](https://github.com/radioactive-labs/phlexi-form), providing a Ruby-first approach to form building with Phlex components.
9
+
10
+ ## Form Class Hierarchy
11
+
12
+ ```
13
+ Phlexi::Form::Base
14
+ └── Plutonium::UI::Form::Base # Base with Plutonium components
15
+ ├── Plutonium::UI::Form::Resource # Resource CRUD forms
16
+ │ └── Plutonium::UI::Form::Interaction # Interactive action forms
17
+ └── Plutonium::UI::Form::Query # Search/filter forms
18
+ ```
19
+
20
+ ## Customizing Resource Forms
21
+
22
+ ### Override in Definition
23
+
24
+ ```ruby
25
+ class PostDefinition < ResourceDefinition
26
+ class Form < Form
27
+ def form_template
28
+ # Your custom form layout
29
+ render_fields
30
+ render_actions
31
+ end
32
+ end
33
+ end
34
+ ```
35
+
36
+ ### Form Template Methods
37
+
38
+ | Method | Description |
39
+ |--------|-------------|
40
+ | `form_template` | Main template method to override |
41
+ | `render_fields` | Render all permitted fields |
42
+ | `render_resource_field(name)` | Render a single field by name |
43
+ | `render_actions` | Render submit buttons |
44
+ | `fields_wrapper { }` | Wrapper div for field grid |
45
+ | `actions_wrapper { }` | Wrapper div for buttons |
46
+
47
+ ### Form Attributes
48
+
49
+ | Attribute | Description |
50
+ |-----------|-------------|
51
+ | `object` / `record` | The form object being edited |
52
+ | `resource_fields` | Array of permitted field names |
53
+ | `resource_definition` | The definition instance |
54
+
55
+ ## Custom Form Layout
56
+
57
+ ### Sectioned Form
58
+
59
+ ```ruby
60
+ class PostDefinition < ResourceDefinition
61
+ class Form < Form
62
+ def form_template
63
+ section("Basic Information") {
64
+ render_resource_field :title
65
+ render_resource_field :slug
66
+ }
67
+
68
+ section("Content") {
69
+ render_resource_field :content
70
+ render_resource_field :excerpt
71
+ }
72
+
73
+ section("Publishing") {
74
+ render_resource_field :published_at
75
+ render_resource_field :category
76
+ }
77
+
78
+ render_actions
79
+ end
80
+
81
+ private
82
+
83
+ def section(title, &)
84
+ div(class: "mb-8") {
85
+ h3(class: "text-lg font-semibold mb-4 text-gray-900 dark:text-white") { title }
86
+ fields_wrapper(&)
87
+ }
88
+ end
89
+ end
90
+ end
91
+ ```
92
+
93
+ ### Two-Column Layout
94
+
95
+ ```ruby
96
+ class Form < Form
97
+ def form_template
98
+ div(class: "grid grid-cols-1 lg:grid-cols-3 gap-6") {
99
+ # Main content - 2 columns
100
+ div(class: "lg:col-span-2") {
101
+ fields_wrapper {
102
+ render_resource_field :title
103
+ render_resource_field :content
104
+ }
105
+ }
106
+
107
+ # Sidebar - 1 column
108
+ div(class: "space-y-4") {
109
+ Panel {
110
+ h4(class: "font-medium mb-2") { "Settings" }
111
+ render_resource_field :status
112
+ render_resource_field :visibility
113
+ }
114
+ }
115
+ }
116
+
117
+ render_actions
118
+ end
119
+ end
120
+ ```
121
+
122
+ ## Field Builder
123
+
124
+ When using `render_resource_field`, Plutonium uses the field builder. For custom rendering, use the `field` method directly.
125
+
126
+ ### Basic Field Usage
127
+
128
+ ```ruby
129
+ def form_template
130
+ # Using field builder directly
131
+ render field(:title).wrapped {|f| f.input_tag }
132
+ render field(:content).wrapped {|f| f.easymde_tag }
133
+ render field(:published).wrapped {|f| f.checkbox_tag }
134
+
135
+ render_actions
136
+ end
137
+ ```
138
+
139
+ ### Field Builder Methods
140
+
141
+ The field builder (`f`) provides these tag methods:
142
+
143
+ | Method | Input Type |
144
+ |--------|------------|
145
+ | `f.input_tag` | Text input (auto-detects type) |
146
+ | `f.string_tag` | Text input |
147
+ | `f.text_tag` | Textarea |
148
+ | `f.number_tag` | Number input |
149
+ | `f.email_tag` | Email input |
150
+ | `f.password_tag` | Password input |
151
+ | `f.url_tag` | URL input |
152
+ | `f.tel_tag` | Telephone input |
153
+ | `f.hidden_tag` | Hidden input |
154
+ | `f.checkbox_tag` | Checkbox |
155
+ | `f.select_tag` | Select dropdown |
156
+ | `f.radio_button_tag` | Radio buttons |
157
+
158
+ ### Plutonium-Enhanced Tags
159
+
160
+ | Method | Description |
161
+ |--------|-------------|
162
+ | `f.easymde_tag` / `f.markdown_tag` | Markdown editor (EasyMDE) |
163
+ | `f.slim_select_tag` | Enhanced select (Slim Select) |
164
+ | `f.flatpickr_tag` | Date/time picker (Flatpickr) |
165
+ | `f.phone_tag` / `f.int_tel_input_tag` | International phone input |
166
+ | `f.uppy_tag` / `f.file_tag` | File upload (Uppy) |
167
+ | `f.secure_association_tag` | Association with authorization |
168
+ | `f.belongs_to_tag` | Belongs-to association |
169
+ | `f.has_many_tag` | Has-many association |
170
+ | `f.has_one_tag` | Has-one association |
171
+ | `f.key_value_store_tag` | Key-value pairs |
172
+
173
+ ### Field with Options
174
+
175
+ ```ruby
176
+ # Select with choices
177
+ render field(:status).wrapped { |f|
178
+ f.select_tag(choices: %w[draft published archived])
179
+ }
180
+
181
+ # Date picker with options
182
+ render field(:published_at).wrapped { |f|
183
+ f.flatpickr_tag(min_date: Date.today, enable_time: true)
184
+ }
185
+
186
+ # File upload with restrictions
187
+ render field(:avatar).wrapped { |f|
188
+ f.uppy_tag(
189
+ allowed_file_types: %w[.jpg .png .gif],
190
+ max_file_size: 5.megabytes
191
+ )
192
+ }
193
+ ```
194
+
195
+ ### Wrapped vs Unwrapped
196
+
197
+ ```ruby
198
+ # Wrapped - includes label, hint, errors
199
+ render field(:title).wrapped { |f| f.input_tag }
200
+
201
+ # Unwrapped - just the input element
202
+ render field(:title).input_tag
203
+
204
+ # Custom wrapper options
205
+ render field(:title).wrapped(class: "col-span-full") { |f|
206
+ f.input_tag
207
+ }
208
+ ```
209
+
210
+ ## Input Configuration in Definitions
211
+
212
+ Define inputs in the definition, render them in the form:
213
+
214
+ ```ruby
215
+ class PostDefinition < ResourceDefinition
216
+ # Configure inputs
217
+ input :title, hint: "Be descriptive", placeholder: "Enter title"
218
+ input :content, as: :markdown
219
+ input :status, as: :select, choices: %w[draft published]
220
+ input :published_at, as: :flatpickr
221
+
222
+ # Custom input with block
223
+ input :category do |f|
224
+ choices = Category.active.pluck(:name, :id)
225
+ f.select_tag(choices: choices)
226
+ end
227
+
228
+ class Form < Form
229
+ def form_template
230
+ # render_resource_field uses the input configuration
231
+ render_resource_field :title
232
+ render_resource_field :content
233
+ render_resource_field :status
234
+ render_resource_field :published_at
235
+ render_resource_field :category
236
+
237
+ render_actions
238
+ end
239
+ end
240
+ end
241
+ ```
242
+
243
+ ## Dynamic Forms (pre_submit)
244
+
245
+ Fields with `pre_submit: true` trigger form re-rendering on change:
246
+
247
+ ```ruby
248
+ class PostDefinition < ResourceDefinition
249
+ input :post_type, as: :select,
250
+ choices: %w[article video podcast],
251
+ pre_submit: true
252
+
253
+ input :video_url,
254
+ condition: -> { object.post_type == "video" }
255
+
256
+ input :podcast_url,
257
+ condition: -> { object.post_type == "podcast" }
258
+ end
259
+ ```
260
+
261
+ When `post_type` changes, the form re-renders via Turbo and shows/hides conditional fields.
262
+
263
+ ## Nested Forms
264
+
265
+ For `has_many` / `has_one` associations with `accepts_nested_attributes_for`:
266
+
267
+ ### Model Setup
268
+
269
+ ```ruby
270
+ class Post < ResourceRecord
271
+ has_many :comments
272
+ accepts_nested_attributes_for :comments, allow_destroy: true
273
+ end
274
+ ```
275
+
276
+ ### Definition Setup
277
+
278
+ ```ruby
279
+ class PostDefinition < ResourceDefinition
280
+ nested_input :comments do |n|
281
+ n.input :author_name
282
+ n.input :body, as: :text
283
+ end
284
+
285
+ # Or reference another definition
286
+ nested_input :comments, using: CommentDefinition, fields: %i[author_name body]
287
+ end
288
+ ```
289
+
290
+ ### Custom Nested Rendering
291
+
292
+ ```ruby
293
+ class Form < Form
294
+ def form_template
295
+ render_resource_field :title
296
+ render_resource_field :content
297
+
298
+ # Nested fields are automatically handled
299
+ render_resource_field :comments
300
+
301
+ render_actions
302
+ end
303
+ end
304
+ ```
305
+
306
+ ## Interaction Forms
307
+
308
+ Forms for interactive actions use `Plutonium::UI::Form::Interaction`:
309
+
310
+ ```ruby
311
+ class PublishPostInteraction < ResourceInteraction
312
+ attribute :publish_date, :date
313
+ attribute :notify_subscribers, :boolean, default: true
314
+
315
+ # Custom form
316
+ class Form < Form
317
+ def form_template
318
+ div(class: "space-y-4") {
319
+ render_resource_field :publish_date
320
+ render_resource_field :notify_subscribers
321
+ }
322
+ render_actions
323
+ end
324
+ end
325
+ end
326
+ ```
327
+
328
+ ## Form Actions
329
+
330
+ ### Default Actions
331
+
332
+ ```ruby
333
+ def render_actions
334
+ actions_wrapper {
335
+ # "Create and add another" / "Update and continue editing" button
336
+ # Primary submit button
337
+ render submit_button
338
+ }
339
+ end
340
+ ```
341
+
342
+ ### Custom Actions
343
+
344
+ ```ruby
345
+ def render_actions
346
+ actions_wrapper {
347
+ # Cancel link
348
+ a(href: resource_url_for(resource_class), class: "btn btn-secondary") {
349
+ "Cancel"
350
+ }
351
+
352
+ # Save as draft
353
+ button(type: :submit, name: "draft", value: "1", class: "btn") {
354
+ "Save Draft"
355
+ }
356
+
357
+ # Primary submit
358
+ render submit_button
359
+ }
360
+ end
361
+ ```
362
+
363
+ ## Form Context
364
+
365
+ Inside form templates, you have access to:
366
+
367
+ ```ruby
368
+ class Form < Form
369
+ def form_template
370
+ # Form object
371
+ object # The record
372
+ record # Alias for object
373
+ object.new_record? # Check if creating
374
+
375
+ # Request context
376
+ current_user
377
+ current_parent
378
+ current_scoped_entity
379
+ request
380
+ params
381
+
382
+ # Definition
383
+ resource_definition
384
+ resource_fields # Permitted fields
385
+
386
+ # URL helpers
387
+ resource_url_for(object)
388
+ resource_url_for(Post, action: :new)
389
+
390
+ # Rails helpers
391
+ helpers.link_to(...)
392
+ end
393
+ end
394
+ ```
395
+
396
+ ## Theming
397
+
398
+ Forms use a theme system for consistent styling:
399
+
400
+ ```ruby
401
+ class PostDefinition < ResourceDefinition
402
+ class Form < Form
403
+ class Theme < Plutonium::UI::Form::Theme
404
+ def self.theme
405
+ super.merge({
406
+ fields_wrapper: "grid grid-cols-2 gap-6",
407
+ actions_wrapper: "flex justify-between mt-8",
408
+ input: "w-full p-3 border-2 rounded-lg",
409
+ label: "block mb-1 font-bold text-gray-700"
410
+ })
411
+ end
412
+ end
413
+ end
414
+ end
415
+ ```
416
+
417
+ ### Theme Keys
418
+
419
+ | Key | Description |
420
+ |-----|-------------|
421
+ | `base` | Form container |
422
+ | `fields_wrapper` | Grid wrapper for fields |
423
+ | `actions_wrapper` | Wrapper for buttons |
424
+ | `wrapper` | Individual field wrapper |
425
+ | `label` | Label styling |
426
+ | `input` | Input styling |
427
+ | `hint` | Hint text styling |
428
+ | `error` | Error message styling |
429
+ | `button` | Submit button styling |
430
+ | `checkbox` | Checkbox styling |
431
+ | `select` | Select styling |
432
+
433
+ ## Related Skills
434
+
435
+ - `definition-fields` - Input configuration (as:, hint:, condition:)
436
+ - `views` - Custom page classes
437
+ - `assets` - TailwindCSS and component theming
438
+ - `interaction` - Interactive action forms
439
+ - `nested-resources` - Parent/child forms
@@ -0,0 +1,300 @@
1
+ ---
2
+ name: installation
3
+ description: Installing Plutonium in a Rails application - setup, generators, and configuration
4
+ ---
5
+
6
+ # Plutonium Installation
7
+
8
+ ## New Rails App (Recommended)
9
+
10
+ Use the Rails template for a fully configured setup:
11
+
12
+ ```bash
13
+ rails new myapp -a propshaft -j esbuild -c tailwind \
14
+ -m https://radioactive-labs.github.io/plutonium-core/templates/plutonium.rb
15
+ ```
16
+
17
+ This sets up Rails with Propshaft, esbuild, TailwindCSS, and Plutonium in one command.
18
+
19
+ ## Existing Rails App
20
+
21
+ ### Option 1: Rails Template
22
+
23
+ ```bash
24
+ bin/rails app:template \
25
+ LOCATION=https://radioactive-labs.github.io/plutonium-core/templates/base.rb
26
+ ```
27
+
28
+ ### Option 2: Manual Installation
29
+
30
+ ```bash
31
+ # Add to Gemfile
32
+ gem "plutonium"
33
+
34
+ # Install
35
+ bundle install
36
+ rails generate pu:core:install
37
+ ```
38
+
39
+ ## What Gets Generated
40
+
41
+ After `pu:core:install`:
42
+
43
+ ```
44
+ app/
45
+ ├── controllers/
46
+ │ ├── plutonium_controller.rb # Base controller
47
+ │ └── resource_controller.rb # Resource CRUD base
48
+ ├── definitions/
49
+ │ └── resource_definition.rb # Definition base class
50
+ ├── interactions/
51
+ │ └── resource_interaction.rb # Interaction base class
52
+ ├── models/
53
+ │ └── resource_record.rb # Abstract model base
54
+ ├── policies/
55
+ │ └── resource_policy.rb # Policy base class
56
+ └── views/
57
+ └── layouts/
58
+ └── resource.html.erb # Base layout
59
+
60
+ config/
61
+ ├── initializers/
62
+ │ └── plutonium.rb # Configuration
63
+ └── packages.rb # Package loader
64
+
65
+ packages/
66
+ └── .keep
67
+ ```
68
+
69
+ ## Base Classes
70
+
71
+ ### ResourceController
72
+
73
+ ```ruby
74
+ class ResourceController < PlutoniumController
75
+ include Plutonium::Resource::Controller
76
+ # Provides: index, show, new, create, edit, update, destroy
77
+ # Plus: interactive actions, authorization, query handling
78
+ end
79
+ ```
80
+
81
+ ### ResourcePolicy
82
+
83
+ ```ruby
84
+ class ResourcePolicy < Plutonium::Resource::Policy
85
+ def create?
86
+ true # Override with your logic
87
+ end
88
+
89
+ def read?
90
+ true
91
+ end
92
+ end
93
+ ```
94
+
95
+ ### ResourceDefinition
96
+
97
+ ```ruby
98
+ class ResourceDefinition < Plutonium::Resource::Definition
99
+ # Add app-wide definition defaults here
100
+ end
101
+ ```
102
+
103
+ ### ResourceRecord
104
+
105
+ ```ruby
106
+ class ResourceRecord < ApplicationRecord
107
+ self.abstract_class = true
108
+ # Models inherit from this for Plutonium features
109
+ end
110
+ ```
111
+
112
+ ## Authentication Setup
113
+
114
+ ### Install Rodauth
115
+
116
+ ```bash
117
+ rails generate pu:rodauth:install
118
+ ```
119
+
120
+ ### Create Account Types
121
+
122
+ ```bash
123
+ # Basic user account
124
+ rails generate pu:rodauth:account user
125
+
126
+ # Admin with 2FA, lockout, audit logging
127
+ rails generate pu:rodauth:admin
128
+
129
+ # Customer with entity association
130
+ rails generate pu:rodauth:customer customer
131
+ ```
132
+
133
+ ### Account Options
134
+
135
+ | Option | Description |
136
+ |--------|-------------|
137
+ | `--defaults` | Enable common features (login, logout, remember, reset_password) |
138
+ | `--kitchen_sink` | Enable all available features |
139
+ | `--no-allow_signup` | Disable public signup |
140
+ | `--entity=Organization` | Create associated entity model |
141
+
142
+ ### Connect Auth to Controllers
143
+
144
+ ```ruby
145
+ # app/controllers/resource_controller.rb
146
+ class ResourceController < PlutoniumController
147
+ include Plutonium::Resource::Controller
148
+ include Plutonium::Auth::Rodauth(:user) # Add this
149
+ end
150
+ ```
151
+
152
+ ## Creating Your First Resource
153
+
154
+ ```bash
155
+ rails generate pu:res:scaffold Post user:belongs_to title:string content:text
156
+ rails db:migrate
157
+ ```
158
+
159
+ ## Creating a Portal
160
+
161
+ ```bash
162
+ rails generate pu:pkg:portal admin
163
+ ```
164
+
165
+ Select authentication when prompted:
166
+ - **Rodauth account** - Use existing auth
167
+ - **Public access** - No authentication
168
+ - **Bring your own** - Custom implementation
169
+
170
+ ### Mount the Portal
171
+
172
+ ```ruby
173
+ # config/routes.rb
174
+ Rails.application.routes.draw do
175
+ mount AdminPortal::Engine, at: "/admin"
176
+ end
177
+ ```
178
+
179
+ ### Connect Resources to Portal
180
+
181
+ ```bash
182
+ rails generate pu:res:conn Post --dest=admin_portal
183
+ ```
184
+
185
+ ## Configuration
186
+
187
+ ```ruby
188
+ # config/initializers/plutonium.rb
189
+ Plutonium.configure do |config|
190
+ config.load_defaults 1.0
191
+
192
+ # Custom assets (optional)
193
+ # config.assets.stylesheet = "custom_stylesheet"
194
+ # config.assets.script = "custom_script"
195
+ # config.assets.logo = "custom_logo.png"
196
+ end
197
+ ```
198
+
199
+ ## Package System
200
+
201
+ Packages are loaded from `config/packages.rb`:
202
+
203
+ ```ruby
204
+ Dir.glob(File.expand_path("../packages/**/lib/engine.rb", __dir__)) { |package| load package }
205
+ ```
206
+
207
+ Create packages in `packages/` directory:
208
+ - **Feature packages** - Business logic (`rails g pu:pkg:package blogging`)
209
+ - **Portal packages** - Web interfaces (`rails g pu:pkg:portal admin`)
210
+
211
+ ## Post-Installation Checklist
212
+
213
+ 1. **Install core**
214
+ ```bash
215
+ rails generate pu:core:install
216
+ ```
217
+
218
+ 2. **Setup authentication** (if needed)
219
+ ```bash
220
+ rails generate pu:rodauth:install
221
+ rails generate pu:rodauth:account user
222
+ ```
223
+
224
+ 3. **Create a portal**
225
+ ```bash
226
+ rails generate pu:pkg:portal admin
227
+ ```
228
+
229
+ 4. **Create resources**
230
+ ```bash
231
+ rails generate pu:res:scaffold Post title:string content:text
232
+ ```
233
+
234
+ 5. **Connect resources to portal**
235
+ ```bash
236
+ rails generate pu:res:conn Post --dest=admin_portal
237
+ ```
238
+
239
+ 6. **Run migrations**
240
+ ```bash
241
+ rails db:migrate
242
+ ```
243
+
244
+ 7. **Mount portal** (add to `config/routes.rb`)
245
+ ```ruby
246
+ mount AdminPortal::Engine, at: "/admin"
247
+ ```
248
+
249
+ 8. **Start server**
250
+ ```bash
251
+ rails server
252
+ ```
253
+
254
+ ## Converting Existing Models
255
+
256
+ For models that already exist in your app:
257
+
258
+ 1. Include the module:
259
+ ```ruby
260
+ class Post < ApplicationRecord
261
+ include Plutonium::Resource::Record
262
+ end
263
+ ```
264
+
265
+ 2. Generate supporting files (skips model/migration):
266
+ ```bash
267
+ rails g pu:res:scaffold Post
268
+ ```
269
+
270
+ 3. Connect to portal:
271
+ ```bash
272
+ rails g pu:res:conn Post --dest=admin_portal
273
+ ```
274
+
275
+ ## Generator Reference
276
+
277
+ | Generator | Purpose |
278
+ |-----------|---------|
279
+ | `pu:core:install` | Initial Plutonium setup |
280
+ | `pu:rodauth:install` | Setup Rodauth authentication |
281
+ | `pu:rodauth:account NAME` | Create user account type |
282
+ | `pu:rodauth:admin` | Create admin account with 2FA |
283
+ | `pu:rodauth:customer NAME` | Create customer with entity |
284
+ | `pu:pkg:package NAME` | Create feature package |
285
+ | `pu:pkg:portal NAME` | Create portal package |
286
+ | `pu:res:scaffold NAME` | Create resource (model, policy, definition, controller) |
287
+ | `pu:res:conn NAME` | Connect resource to portal |
288
+ | `pu:eject:layout` | Eject layout files for customization |
289
+ | `pu:skills:sync` | Sync Claude Code skills to project |
290
+
291
+ ## Related Skills
292
+
293
+ - `resource` - Resource architecture overview
294
+ - `rodauth` - Authentication setup and configuration
295
+ - `package` - Feature and portal packages
296
+ - `portal` - Portal configuration
297
+ - `views` - Custom pages, layouts, and Phlex components
298
+ - `assets` - TailwindCSS and custom styling
299
+ - `create-resource` - Resource scaffold options
300
+ - `connect-resource` - Portal connection