plutonium 0.55.0 โ 0.56.1
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.
- checksums.yaml +4 -4
- data/.claude/skills/plutonium/SKILL.md +1 -1
- data/.claude/skills/plutonium-app/SKILL.md +1 -1
- data/.claude/skills/plutonium-auth/SKILL.md +1 -1
- data/.claude/skills/plutonium-resource/SKILL.md +56 -3
- data/.claude/skills/plutonium-ui/SKILL.md +15 -2
- data/CHANGELOG.md +44 -0
- data/CONTRIBUTING.md +1 -1
- data/README.md +38 -16
- data/app/assets/plutonium.css +1 -1
- data/app/assets/plutonium.js +94 -26
- data/app/assets/plutonium.js.map +2 -2
- data/app/assets/plutonium.min.js +9 -9
- data/app/assets/plutonium.min.js.map +3 -3
- data/config/initializers/rabl.rb +16 -0
- data/docs/.vitepress/config.ts +1 -0
- data/docs/getting-started/installation.md +2 -2
- data/docs/getting-started/tutorial/02-first-resource.md +1 -1
- data/docs/getting-started/tutorial/03-authentication.md +3 -3
- data/docs/guides/adding-resources.md +1 -1
- data/docs/guides/authentication.md +1 -1
- data/docs/guides/creating-packages.md +1 -1
- data/docs/guides/multi-tenancy.md +1 -1
- data/docs/guides/nested-resources.md +1 -1
- data/docs/guides/user-invites.md +1 -1
- data/docs/guides/user-profile.md +1 -1
- data/docs/public/templates/lite.rb +10 -0
- data/docs/reference/app/generators.md +3 -3
- data/docs/reference/app/index.md +1 -1
- data/docs/reference/app/portals.md +1 -1
- data/docs/reference/auth/profile.md +1 -1
- data/docs/reference/generators/lite.md +65 -0
- data/docs/reference/resource/actions.md +55 -0
- data/docs/reference/resource/definition.md +18 -2
- data/docs/reference/resource/index.md +1 -1
- data/docs/reference/tenancy/invites.md +1 -1
- data/docs/reference/ui/assets.md +14 -0
- data/docs/reference/ui/displays.md +27 -1
- data/docs/reference/ui/forms.md +2 -1
- data/docs/reference/ui/layouts.md +33 -0
- data/docs/superpowers/plans/2026-06-04-sqlite-tune-maintenance-generators.md +857 -0
- data/docs/superpowers/plans/2026-06-04-sqlite-tune-maintenance-generators.md.tasks.json +45 -0
- data/docs/superpowers/specs/2026-06-04-sqlite-tune-maintenance-generators-design.md +238 -0
- data/gemfiles/rails_7.gemfile.lock +1 -1
- data/gemfiles/rails_8.0.gemfile.lock +1 -1
- data/gemfiles/rails_8.1.gemfile.lock +1 -1
- data/lib/generators/pu/core/typespec/typespec_generator.rb +1 -1
- data/lib/generators/pu/core/update/update_generator.rb +4 -1
- data/lib/generators/pu/lib/plutonium_generators/concerns/configures_recurring.rb +89 -0
- data/lib/generators/pu/lite/maintenance/maintenance_generator.rb +45 -0
- data/lib/generators/pu/lite/maintenance/templates/app/jobs/sqlite_maintenance_job.rb.tt +60 -0
- data/lib/generators/pu/lite/rails_pulse/rails_pulse_generator.rb +4 -51
- data/lib/generators/pu/lite/rails_pulse/templates/config/initializers/rails_pulse.rb.tt +1 -1
- data/lib/generators/pu/lite/tune/tune_generator.rb +105 -0
- data/lib/generators/pu/saas/welcome_generator.rb +1 -1
- data/lib/plutonium/action/base.rb +19 -2
- data/lib/plutonium/action/condition_context.rb +33 -0
- data/lib/plutonium/models/has_cents.rb +10 -0
- data/lib/plutonium/resource/controllers/interactive_actions.rb +19 -2
- data/lib/plutonium/routing/mapper_extensions.rb +5 -0
- data/lib/plutonium/ui/display/base.rb +9 -0
- data/lib/plutonium/ui/display/components/badge.rb +83 -0
- data/lib/plutonium/ui/display/components/boolean.rb +28 -6
- data/lib/plutonium/ui/display/components/currency.rb +50 -0
- data/lib/plutonium/ui/display/options/inferred_types.rb +13 -0
- data/lib/plutonium/ui/display/theme.rb +5 -0
- data/lib/plutonium/ui/form/base.rb +5 -0
- data/lib/plutonium/ui/form/components/toggle.rb +14 -0
- data/lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb +14 -25
- data/lib/plutonium/ui/form/concerns/renders_repeater_row_controls.rb +67 -0
- data/lib/plutonium/ui/form/concerns/renders_structured_inputs.rb +5 -38
- data/lib/plutonium/ui/form/interaction.rb +7 -2
- data/lib/plutonium/ui/form/options/inferred_types.rb +2 -0
- data/lib/plutonium/ui/form/resource.rb +1 -0
- data/lib/plutonium/ui/form/theme.rb +12 -0
- data/lib/plutonium/ui/grid/card.rb +61 -23
- data/lib/plutonium/ui/layout/icon_rail.rb +29 -9
- data/lib/plutonium/ui/page/index.rb +1 -1
- data/lib/plutonium/ui/page/show.rb +1 -1
- data/lib/plutonium/ui/sidebar_menu.rb +29 -0
- data/lib/plutonium/ui/table/resource.rb +2 -2
- data/lib/plutonium/version.rb +1 -1
- data/package.json +1 -1
- data/plutonium.gemspec +5 -4
- data/src/css/components.css +126 -0
- data/src/js/controllers/dirty_form_guard_controller.js +55 -4
- data/src/js/controllers/nested_resource_form_fields_controller.js +35 -12
- data/src/js/controllers/resource_drop_down_controller.js +49 -14
- metadata +20 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 17f6996a0503aeccb6070cb0c379d09bfd253e2356a839745368bcaf0110e377
|
|
4
|
+
data.tar.gz: 0603760cfc2a10d6bedaa4b225b33fc5cf5b632a36829c4e6c9e9c4ce7b7971b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b77a88b0c0e04b5a9e143c88b292ec305f7d5c8ef74efe3e0891f20c772e63eef35383a7785338863f4cd9a942c5b76c94c1fa6fedc2995c41da8df680f1e390
|
|
7
|
+
data.tar.gz: 7dd9fd4420c8145227929c4f6ec434520c6701ea387767d9395bb74b413d2e99ce451611c89797604a1cf5a62bb51368a4aa2f5c79d1b698a1c11842979dcb29
|
|
@@ -127,7 +127,7 @@ Meta-generators (`pu:saas:setup`) propagate flags to the generators they chain.
|
|
|
127
127
|
|
|
128
128
|
1. **Load the bootstrap bundle** (or the targeted skill from the router table).
|
|
129
129
|
2. **Generate** โ `rails g pu:res:scaffold Model field:type ... --dest=main_app`.
|
|
130
|
-
3. **Migrate** โ `rails db:
|
|
130
|
+
3. **Migrate** โ `rails db:prepare`.
|
|
131
131
|
4. **Connect** โ `rails g pu:res:conn Model --dest=portal_name`.
|
|
132
132
|
5. **Customize** โ edit definition / policy as needed.
|
|
133
133
|
6. **Verify** โ hit the route in the browser.
|
|
@@ -62,7 +62,7 @@ rails generate pu:pkg:portal admin --auth=user
|
|
|
62
62
|
|
|
63
63
|
# 4. First resource
|
|
64
64
|
rails generate pu:res:scaffold Post user:belongs_to title:string 'content:text?' --dest=main_app
|
|
65
|
-
rails db:
|
|
65
|
+
rails db:prepare
|
|
66
66
|
|
|
67
67
|
# 5. Connect resource to portal
|
|
68
68
|
rails generate pu:res:conn Post --dest=admin_portal
|
|
@@ -279,7 +279,7 @@ rails g pu:profile:setup date_of_birth:date bio:text \
|
|
|
279
279
|
rails generate pu:profile:install bio:text avatar:attachment 'timezone:string?' \
|
|
280
280
|
--dest=customer
|
|
281
281
|
|
|
282
|
-
rails db:
|
|
282
|
+
rails db:prepare
|
|
283
283
|
|
|
284
284
|
rails generate pu:profile:conn --dest=customer_portal
|
|
285
285
|
```
|
|
@@ -29,7 +29,7 @@ For tenancy / `associated_with` / `relation_scope`, load [[plutonium-tenancy]].
|
|
|
29
29
|
1. Pick destination: `--dest=main_app` or `--dest=package_name`.
|
|
30
30
|
2. Run `rails g pu:res:scaffold ResourceName field:type ... --dest=<dest>`.
|
|
31
31
|
3. Review the generated migration โ add cascade deletes, composite indexes, defaults.
|
|
32
|
-
4. `rails db:
|
|
32
|
+
4. `rails db:prepare`.
|
|
33
33
|
5. `rails g pu:res:conn ResourceName --dest=<portal_name>`.
|
|
34
34
|
6. Customize the policy's `permitted_attributes_for_*` as needed.
|
|
35
35
|
7. Open the portal route in the browser.
|
|
@@ -484,7 +484,7 @@ end
|
|
|
484
484
|
| Text | `:string`, `:text`, `:email`, `:url`, `:tel`, `:password` |
|
|
485
485
|
| Rich Text | `:markdown` (EasyMDE) |
|
|
486
486
|
| Numeric | `:number`, `:integer`, `:decimal`, `:range` |
|
|
487
|
-
| Boolean | `:boolean` |
|
|
487
|
+
| Boolean | `:toggle` / `:switch` (switch โ **default** for boolean columns), `:boolean` (plain checkbox) |
|
|
488
488
|
| Date/Time | `:date`, `:time`, `:datetime` |
|
|
489
489
|
| Selection | `:select`, `:slim_select`, `:radio_buttons`, `:check_boxes` |
|
|
490
490
|
| Files | `:file`, `:uppy`, `:attachment` |
|
|
@@ -493,7 +493,26 @@ end
|
|
|
493
493
|
|
|
494
494
|
### Display Types (show / index)
|
|
495
495
|
|
|
496
|
-
`:string`, `:text`, `:email`, `:url`, `:phone`, `:markdown`, `:number`, `:integer`, `:decimal`, `:boolean`, `:date`, `:time`, `:datetime`, `:association`, `:attachment`
|
|
496
|
+
`:string`, `:text`, `:email`, `:url`, `:phone`, `:markdown`, `:number`, `:integer`, `:decimal`, `:boolean`, `:badge`, `:currency`, `:color`, `:date`, `:time`, `:datetime`, `:association`, `:attachment`
|
|
497
|
+
|
|
498
|
+
#### Auto-inferred display formatting
|
|
499
|
+
|
|
500
|
+
These render automatically โ declare an `as:` only to override or pass options:
|
|
501
|
+
|
|
502
|
+
| Column | Renders as | Notes |
|
|
503
|
+
|--------|-----------|-------|
|
|
504
|
+
| `boolean` | Yes/No pill (`:boolean`) | green "Yes" / neutral "No". Override labels: `true_label:`, `false_label:` |
|
|
505
|
+
| `enum` | colored status badge (`:badge`) | known statuses (active, pending, failedโฆ) auto-colored; unknown values get a stable decorative color |
|
|
506
|
+
| `has_cents` decimal | currency (`:currency`) | delimited, 2 decimals, **no symbol** unless you add `unit:` |
|
|
507
|
+
|
|
508
|
+
```ruby
|
|
509
|
+
display :status, as: :badge, colors: {archived: :neutral, vip: :accent} # override per-value color
|
|
510
|
+
display :price, as: :currency, unit: "ยฃ" # literal symbol
|
|
511
|
+
display :price, as: :currency, unit: :currency_symbol # Symbol โ read off the record (per-row)
|
|
512
|
+
display :active, as: :boolean, true_label: "Live", false_label: "Off"
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
Badge color keys: `:neutral`, `:primary`, `:secondary`, `:success`, `:danger`, `:warning`, `:info`, `:accent`.
|
|
497
516
|
|
|
498
517
|
## Field Options
|
|
499
518
|
|
|
@@ -1031,6 +1050,10 @@ action :name,
|
|
|
1031
1050
|
collection_record_action: true,
|
|
1032
1051
|
bulk_action: true,
|
|
1033
1052
|
|
|
1053
|
+
# Conditional visibility โ display-only toggle, NOT authorization (see below).
|
|
1054
|
+
# `-> { false }` keeps the route live but hides the button (e.g. API-only).
|
|
1055
|
+
condition: -> { params[:beta] == "1" },
|
|
1056
|
+
|
|
1034
1057
|
# Grouping
|
|
1035
1058
|
category: :primary, # :primary, :secondary, :danger
|
|
1036
1059
|
position: 50,
|
|
@@ -1043,6 +1066,36 @@ action :name,
|
|
|
1043
1066
|
size: :lg # :sm / :md / :lg / :xl / :auto / :full โ overrides definition's modal size
|
|
1044
1067
|
```
|
|
1045
1068
|
|
|
1069
|
+
### Conditional Actions (`condition:`)
|
|
1070
|
+
|
|
1071
|
+
Like `condition:` on inputs/displays/columns โ define an action but render its **button** only when a runtime proc is truthy. The action and its route stay live either way; `condition:` only toggles the UI.
|
|
1072
|
+
|
|
1073
|
+
Headline use case: **expose an action's endpoint without a button** โ one you call from the API, a webhook, or another service. Hide it with an always-falsy condition; the route still works:
|
|
1074
|
+
|
|
1075
|
+
```ruby
|
|
1076
|
+
# Defined and callable (API / programmatic), but no button anywhere:
|
|
1077
|
+
action :sync_inventory, interaction: SyncInventoryInteraction, condition: -> { false }
|
|
1078
|
+
|
|
1079
|
+
# Per-record display state โ object is the row/shown record:
|
|
1080
|
+
action :reopen, interaction: ReopenInteraction, condition: -> { object.closed? }
|
|
1081
|
+
|
|
1082
|
+
# View/request-level toggle (feature flag, beta mode):
|
|
1083
|
+
action :preview, interaction: PreviewInteraction, condition: -> { params[:beta] == "1" }
|
|
1084
|
+
```
|
|
1085
|
+
|
|
1086
|
+
Inside the proc, `object`/`record` is the contextual record โ the row/shown record for **record** and **collection-record** actions, **nil** for **resource** and **bulk** actions (guard with `object&.โฆ` if shared). Every other call delegates to the **view context**: `current_user`, `current_parent`, `params`, `request`, `allowed_to?`, `resource_record!`, etc. `object` is evaluated per row in tables/grids, so per-record show/hide works there.
|
|
1087
|
+
|
|
1088
|
+
๐จ **`condition:` is NOT authorization โ it only hides the button.** A hidden action still has a live route; anyone with the URL can trigger it. "Who may run this" belongs in the policy:
|
|
1089
|
+
|
|
1090
|
+
```ruby
|
|
1091
|
+
# ๐ซ WRONG โ does not stop non-admins; the route is live.
|
|
1092
|
+
action :wipe, interaction: WipeInteraction, condition: -> { current_user.admin? }
|
|
1093
|
+
# โ
RIGHT โ authorization in the policy, enforced regardless of condition:
|
|
1094
|
+
def wipe? = current_user.admin?
|
|
1095
|
+
```
|
|
1096
|
+
|
|
1097
|
+
The two compose: an action's button shows only when the policy permits **and** the condition is truthy; execution is gated by the policy alone. Use `object` in `condition:` for per-record *display*; use the policy for per-record *authorization*.
|
|
1098
|
+
|
|
1046
1099
|
`Action#with(...)` โ actions are frozen value objects; clone with overrides:
|
|
1047
1100
|
|
|
1048
1101
|
```ruby
|
|
@@ -252,6 +252,7 @@ render field(:title).wrapped(class: "col-span-full") { |f| f.input_tag }
|
|
|
252
252
|
| `input_tag` | text (auto-detected type) |
|
|
253
253
|
| `string_tag`, `text_tag`, `number_tag`, `email_tag`, `password_tag`, `url_tag`, `tel_tag`, `hidden_tag` | standard HTML inputs |
|
|
254
254
|
| `checkbox_tag`, `select_tag`, `radio_button_tag` | standard |
|
|
255
|
+
| `toggle_tag` / `switch_tag` | switch-styled boolean (`as: :toggle` / `:switch`) โ default for boolean columns; `as: :boolean` for a plain checkbox |
|
|
255
256
|
|
|
256
257
|
### Plutonium-enhanced tags
|
|
257
258
|
|
|
@@ -513,6 +514,16 @@ rails generate pu:eject:layout
|
|
|
513
514
|
|
|
514
515
|
These copy `_resource_header.html.erb`, `_resource_sidebar.html.erb`, and `layouts/resource.html.erb` into the portal so you can edit them directly.
|
|
515
516
|
|
|
517
|
+
## Navigation menu items
|
|
518
|
+
|
|
519
|
+
The sidebar/icon-rail menu is built with `Phlexi::Menu::Builder` in `_resource_sidebar.html.erb`. Extra options on `item` are spread onto the rendered `<a>`, so an item can opt into `target` / `rel` / `data:` / `aria:`:
|
|
520
|
+
|
|
521
|
+
```ruby
|
|
522
|
+
m.item "Inbox", url: inbox_path, icon: Icon, target: "_blank", rel: "noopener", data: {turbo_frame: "_top"}
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
Applies to both shells (icon-rail leaf, parent flyout trigger, and flyout children; classic sidebar). Framework `class`/`data`/`aria` win on conflict โ `class:` merges with the base classes, and on a parent trigger `data:`/`aria:` merge with the flyout wiring so options can't break the toggle. Phlexi's reserved `:active` key is never emitted as an attribute.
|
|
526
|
+
|
|
516
527
|
## Custom layout class (Phlex)
|
|
517
528
|
|
|
518
529
|
```ruby
|
|
@@ -733,7 +744,8 @@ Ready-to-use styled components in `src/css/components.css`. **Prefer these over
|
|
|
733
744
|
### Inputs, cards, panels, tables, toolbars, empty states
|
|
734
745
|
|
|
735
746
|
```
|
|
736
|
-
.pu-input / -invalid / -valid .pu-label / -required .pu-hint / .pu-error .pu-checkbox
|
|
747
|
+
.pu-input / -invalid / -valid .pu-label / -required .pu-hint / .pu-error .pu-checkbox / .pu-toggle
|
|
748
|
+
.pu-badge / -neutral / -primary / -secondary / -success / -danger / -warning / -info / -accent
|
|
737
749
|
.pu-card / .pu-card-body
|
|
738
750
|
.pu-panel-header / -title / -description
|
|
739
751
|
.pu-table-wrapper / .pu-table / -header / -header-cell / -body-row / -body-row-selected / -body-cell / .pu-selection-cell
|
|
@@ -845,7 +857,7 @@ end
|
|
|
845
857
|
|
|
846
858
|
### Display theme keys
|
|
847
859
|
|
|
848
|
-
`fields_wrapper`, `label`, `description`, `string`, `text`, `link`, `email`, `phone`, `markdown`, `json`.
|
|
860
|
+
`fields_wrapper`, `label`, `description`, `string`, `text`, `link`, `email`, `phone`, `markdown`, `json`, `boolean`, `badge`, `currency`, `color`.
|
|
849
861
|
|
|
850
862
|
## Table theme
|
|
851
863
|
|
|
@@ -914,6 +926,7 @@ end
|
|
|
914
926
|
- **Dark mode is `selector`, not `class`.** Toggle via `document.documentElement.classList.toggle('dark')`.
|
|
915
927
|
- **Tokens are CSS variables, not Tailwind keys** โ `bg-[var(--pu-surface)]`, not `bg-pu-surface`.
|
|
916
928
|
- **`render_actions` is mandatory in custom `form_template`** โ otherwise no submit button.
|
|
929
|
+
- **Dropdowns (`resource-drop-down`) teleport their menu to `<body>` while open.** popper's `fixed` strategy alone is still clipped by a transformed + `overflow:hidden` ancestor (e.g. grid cards, app shells), so the controller reparents the open menu to `<body>` and restores it on close. Don't rely on the menu being a DOM child of its trigger while open.
|
|
917
930
|
|
|
918
931
|
---
|
|
919
932
|
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,47 @@
|
|
|
1
|
+
## [0.56.1] - 2026-06-05
|
|
2
|
+
|
|
3
|
+
### ๐ Features
|
|
4
|
+
|
|
5
|
+
- *(actions)* Add display-only condition: to actions
|
|
6
|
+
|
|
7
|
+
### ๐ Bug Fixes
|
|
8
|
+
|
|
9
|
+
- *(dummy)* Version-adapt kitchen_sinks migration
|
|
10
|
+
|
|
11
|
+
### ๐ Documentation
|
|
12
|
+
|
|
13
|
+
- Recommend db:prepare, firm up README, document conditional actions
|
|
14
|
+
## [0.56.0] - 2026-06-05
|
|
15
|
+
|
|
16
|
+
### ๐ Features
|
|
17
|
+
|
|
18
|
+
- *(ui)* Auto-rendered components for boolean, enum & money fields
|
|
19
|
+
- *(generators/lite)* Add pu:lite:tune and pu:lite:maintenance for SQLite tuning + maintenance
|
|
20
|
+
- *(ui)* Sidebar menu items accept arbitrary link attributes
|
|
21
|
+
- *(ui)* Restore deleted nested rows + shared, polished removed bar
|
|
22
|
+
- *(ui)* Type-aware grid cards + overhaul KitchenSink dummy resource
|
|
23
|
+
|
|
24
|
+
### ๐ Bug Fixes
|
|
25
|
+
|
|
26
|
+
- *(generators/update)* Sync skills in a fresh process; pin post-install notice to 0.49.0
|
|
27
|
+
- *(ui)* Native multi-selects render at a usable height
|
|
28
|
+
- *(ui)* Dropdown menu teleports to <body> to escape overflow clipping
|
|
29
|
+
- *(routing)* Force :resources route_type for has_many nested routes
|
|
30
|
+
- *(ui)* Record-scoped commit URL for actions with record_action: false
|
|
31
|
+
- *(api)* Serialize JSON values via as_json (ISO 8601 datetimes)
|
|
32
|
+
- *(generators)* Add reading role to Rails Pulse connects_to config
|
|
33
|
+
- *(actions)* Bind subject during interaction param extraction
|
|
34
|
+
- *(ui)* Dirty-form-guard tracks edits via first-interaction baseline
|
|
35
|
+
- *(ui)* Give the JSON form input dark-mode styling
|
|
36
|
+
|
|
37
|
+
### ๐งช Testing
|
|
38
|
+
|
|
39
|
+
- Fix stale generator assertions and drop committed dummy schema
|
|
40
|
+
- *(dummy)* Add KitchenSink resource exercising every input/display type
|
|
41
|
+
|
|
42
|
+
### โ๏ธ Miscellaneous Tasks
|
|
43
|
+
|
|
44
|
+
- Sync appraisal gemfile.lock files to v0.55.0
|
|
1
45
|
## [0.55.0] - 2026-06-03
|
|
2
46
|
|
|
3
47
|
### ๐ Features
|
data/CONTRIBUTING.md
CHANGED
data/README.md
CHANGED
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/rb/plutonium)
|
|
4
4
|
[](https://github.com/radioactive-labs/plutonium-core/actions/workflows/main.yml)
|
|
5
|
+
[](LICENSE.txt)
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
**The Rails framework for things you should never write again.**
|
|
8
|
+
|
|
9
|
+
Convention over configuration, extended to everything you keep rebuilding: **CRUD. Auth. Authorization. Multi-tenancy. Admin portals. Search, filters, bulk actions.** All generated. All customizable. All Rails.
|
|
7
10
|
|
|
8
11
|
## Quick Start
|
|
9
12
|
|
|
@@ -12,20 +15,36 @@ rails new myapp -a propshaft -j esbuild -c tailwind \
|
|
|
12
15
|
-m https://radioactive-labs.github.io/plutonium-core/templates/plutonium.rb
|
|
13
16
|
```
|
|
14
17
|
|
|
15
|
-
Then create
|
|
18
|
+
Then scaffold a resource, create a portal, and connect them:
|
|
16
19
|
|
|
17
20
|
```bash
|
|
18
21
|
cd myapp
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
|
|
23
|
+
# Scaffold a resource โ model, migration, definition, policy
|
|
24
|
+
rails g pu:res:scaffold Post title:string body:text published_at:datetime --dest=main_app
|
|
25
|
+
rails db:prepare
|
|
26
|
+
|
|
27
|
+
# Create a portal (web interface) and connect the resource to it
|
|
28
|
+
rails g pu:pkg:portal app --public
|
|
29
|
+
rails g pu:res:conn Post --dest=app_portal
|
|
30
|
+
|
|
21
31
|
bin/dev
|
|
22
32
|
```
|
|
23
33
|
|
|
24
|
-
Visit `http://localhost:3000`
|
|
34
|
+
Visit `http://localhost:3000/app/posts` โ you have a complete CRUD interface.
|
|
25
35
|
|
|
26
|
-
## What You
|
|
36
|
+
## What You Stop Writing
|
|
27
37
|
|
|
28
|
-
|
|
38
|
+
Same scaffold command you already know. A very different surface area.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
rails g scaffold Post ... # Rails: just CRUD
|
|
42
|
+
rails g pu:res:scaffold Post ... # Plutonium: full CRUD + search + filters + bulk actions
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
And it doesn't stop at scaffolds:
|
|
46
|
+
|
|
47
|
+
**Resource-oriented architecture** โ models, policies, definitions, and controllers that work together:
|
|
29
48
|
|
|
30
49
|
```ruby
|
|
31
50
|
# Policy controls WHO can do WHAT
|
|
@@ -47,7 +66,7 @@ class PostDefinition < ResourceDefinition
|
|
|
47
66
|
end
|
|
48
67
|
```
|
|
49
68
|
|
|
50
|
-
**Packages
|
|
69
|
+
**Packages and portals** โ split your app into feature engines and themed web interfaces:
|
|
51
70
|
|
|
52
71
|
```bash
|
|
53
72
|
rails g pu:pkg:package blogging # Business logic
|
|
@@ -65,7 +84,7 @@ rails g pu:rodauth:account user
|
|
|
65
84
|
**Multi-tenancy** with entity scoping:
|
|
66
85
|
|
|
67
86
|
```ruby
|
|
68
|
-
# In portal engine
|
|
87
|
+
# In a portal engine
|
|
69
88
|
scope_to_entity Organization, strategy: :path
|
|
70
89
|
# Routes become /organizations/:organization_id/posts
|
|
71
90
|
```
|
|
@@ -86,6 +105,13 @@ class PublishInteraction < ResourceInteraction
|
|
|
86
105
|
end
|
|
87
106
|
```
|
|
88
107
|
|
|
108
|
+
## Why Plutonium
|
|
109
|
+
|
|
110
|
+
- **Convention over configuration** โ extended to resources, policies, portals, and tenancy, not just routes and views.
|
|
111
|
+
- **It's just Rails** โ generated code lives in your repo. Edit it, override it, delete it. The "magic" is regular Ruby mixins you can read.
|
|
112
|
+
- **Multi-tenant ready** โ path or domain tenancy, scoped relations, invites and memberships out of the box.
|
|
113
|
+
- **AI-readable** โ predictable file layout and naming, plus built-in [Claude Code skills](.claude/skills) that teach AI assistants the patterns.
|
|
114
|
+
|
|
89
115
|
## Documentation
|
|
90
116
|
|
|
91
117
|
Full documentation at **[radioactive-labs.github.io/plutonium-core](https://radioactive-labs.github.io/plutonium-core/)**
|
|
@@ -97,18 +123,14 @@ Full documentation at **[radioactive-labs.github.io/plutonium-core](https://radi
|
|
|
97
123
|
|
|
98
124
|
## Requirements
|
|
99
125
|
|
|
100
|
-
- Ruby 3.2+
|
|
101
|
-
- Rails 7.
|
|
126
|
+
- Ruby 3.2.2+
|
|
127
|
+
- Rails 7.2+ (Rails 8 recommended)
|
|
102
128
|
- Node.js 18+
|
|
103
129
|
|
|
104
130
|
## Contributing
|
|
105
131
|
|
|
106
132
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
|
|
107
133
|
|
|
108
|
-
## Status
|
|
109
|
-
|
|
110
|
-
Plutonium is used in production but still evolving. APIs may change between minor versions. Pin your version in Gemfile.
|
|
111
|
-
|
|
112
134
|
## License
|
|
113
135
|
|
|
114
|
-
MIT License
|
|
136
|
+
MIT License โ see [LICENSE.txt](LICENSE.txt).
|