plutonium 0.64.0 → 0.65.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.
- checksums.yaml +4 -4
- data/.claude/skills/plutonium/SKILL.md +4 -1
- data/.claude/skills/plutonium-app/SKILL.md +14 -0
- data/.claude/skills/plutonium-dashboard/SKILL.md +161 -0
- data/.claude/skills/plutonium-resource/SKILL.md +94 -14
- data/.claude/skills/plutonium-ui/SKILL.md +22 -1
- data/CHANGELOG.md +59 -0
- data/CLAUDE.md +282 -210
- data/SECURITY.md +1 -1
- data/app/assets/plutonium-charts.js +20604 -0
- data/app/assets/plutonium-charts.js.map +7 -0
- data/app/assets/plutonium-charts.min.js +41 -0
- data/app/assets/plutonium-charts.min.js.map +7 -0
- data/app/assets/plutonium.css +1 -1
- data/app/assets/plutonium.js +794 -629
- data/app/assets/plutonium.js.map +4 -4
- data/app/assets/plutonium.min.js +48 -48
- data/app/assets/plutonium.min.js.map +4 -4
- data/app/views/plutonium/_flash_alerts.html.erb +2 -2
- data/app/views/plutonium/_resource_header.html.erb +2 -2
- data/app/views/plutonium/_resource_sidebar.html.erb +11 -2
- data/app/views/plutonium/_toast.html.erb +2 -2
- data/app/views/rodauth/_login_form.html.erb +1 -1
- data/app/views/rodauth/_password_visibility.html.erb +1 -1
- data/app/views/rodauth/add_recovery_codes.html.erb +2 -2
- data/app/views/rodauth/change_login.html.erb +2 -2
- data/app/views/rodauth/create_account.html.erb +4 -4
- data/app/views/rodauth/reset_password_request.html.erb +1 -1
- data/app/views/rodauth/verify_account_resend.html.erb +1 -1
- data/app/views/rodauth/webauthn_remove.html.erb +1 -1
- data/config/brakeman.ignore +4 -4
- data/config/initializers/rabl.rb +0 -40
- data/config/locales/en/api_client.yml +22 -0
- data/config/locales/en/async.yml +34 -0
- data/config/locales/en/dashboard.yml +25 -0
- data/config/locales/en/invites.yml +37 -0
- data/config/locales/en/js.yml +40 -0
- data/config/locales/en/kanban.yml +28 -0
- data/config/locales/en/plutonium.yml +26 -0
- data/config/locales/en/positioning.yml +14 -0
- data/config/locales/en/profile.yml +29 -0
- data/config/locales/en/resource.yml +60 -0
- data/config/locales/en/ui.yml +93 -0
- data/config/locales/en/wizard.yml +46 -0
- data/docs/.vitepress/config.ts +34 -1
- data/docs/.vitepress/theme/components/HomeHero.vue +1 -1
- data/docs/.vitepress/theme/components/HomeInTheBox.vue +14 -0
- data/docs/.vitepress/theme/custom.css +34 -377
- data/docs/blog/introducing-plutonium-dashboards.md +175 -0
- data/docs/blog/introducing-plutonium-i18n.md +91 -0
- data/docs/blog/introducing-plutonium.md +4 -5
- data/docs/blog/whats-new-async-kanban-wizards.md +1 -1
- data/docs/guides/dashboards.md +300 -0
- data/docs/guides/index.md +1 -0
- data/docs/guides/kanban.md +2 -2
- data/docs/public/images/blog/dashboards-card-error.png +0 -0
- data/docs/public/images/blog/dashboards-loading.png +0 -0
- data/docs/public/images/blog/dashboards-overview.png +0 -0
- data/docs/public/images/blog/dashboards-sidebar.png +0 -0
- data/docs/public/images/blog/i18n-es-admin.png +0 -0
- data/docs/public/images/guides/dashboard-card-error.png +0 -0
- data/docs/public/images/guides/dashboard-loading.png +0 -0
- data/docs/public/images/guides/dashboard-overview.png +0 -0
- data/docs/public/images/guides/dashboard-sidebar.png +0 -0
- data/docs/reference/app/generators.md +2 -2
- data/docs/reference/app/portals.md +2 -0
- data/docs/reference/configuration.md +84 -27
- data/docs/reference/dashboard/dsl.md +123 -0
- data/docs/reference/dashboard/index.md +35 -0
- data/docs/reference/dashboard/registration.md +76 -0
- data/docs/reference/i18n.md +239 -0
- data/docs/reference/index.md +10 -1
- data/docs/reference/kanban/dsl.md +1 -1
- data/docs/reference/kanban/positioning.md +2 -2
- data/docs/reference/resource/actions.md +1 -1
- data/docs/reference/resource/definition.md +96 -18
- data/docs/reference/resource/export.md +5 -1
- data/docs/reference/resource/index.md +2 -0
- data/docs/reference/{positioning.md → resource/positioning.md} +2 -1
- data/docs/reference/resource/query.md +2 -0
- data/docs/reference/ui/assets.md +8 -0
- data/docs/reference/ui/displays.md +5 -8
- data/docs/reference/ui/index.md +1 -1
- data/docs/reference/wizard/dsl.md +2 -2
- data/esbuild.config.js +5 -2
- data/gemfiles/rails_7.gemfile.lock +2 -2
- data/gemfiles/rails_8.1.gemfile.lock +1 -1
- data/lib/active_model/validations/array_validator.rb +1 -1
- data/lib/active_model/validations/attached_validator.rb +1 -1
- data/lib/active_model/validations/url_validator.rb +2 -2
- data/lib/generators/pu/core/assets/assets_generator.rb +36 -21
- data/lib/generators/pu/core/assets/templates/postcss.config.js +1 -1
- data/lib/generators/pu/core/update/update_generator.rb +3 -2
- data/lib/generators/pu/dashboard/dashboard_generator.rb +96 -0
- data/lib/generators/pu/dashboard/templates/dashboard.rb.tt +25 -0
- data/lib/generators/pu/docker/install/install_generator.rb +58 -0
- data/lib/generators/pu/docker/install/templates/Dockerfile.dev.tt +18 -3
- data/lib/generators/pu/docker/install/templates/Dockerfile.tt +20 -5
- data/lib/generators/pu/docker/install/templates/{docker-compose.yml → docker-compose.yml.tt} +1 -1
- data/lib/generators/pu/gem/dotenv/dotenv_generator.rb +1 -1
- data/lib/generators/pu/invites/invitable_generator.rb +18 -1
- data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +0 -95
- data/lib/generators/pu/lib/plutonium_generators/concerns/js_package_manager.rb +146 -0
- data/lib/generators/pu/lib/plutonium_generators/generator.rb +1 -17
- data/lib/generators/pu/pkg/package/package_generator.rb +1 -0
- data/lib/generators/pu/pkg/package/templates/config/locales/en.yml.tt +28 -0
- data/lib/generators/pu/pkg/portal/portal_generator.rb +1 -0
- data/lib/generators/pu/pkg/portal/templates/config/locales/en.yml.tt +30 -0
- data/lib/generators/pu/res/scaffold/scaffold_generator.rb +0 -8
- data/lib/generators/pu/rodauth/concerns/feature_selector.rb +0 -18
- data/lib/plutonium/action/base.rb +27 -3
- data/lib/plutonium/action/interactive.rb +17 -4
- data/lib/plutonium/api_client/concerns/create_api_client.rb +12 -11
- data/lib/plutonium/api_client/concerns/disable_api_client.rb +3 -3
- data/lib/plutonium/auth/sequel_adapter.rb +1 -1
- data/lib/plutonium/configuration.rb +5 -0
- data/lib/plutonium/core/controller.rb +25 -1
- data/lib/plutonium/core/controllers/authorizable.rb +1 -1
- data/lib/plutonium/dashboard/base.rb +114 -0
- data/lib/plutonium/dashboard/card.rb +200 -0
- data/lib/plutonium/dashboard/controller.rb +101 -0
- data/lib/plutonium/dashboard/dsl.rb +104 -0
- data/lib/plutonium/dashboard/register.rb +40 -0
- data/lib/plutonium/dashboard/route_resolution.rb +30 -0
- data/lib/plutonium/dashboard.rb +42 -0
- data/lib/plutonium/definition/actions.rb +6 -1
- data/lib/plutonium/definition/base.rb +41 -3
- data/lib/plutonium/definition/index_views.rb +1 -1
- data/lib/plutonium/definition/positioning.rb +1 -1
- data/lib/plutonium/definition/presentable.rb +2 -2
- data/lib/plutonium/helpers/assets_helper.rb +15 -2
- data/lib/plutonium/helpers/display_helper.rb +10 -2
- data/lib/plutonium/interaction/async/context.rb +6 -8
- data/lib/plutonium/interaction/async/executor.rb +6 -7
- data/lib/plutonium/interaction/async/run.rb +2 -2
- data/lib/plutonium/interaction/async/run_definition.rb +1 -1
- data/lib/plutonium/interaction/base.rb +2 -0
- data/lib/plutonium/interaction/concerns/dispatchable.rb +15 -3
- data/lib/plutonium/invites/concerns/cancel_invite.rb +3 -3
- data/lib/plutonium/invites/concerns/invite_token.rb +25 -7
- data/lib/plutonium/invites/concerns/invite_user.rb +10 -5
- data/lib/plutonium/invites/concerns/resend_invite.rb +4 -4
- data/lib/plutonium/invites/controller.rb +11 -11
- data/lib/plutonium/kanban/column.rb +13 -3
- data/lib/plutonium/kanban/dsl.rb +7 -4
- data/lib/plutonium/models/has_cents.rb +1 -1
- data/lib/plutonium/profile/security_section.rb +10 -18
- data/lib/plutonium/query/filter.rb +10 -1
- data/lib/plutonium/query/filters/association.rb +27 -2
- data/lib/plutonium/query/filters/boolean.rb +4 -4
- data/lib/plutonium/query/filters/date.rb +4 -17
- data/lib/plutonium/query/filters/date_range.rb +2 -2
- data/lib/plutonium/query/filters/select.rb +10 -1
- data/lib/plutonium/query/filters/text.rb +9 -12
- data/lib/plutonium/railtie.rb +17 -0
- data/lib/plutonium/resource/controller.rb +10 -5
- data/lib/plutonium/resource/controllers/crud_actions.rb +17 -20
- data/lib/plutonium/resource/controllers/export_csv.rb +19 -9
- data/lib/plutonium/resource/controllers/kanban_actions.rb +16 -16
- data/lib/plutonium/resource/controllers/position_actions.rb +5 -5
- data/lib/plutonium/resource/controllers/typeahead.rb +24 -1
- data/lib/plutonium/resource/query_object.rb +30 -3
- data/lib/plutonium/routing/dashboard_registration.rb +137 -0
- data/lib/plutonium/routing/resource_registration.rb +5 -0
- data/lib/plutonium/routing/route_set_extensions.rb +2 -1
- data/lib/plutonium/translation.rb +227 -0
- data/lib/plutonium/ui/actions_dropdown.rb +2 -2
- data/lib/plutonium/ui/breadcrumbs.rb +3 -3
- data/lib/plutonium/ui/color_mode_selector.rb +1 -1
- data/lib/plutonium/ui/component/behaviour.rb +46 -0
- data/lib/plutonium/ui/component/methods.rb +4 -0
- data/lib/plutonium/ui/dashboard/board.rb +80 -0
- data/lib/plutonium/ui/dashboard/card.rb +127 -0
- data/lib/plutonium/ui/dashboard/chart.rb +43 -0
- data/lib/plutonium/ui/dashboard/custom.rb +32 -0
- data/lib/plutonium/ui/dashboard/frame.rb +25 -0
- data/lib/plutonium/ui/dashboard/metric.rb +137 -0
- data/lib/plutonium/ui/dashboard/skeleton.rb +40 -0
- data/lib/plutonium/ui/display/components/attachment.rb +1 -1
- data/lib/plutonium/ui/display/components/badge.rb +7 -1
- data/lib/plutonium/ui/display/components/boolean.rb +2 -2
- data/lib/plutonium/ui/display/resource.rb +23 -16
- data/lib/plutonium/ui/export_button.rb +3 -3
- data/lib/plutonium/ui/form/components/key_value_store.rb +5 -5
- data/lib/plutonium/ui/form/components/password.rb +2 -2
- data/lib/plutonium/ui/form/components/resource_select.rb +27 -0
- data/lib/plutonium/ui/form/components/secure_association.rb +2 -0
- data/lib/plutonium/ui/form/components/uppy.rb +11 -3
- data/lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb +1 -1
- data/lib/plutonium/ui/form/concerns/renders_repeater_row_controls.rb +5 -4
- data/lib/plutonium/ui/form/concerns/renders_structured_inputs.rb +1 -1
- data/lib/plutonium/ui/form/query.rb +8 -8
- data/lib/plutonium/ui/form/resource.rb +54 -26
- data/lib/plutonium/ui/frame_navigator_panel.rb +4 -4
- data/lib/plutonium/ui/grid/card.rb +3 -3
- data/lib/plutonium/ui/grid/resource.rb +3 -3
- data/lib/plutonium/ui/interaction/async/run_progress.rb +6 -6
- data/lib/plutonium/ui/interaction/async/running_banner.rb +2 -2
- data/lib/plutonium/ui/kanban/column.rb +6 -6
- data/lib/plutonium/ui/layout/base.rb +19 -1
- data/lib/plutonium/ui/layout/header.rb +1 -1
- data/lib/plutonium/ui/layout/icon_rail.rb +4 -4
- data/lib/plutonium/ui/layout/rodauth_layout.rb +1 -1
- data/lib/plutonium/ui/layout/sidebar.rb +1 -1
- data/lib/plutonium/ui/layout/topbar.rb +2 -2
- data/lib/plutonium/ui/modal/base.rb +4 -4
- data/lib/plutonium/ui/nav_grid_menu.rb +1 -1
- data/lib/plutonium/ui/nav_user.rb +2 -2
- data/lib/plutonium/ui/page/base.rb +10 -2
- data/lib/plutonium/ui/page/dashboard.rb +54 -0
- data/lib/plutonium/ui/page/edit.rb +3 -3
- data/lib/plutonium/ui/page/interactive_action.rb +2 -2
- data/lib/plutonium/ui/page/new.rb +3 -3
- data/lib/plutonium/ui/page/show.rb +2 -2
- data/lib/plutonium/ui/page/wizard.rb +22 -16
- data/lib/plutonium/ui/page/wizard_chooser.rb +10 -10
- data/lib/plutonium/ui/page/wizard_completed.rb +2 -2
- data/lib/plutonium/ui/skeleton_table.rb +1 -1
- data/lib/plutonium/ui/table/base.rb +2 -2
- data/lib/plutonium/ui/table/components/attachment.rb +1 -1
- data/lib/plutonium/ui/table/components/bulk_actions_toolbar.rb +19 -3
- data/lib/plutonium/ui/table/components/drag_handle.rb +3 -3
- data/lib/plutonium/ui/table/components/filter_form.rb +11 -10
- data/lib/plutonium/ui/table/components/filter_pills.rb +4 -4
- data/lib/plutonium/ui/table/components/pagy_info.rb +61 -17
- data/lib/plutonium/ui/table/components/pagy_pagination.rb +9 -5
- data/lib/plutonium/ui/table/components/row_actions_dropdown.rb +1 -1
- data/lib/plutonium/ui/table/components/scopes_bar.rb +6 -2
- data/lib/plutonium/ui/table/components/scopes_pills.rb +3 -3
- data/lib/plutonium/ui/table/components/selection_column.rb +1 -1
- data/lib/plutonium/ui/table/components/toolbar.rb +2 -2
- data/lib/plutonium/ui/table/components/view_switcher.rb +12 -8
- data/lib/plutonium/ui/table/resource.rb +53 -17
- data/lib/plutonium/ui/wizard/review.rb +7 -7
- data/lib/plutonium/ui/wizard/stepper.rb +1 -1
- data/lib/plutonium/ui/wizard/summary_display.rb +6 -0
- data/lib/plutonium/version.rb +1 -1
- data/lib/plutonium/wizard/base.rb +2 -0
- data/lib/plutonium/wizard/driving.rb +8 -2
- data/lib/plutonium/wizard/dsl.rb +4 -2
- data/lib/plutonium/wizard/field_importer.rb +1 -8
- data/lib/plutonium/wizard/review_step.rb +8 -1
- data/lib/plutonium/wizard/step.rb +14 -3
- data/package.json +4 -2
- data/plutonium.gemspec +4 -1
- data/postcss-gem-import.cjs +34 -0
- data/postcss-gem-import.js +3 -28
- data/src/css/components.css +91 -0
- data/src/css/slim_select.css +0 -38
- data/src/css/tokens.css +24 -0
- data/src/js/controllers/attachment_input_controller.js +15 -6
- data/src/js/controllers/chart_controller.js +162 -0
- data/src/js/controllers/clipboard_controller.js +3 -2
- data/src/js/controllers/dirty_form_guard_controller.js +4 -3
- data/src/js/controllers/easymde_controller.js +3 -0
- data/src/js/controllers/filter_panel_controller.js +15 -6
- data/src/js/controllers/flatpickr_controller.js +8 -0
- data/src/js/controllers/frame_refresh_controller.js +59 -0
- data/src/js/controllers/intl_tel_input_controller.js +5 -0
- data/src/js/controllers/register_controllers.js +4 -0
- data/src/js/controllers/slim_select_controller.js +79 -176
- data/src/js/core.js +9 -1
- data/src/js/i18n.js +69 -0
- data/src/js/plutonium-charts.js +15 -0
- data/src/js/turbo/turbo_confirm.js +5 -3
- metadata +64 -16
- data/.yarnrc.yml +0 -8
- data/config/initializers/hotwire_turbo_monkey_patches.rb +0 -12
- data/lib/generators/pu/field/input/input_generator.rb +0 -32
- data/lib/generators/pu/field/input/templates/input.rb.tt +0 -15
- data/lib/generators/pu/lib/plutonium_generators/concerns/config.rb +0 -39
- data/lib/generators/pu/lib/plutonium_generators/concerns/serializer.rb +0 -39
- data/lib/generators/pu/lib/plutonium_generators/installer.rb +0 -205
- data/lib/generators/pu/res/scaffold/templates/presenter.rb.tt +0 -13
- data/lib/generators/pu/res/scaffold/templates/query_object.rb.tt +0 -17
- data/public/plutonium-assets/application.js +0 -31419
- data/public/plutonium-assets/plutonium.ico +0 -0
- data/yarn.lock +0 -6858
data/CLAUDE.md
CHANGED
|
@@ -2,206 +2,305 @@
|
|
|
2
2
|
|
|
3
3
|
This guide helps AI assistants contribute to the Plutonium framework itself.
|
|
4
4
|
|
|
5
|
+
> Personal, cross-project rules live in `~/.claude/CLAUDE.md` (never stage/commit
|
|
6
|
+
> unless asked, `rails runner` over console, `Rails.logger.warn { ... }` block
|
|
7
|
+
> syntax, `with_connection` over `connection`, inline migration indexes, Stimulus
|
|
8
|
+
> for interactivity and always register controllers, no defensive `respond_to?`
|
|
9
|
+
> checks, no Claude attribution in commits/PRs). Those are not repeated here.
|
|
10
|
+
> This file carries only what is specific to this repo.
|
|
11
|
+
|
|
5
12
|
## Project Overview
|
|
6
13
|
|
|
7
|
-
Plutonium is a Rails RAD framework distributed as a Ruby gem. It provides
|
|
14
|
+
Plutonium is a Rails RAD framework distributed as a Ruby gem. It provides
|
|
15
|
+
resource-oriented architecture with automatic CRUD, policies, definitions, and
|
|
16
|
+
multi-tenancy. Assets (JS/CSS) ship inside the gem and are also published to npm.
|
|
8
17
|
|
|
9
18
|
## Repository Structure
|
|
10
19
|
|
|
11
20
|
```
|
|
12
21
|
plutonium-core/
|
|
13
|
-
├── lib/
|
|
14
|
-
│ ├──
|
|
15
|
-
│ ├──
|
|
16
|
-
│ ├──
|
|
17
|
-
│ ├──
|
|
18
|
-
│ ├──
|
|
19
|
-
│ ├──
|
|
20
|
-
│ ├──
|
|
21
|
-
│ ├──
|
|
22
|
-
│
|
|
23
|
-
├──
|
|
24
|
-
│ ├──
|
|
25
|
-
│ ├──
|
|
26
|
-
│
|
|
27
|
-
├──
|
|
28
|
-
│ ├──
|
|
29
|
-
│
|
|
30
|
-
│ └──
|
|
31
|
-
├──
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
│
|
|
35
|
-
|
|
22
|
+
├── lib/
|
|
23
|
+
│ ├── plutonium/ # Core framework code
|
|
24
|
+
│ │ ├── resource/ # Record, Policy, Definition, Controller, Interaction
|
|
25
|
+
│ │ ├── portal/ # Portal engines and multi-tenancy
|
|
26
|
+
│ │ ├── package/ # Feature-package engine mixin
|
|
27
|
+
│ │ ├── interaction/ # Business logic encapsulation
|
|
28
|
+
│ │ ├── dashboard/ # Dashboard DSL (metric / chart / card)
|
|
29
|
+
│ │ ├── kanban/ # Kanban board support
|
|
30
|
+
│ │ ├── wizard/ # Multi-step wizard DSL
|
|
31
|
+
│ │ ├── invites/ # Invitation model + token lifecycle
|
|
32
|
+
│ │ ├── rodauth/ # Rodauth integration
|
|
33
|
+
│ │ ├── positioning/ # Drag-to-reorder
|
|
34
|
+
│ │ ├── ui/ # Phlex view components
|
|
35
|
+
│ │ ├── definition/ # Definition DSL and field types
|
|
36
|
+
│ │ ├── query/ # Search, filters, scopes
|
|
37
|
+
│ │ ├── routing/ # Route helpers and extensions
|
|
38
|
+
│ │ └── testing/ # Test helpers shipped to consumers
|
|
39
|
+
│ └── generators/pu/ # Rails generators (pu:*), NOT under lib/plutonium/
|
|
40
|
+
├── app/ # Rails app components (assets, controllers, Phlex views)
|
|
41
|
+
├── src/ # JS/CSS sources (built into app/assets and src/build)
|
|
42
|
+
├── test/
|
|
43
|
+
│ ├── plutonium/ # Unit tests mirroring lib/
|
|
44
|
+
│ ├── integration/ # Request/integration tests
|
|
45
|
+
│ ├── generators/ # Generator tests
|
|
46
|
+
│ ├── system/ # System/browser tests
|
|
47
|
+
│ ├── support/ # Test helpers (generator_test_helper.rb, etc.)
|
|
48
|
+
│ └── dummy/ # Test Rails app (see below)
|
|
49
|
+
├── docs/ # VitePress docs; docs/public/skills/ is generated
|
|
50
|
+
├── .claude/skills/ # AI assistant skills (source of the shipped skills)
|
|
51
|
+
└── lib/tasks/release.rake # Release automation
|
|
36
52
|
```
|
|
37
53
|
|
|
54
|
+
Generators live at `lib/generators/pu/<category>/`. Categories on disk:
|
|
55
|
+
`async_interactions, core, dashboard, docker, eject, extra, field, gem, gen,
|
|
56
|
+
invites, lib, lite, pkg, profile, res, rodauth, saas, service, skills, test,
|
|
57
|
+
wizards`. There is **no** `pu:res:interaction` generator: interactions are
|
|
58
|
+
hand-authored.
|
|
59
|
+
|
|
38
60
|
## Key Abstractions
|
|
39
61
|
|
|
40
62
|
### Resource System
|
|
41
|
-
- `Plutonium::Resource::Record
|
|
42
|
-
- `Plutonium::Resource::Policy
|
|
43
|
-
- `Plutonium::Resource::Definition
|
|
44
|
-
- `Plutonium::Resource::Controller
|
|
45
|
-
- `Plutonium::Resource::Interaction
|
|
46
|
-
|
|
47
|
-
### Portal System
|
|
48
|
-
- `Plutonium::Portal::Engine
|
|
49
|
-
- `Plutonium::Package::Engine
|
|
63
|
+
- `Plutonium::Resource::Record`: model mixin with `associated_with` scopes
|
|
64
|
+
- `Plutonium::Resource::Policy`: ActionPolicy-based authorization
|
|
65
|
+
- `Plutonium::Resource::Definition`: declarative UI configuration
|
|
66
|
+
- `Plutonium::Resource::Controller`: CRUD controller mixin
|
|
67
|
+
- `Plutonium::Resource::Interaction`: business logic encapsulation
|
|
68
|
+
|
|
69
|
+
### Portal / Package System
|
|
70
|
+
- `Plutonium::Portal::Engine`: Rails engine mixin for web interfaces
|
|
71
|
+
- `Plutonium::Package::Engine`: Rails engine mixin for feature packages
|
|
50
72
|
- Entity scoping for multi-tenancy (path or custom strategy)
|
|
51
73
|
|
|
52
74
|
### UI Components
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
- `form/` - Form builders and field components
|
|
56
|
-
- `table/` - Data tables with sorting, pagination
|
|
57
|
-
- `display/` - Field display components
|
|
75
|
+
Phlex-based, under `lib/plutonium/ui/`: `page/`, `form/`, `table/`, `display/`,
|
|
76
|
+
`dashboard/`.
|
|
58
77
|
|
|
59
|
-
##
|
|
78
|
+
## The Dummy Test App (`test/dummy`)
|
|
79
|
+
|
|
80
|
+
This is where you exercise the framework end to end. Its packages are:
|
|
81
|
+
`admin_portal, blogging, catalog, locus_portal, org_portal, storefront_portal`.
|
|
82
|
+
Packages are auto-loaded by `config/packages.rb` globbing `packages/**/lib/engine.rb`.
|
|
83
|
+
|
|
84
|
+
### Always use generators here
|
|
85
|
+
|
|
86
|
+
Use the `pu:*` generators (`pu:res:scaffold`, `pu:res:conn`, `pu:pkg:portal`,
|
|
87
|
+
`pu:pkg:package`, etc.) to create dummy-app files, then customize the output.
|
|
88
|
+
**Why:** generators produce the correct file structure, inheritance, and marker
|
|
89
|
+
comments. Manual creation drifts into wrong patterns (making `ResourcePolicy` a
|
|
90
|
+
class instead of a module, missing the portal-module `include`, wrong inheritance
|
|
91
|
+
chain), and running the generator also validates that it works. Use `--policy`
|
|
92
|
+
and `--definition` on `pu:res:conn` when portal-specific overrides are needed.
|
|
93
|
+
|
|
94
|
+
### Databases
|
|
60
95
|
|
|
61
|
-
|
|
96
|
+
Every environment is SQLite (`test/dummy/config/database.yml`):
|
|
97
|
+
- `development` → `storage/development.sqlite3`
|
|
98
|
+
- `test` → `storage/test.sqlite3`
|
|
99
|
+
- `production` → multi-database: `primary`, `cache`, `queue`, `cable`
|
|
62
100
|
|
|
63
|
-
|
|
101
|
+
The **test** database is dropped, recreated, and migrated on every test boot
|
|
102
|
+
(`test/test_helper.rb`), so it has the current schema but zero rows.
|
|
103
|
+
|
|
104
|
+
### Running rails commands against the dummy
|
|
105
|
+
|
|
106
|
+
The dummy's own `Gemfile` is not installed; run everything through an appraisal
|
|
107
|
+
gemfile, from `test/dummy`:
|
|
64
108
|
|
|
65
109
|
```bash
|
|
66
|
-
|
|
110
|
+
cd test/dummy && env -u BUNDLE_GEMFILE \
|
|
111
|
+
BUNDLE_GEMFILE="$(cd ../.. && pwd)/gemfiles/rails_8.1.gemfile" \
|
|
112
|
+
bundle exec rails runner '<code>'
|
|
67
113
|
```
|
|
68
114
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
115
|
+
To seed data a running server will see, use a plain runner (do **not**
|
|
116
|
+
`require "test_helper"`, which drops the DB). Create a `User`
|
|
117
|
+
(`status = 2`, `password_hash = BCrypt::Password.create("password123")`), an
|
|
118
|
+
`Organization`, an `OrganizationUser` membership, then your records.
|
|
73
119
|
|
|
74
|
-
###
|
|
120
|
+
### Logins (seeds in `db/seeds.rb`)
|
|
75
121
|
|
|
76
|
-
|
|
122
|
+
Both logins are email-first (submit email, the same page reveals the password):
|
|
123
|
+
- User portal: `/users/login`, e.g. `alice@acme.com` / `password123`
|
|
124
|
+
- Admin portal: `/admins/login`, `admin@plutonium.dev` / `password123`. After
|
|
125
|
+
admin login, `/` redirects to `/users/login` (home wants a user session), so
|
|
126
|
+
navigate straight to `/admin/<resource>`.
|
|
77
127
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
128
|
+
Confirm which env/DB a running server is bound to before trusting it:
|
|
129
|
+
`lsof -p <pid> | grep sqlite3`, or watch which `*.sqlite3` mtime changes after an
|
|
130
|
+
action. A test-env server on an emptied `test.sqlite3` shows "no matching login"
|
|
131
|
+
because the users table is empty, not because credentials are wrong. Seeding the
|
|
132
|
+
wrong DB fails silently.
|
|
81
133
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
134
|
+
## Development Workflow
|
|
135
|
+
|
|
136
|
+
### Environment: `PLUTONIUM_DEV=1`
|
|
137
|
+
|
|
138
|
+
Set `PLUTONIUM_DEV=1` when working on the framework. It serves local assets,
|
|
139
|
+
hot-reloads components, and shows detailed errors.
|
|
85
140
|
|
|
86
|
-
-
|
|
87
|
-
|
|
141
|
+
Asset-source gotcha: `PLUTONIUM_DEV=1` reads `src/build/*.manifest`, which is the
|
|
142
|
+
output of `yarn dev`. If you only ran `yarn build` (which writes `app/assets/`),
|
|
143
|
+
a `PLUTONIUM_DEV=1` server 500s with a missing `src/build/css.manifest`. To serve
|
|
144
|
+
the packaged `app/assets/`, boot **without** `PLUTONIUM_DEV`. A stale
|
|
145
|
+
`src/build/css.manifest` can point at an old bundle, so when verifying anything
|
|
146
|
+
visual, grep the served CSS for the rule you expect rather than trusting the
|
|
147
|
+
manifest's presence.
|
|
88
148
|
|
|
89
|
-
|
|
149
|
+
Hot reload covers `lib/plutonium/**` and `packages/**` in **development** only
|
|
150
|
+
(the Listen-based `Plutonium::Reloader`). The **test**-env server does not reload,
|
|
151
|
+
so `lib/` edits there need a restart.
|
|
90
152
|
|
|
91
|
-
###
|
|
153
|
+
### Building assets
|
|
92
154
|
|
|
93
|
-
|
|
155
|
+
There is no `Procfile.dev`. Frontend work runs through yarn:
|
|
94
156
|
|
|
95
157
|
```bash
|
|
96
|
-
#
|
|
97
|
-
|
|
158
|
+
yarn dev # concurrently: css:dev (chokidar → src/build) + js:dev (esbuild --dev)
|
|
159
|
+
yarn build # js:prod + css:prod → app/assets/ (release artifacts)
|
|
160
|
+
```
|
|
98
161
|
|
|
99
|
-
|
|
100
|
-
|
|
162
|
+
Keep `yarn dev` running in a terminal while editing JS/CSS in `src/`.
|
|
163
|
+
|
|
164
|
+
### Running tests
|
|
165
|
+
|
|
166
|
+
Tests use [Appraisal](https://github.com/thoughtbot/appraisal). Gemfiles:
|
|
167
|
+
`rails_7`, `rails_8.0`, `rails_8.1`, and `postgres` (some concurrency tests skip
|
|
168
|
+
unless run under PostgreSQL, e.g. invite token row locks).
|
|
101
169
|
|
|
102
|
-
|
|
170
|
+
```bash
|
|
171
|
+
bundle exec appraisal rake test # unit + integration, one rails version
|
|
172
|
+
bundle exec appraisal rails-8.1 rake test
|
|
103
173
|
bundle exec appraisal rails-8.1 ruby -Itest test/plutonium/resource/policy_test.rb
|
|
104
174
|
```
|
|
105
175
|
|
|
106
|
-
|
|
176
|
+
Task layout (`Rakefile`): `rake test` **excludes** `test/system` and
|
|
177
|
+
`test/generators`. Generator tests run via `rake test_generators`; system tests
|
|
178
|
+
via `rake test:system`; `rake test_all` runs `test` + `test_generators`.
|
|
179
|
+
|
|
180
|
+
**Schema/working-tree hazard: stage dummy edits before running generator tests.**
|
|
181
|
+
Generator tests call `git_restore_dummy_app` in `setup`
|
|
182
|
+
(`test/support/generator_test_helper.rb`), which runs `git checkout -- test/dummy`
|
|
183
|
+
**and** `git clean -fd test/dummy` before every test. `git checkout --` restores
|
|
184
|
+
from the index, so **staged** `test/dummy` changes survive but unstaged ones are
|
|
185
|
+
reverted; `git clean -fd` **permanently deletes** untracked files under
|
|
186
|
+
`test/dummy` with no recovery. Before `rake test_generators` / `rake test:system`,
|
|
187
|
+
`git add` any `test/dummy` changes the run depends on, and clear or stash
|
|
188
|
+
untracked scratch files there first.
|
|
107
189
|
|
|
108
|
-
###
|
|
190
|
+
### Worktrees
|
|
109
191
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
192
|
+
The long-running dummy servers run the main checkout (master). To exercise code
|
|
193
|
+
on a branch in a worktree, boot a fresh server from that worktree directory; a
|
|
194
|
+
server started elsewhere is running master's code, not yours.
|
|
113
195
|
|
|
114
|
-
###
|
|
196
|
+
### Docs
|
|
115
197
|
|
|
116
198
|
```bash
|
|
117
|
-
yarn docs:dev #
|
|
118
|
-
yarn docs:build #
|
|
199
|
+
yarn docs:dev # localhost:5173 (runs docs:sync-skills first)
|
|
200
|
+
yarn docs:build # must pass; dead internal links fail the build
|
|
119
201
|
```
|
|
120
202
|
|
|
121
|
-
## Code Conventions
|
|
203
|
+
## Code Conventions (project-specific)
|
|
122
204
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
-
|
|
126
|
-
|
|
127
|
-
|
|
205
|
+
Shared Ruby style lives in `~/.claude/CLAUDE.md`. Project-specific rules:
|
|
206
|
+
|
|
207
|
+
### Policies: gate per-record actions in the policy, not with `condition:`
|
|
208
|
+
|
|
209
|
+
For a record action whose availability depends on record state, gate it in the
|
|
210
|
+
**policy**, not with a `condition: -> { ... }` proc on the `action` declaration:
|
|
211
|
+
|
|
212
|
+
```ruby
|
|
213
|
+
def resend_invite?
|
|
214
|
+
record.unverified?
|
|
215
|
+
end
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
A record action's policy method always receives the **instance** (the table binds
|
|
219
|
+
`policy_for(record:)` to the row; the show page's `current_policy` is
|
|
220
|
+
instance-bound on member routes). So do **not** add a `record.is_a?(Model)` guard
|
|
221
|
+
on a record-action method. The class subject only appears on collection routes
|
|
222
|
+
(`index`/`new`), where it backs `read?`, `create?`/`new?`, `export_csv?`,
|
|
223
|
+
`search?`/`typeahead?`, and resource-action gates (kanban column actions are
|
|
224
|
+
auto-registered resource actions). Note `read?` gets the class via `index?` but
|
|
225
|
+
the instance via `show?`. Bulk actions are checked per record, not on the class.
|
|
226
|
+
|
|
227
|
+
**Why:** authorization belongs in the policy (single source of truth). A
|
|
228
|
+
`condition:` only hides UI; it does not deny a forced request, which splits the
|
|
229
|
+
guard. Keep a defense-in-depth re-check in the interaction's `execute` if desired.
|
|
128
230
|
|
|
129
231
|
### Generators
|
|
130
|
-
-
|
|
131
|
-
|
|
132
|
-
- Quote shell
|
|
133
|
-
|
|
134
|
-
### Migrations
|
|
135
|
-
- Inline indexes and constraints in `create_table` blocks
|
|
136
|
-
- Use `foreign_key: {on_delete: :cascade}` where appropriate
|
|
137
|
-
|
|
138
|
-
### Frontend
|
|
139
|
-
- Stimulus controllers for interactivity (always register controllers)
|
|
140
|
-
- TailwindCSS 4 for styling
|
|
141
|
-
- Phlex for view components
|
|
142
|
-
|
|
143
|
-
## Adding New Features
|
|
144
|
-
|
|
145
|
-
### New Field Type
|
|
146
|
-
1. Add renderer in `lib/plutonium/ui/display/components/`
|
|
147
|
-
2. Add input in `lib/plutonium/ui/form/components/`
|
|
148
|
-
3. Register in field type mappings
|
|
149
|
-
4. Add tests and documentation
|
|
150
|
-
|
|
151
|
-
### New Generator
|
|
152
|
-
1. Create in `lib/generators/pu/category/name_generator.rb`
|
|
153
|
-
2. Add templates in `lib/generators/pu/category/name/templates/`
|
|
154
|
-
3. Add tests in `test/generators/`
|
|
155
|
-
4. Document in `docs/reference/generators/`
|
|
232
|
+
- Provide `--dest=` to avoid interactive prompts: `--dest=main_app` for the main
|
|
233
|
+
app, `--dest=<package_name>` for a package.
|
|
234
|
+
- Quote shell args with special characters: `'field:type?'`, `'field:decimal{10,2}'`.
|
|
156
235
|
|
|
157
|
-
|
|
158
|
-
1. Add response class in `lib/plutonium/interaction/response/`
|
|
159
|
-
2. Add helper method in `Plutonium::Interaction::Outcome::Success`
|
|
160
|
-
3. Document usage
|
|
236
|
+
## Git, Commits, and Releases
|
|
161
237
|
|
|
162
|
-
|
|
238
|
+
### Conventional commits, including PR titles
|
|
163
239
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
240
|
+
Use conventional commits (`type(scope): subject`) for every commit **and** every
|
|
241
|
+
PR title. PRs are squash-merged, so the PR title lands on master verbatim with
|
|
242
|
+
`(#NN)` appended; a non-conventional title becomes a malformed commit.
|
|
167
243
|
|
|
168
|
-
|
|
244
|
+
**Why:** `git-cliff` generates the changelog and computes the next version from
|
|
245
|
+
the commit history. A prose title like "Wizard DSL Updates and Cleanups" is
|
|
246
|
+
invisible to the changelog and can throw off version inference.
|
|
247
|
+
|
|
248
|
+
Check `git log origin/master --oneline` for the scopes in use (`wizard`, `ui`,
|
|
249
|
+
`policy`, `positioning`, `generators`, `auth`, `inputs`, `test`, `chore`,
|
|
250
|
+
`dashboard`, `i18n`, ...) and match the format when writing a PR title.
|
|
251
|
+
|
|
252
|
+
### Release process
|
|
253
|
+
|
|
254
|
+
Publishing happens from a laptop. CI (`.github/workflows/release.yml`) does
|
|
255
|
+
**not** push to any registry: when the `vX.Y.Z` tag lands it only cuts the GitHub
|
|
256
|
+
Release (notes + built gem attached), so it cannot race the local publish.
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
bundle exec rake release:prepare # or release:prepare[1.2.3]; bump + changelog + assets, commit (no push)
|
|
260
|
+
git show # review the bump commit
|
|
261
|
+
bundle exec rake release:publish # push gem + npm, then tag + push → CI cuts the Release
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
`release:publish` is idempotent and resumable (skips a gem/npm already live, tags
|
|
265
|
+
only if missing). `rake release:version` shows the next version git-cliff would
|
|
266
|
+
pick. The bare `rake release` from bundler is neutralized on purpose; use the
|
|
267
|
+
`prepare` then `publish` flow.
|
|
169
268
|
|
|
170
|
-
|
|
171
|
-
docs is not enough; the docs have been wrong. Two real cases:
|
|
269
|
+
## Writing Rules (docs, blog posts, skills)
|
|
172
270
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
271
|
+
Docs and posts here are read by developers **and** by AI assistants (the
|
|
272
|
+
`.claude/skills/` are synced to `docs/public/skills/` and shipped). A wrong
|
|
273
|
+
example does not just mislead a reader; it gets reproduced as code.
|
|
274
|
+
|
|
275
|
+
### Verify every code sample
|
|
178
276
|
|
|
179
|
-
|
|
277
|
+
Never publish a snippet you have not checked against this codebase. The docs have
|
|
278
|
+
been wrong before (`as: :phlexi_tag` resolved to `phlexi_tag_tag` and raised;
|
|
279
|
+
working values are `:phlexi_render` and `:phlexi`). When a claim is mechanical,
|
|
280
|
+
prove it at runtime:
|
|
180
281
|
|
|
181
282
|
```bash
|
|
182
283
|
cd test/dummy && RAILS_ENV=test bin/rails runner 'puts SomeClass.instance_methods.grep(/x/)'
|
|
183
284
|
```
|
|
184
285
|
|
|
185
|
-
###
|
|
286
|
+
### Document behavior, not internals
|
|
186
287
|
|
|
187
|
-
-
|
|
188
|
-
is dead code
|
|
189
|
-
- **Definition = how a field renders. Policy = whether it appears.** "Only admins
|
|
190
|
-
this" is `permitted_attributes_for_*`, never a definition line or a
|
|
191
|
-
|
|
192
|
-
-
|
|
288
|
+
- Don't declare what is auto-detected. `field :title` matching the detected type
|
|
289
|
+
is dead code.
|
|
290
|
+
- **Definition = how a field renders. Policy = whether it appears.** "Only admins
|
|
291
|
+
see this" is `permitted_attributes_for_*`, never a definition line or a
|
|
292
|
+
`condition:`.
|
|
293
|
+
- Pages expose the record as `object`, not `resource`.
|
|
294
|
+
- Interactions: `attribute :resource` (no `class:`); rescue
|
|
193
295
|
`ActiveRecord::RecordInvalid` around any `create!`/`update!`/`save!`.
|
|
194
|
-
-
|
|
296
|
+
- Policies: never put `*_attributes` hashes in `permitted_attributes_for_*`.
|
|
195
297
|
|
|
196
|
-
### Voice
|
|
197
|
-
|
|
198
|
-
Match the reference docs: direct, concrete, no throat-clearing. Specifically avoid
|
|
199
|
-
the tells that make writing read as machine-generated:
|
|
298
|
+
### Voice: avoid the machine-generated tells
|
|
200
299
|
|
|
201
300
|
- **No em dashes.** Use commas, colons, parentheses, or two sentences.
|
|
202
|
-
- No "It's not X, it's Y" or "The pitch isn't
|
|
203
|
-
- No tidy-summary line after every example
|
|
204
|
-
- No stacked negation before the real claim
|
|
301
|
+
- No "It's not X, it's Y" or "The pitch isn't... The pitch is..." constructions.
|
|
302
|
+
- No tidy-summary line after every example.
|
|
303
|
+
- No stacked negation before the real claim; no "critically"; no "genuinely".
|
|
205
304
|
- Cut a sentence that restates the one before it.
|
|
206
305
|
|
|
207
306
|
Deliberate parallelism and a stated opinion are fine. The tell is *unearned*
|
|
@@ -209,71 +308,60 @@ rhetoric, not rhetoric itself.
|
|
|
209
308
|
|
|
210
309
|
### Blog mechanics
|
|
211
310
|
|
|
212
|
-
Posts are markdown in `docs/blog
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
title
|
|
217
|
-
|
|
218
|
-
date: 2026-08-19
|
|
219
|
-
description: One sentence. Becomes the RSS description and the index subtitle.
|
|
220
|
-
author: Stefan Froelich
|
|
221
|
-
tags: [announcement, rails]
|
|
222
|
-
draft: true # omit to publish
|
|
223
|
-
---
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
- Put `<BlogMeta />` directly under the `# ` heading; it renders date, author and
|
|
227
|
-
the back link from frontmatter.
|
|
228
|
-
- **A title must be legible with no context.** It travels via RSS, social and pasted
|
|
229
|
-
links, where the site name is absent, so include both "Plutonium" and "Rails".
|
|
230
|
-
"Plutonium" alone collides with the element and other projects.
|
|
231
|
-
- **The slug must match the title.** Renaming the title means renaming the file;
|
|
232
|
-
URLs are permanent once published.
|
|
233
|
-
- A post is distinguished from the section index by having a `date`. `draft: true`
|
|
234
|
-
hides it from the index and the feed.
|
|
235
|
-
- `yarn docs:build` must pass. Dead internal links fail the build.
|
|
311
|
+
Posts are markdown in `docs/blog/` with frontmatter (`title`, `titleTemplate`,
|
|
312
|
+
`date`, `description`, `author`, `tags`, optional `draft: true`). Put `<BlogMeta />`
|
|
313
|
+
under the `#` heading. A title must be legible cold (it travels via RSS and social
|
|
314
|
+
with no site name), so include both "Plutonium" and "Rails". The slug must match
|
|
315
|
+
the title; renaming the title means renaming the file. A post is distinguished
|
|
316
|
+
from a section index by having a `date`. `yarn docs:build` must pass.
|
|
236
317
|
|
|
237
318
|
### Screenshots
|
|
238
319
|
|
|
239
|
-
Match the existing ones
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
-
|
|
320
|
+
Match the existing ones: 2480px wide (1240 CSS at `deviceScaleFactor: 2`), light
|
|
321
|
+
mode, icon rail collapsed (`localStorage.theme = 'light'`,
|
|
322
|
+
`localStorage.pu_rail_pinned = 'false'`). Use realistic, distinct data (repeated
|
|
323
|
+
generated titles read as broken data). Crop dead space with `magick`. Store under
|
|
324
|
+
`docs/public/images/<section>/`.
|
|
325
|
+
|
|
326
|
+
## Settled Decisions (do not re-litigate)
|
|
327
|
+
|
|
328
|
+
- **SQLite for the dummy app** across all environments; production is multi-db
|
|
329
|
+
(primary/cache/queue/cable). Postgres is a test-only appraisal for concurrency
|
|
330
|
+
tests, not the app default.
|
|
331
|
+
- **Package + portal engine architecture** for multi-tenancy and features. New
|
|
332
|
+
features go in packages, wired through portal engines.
|
|
333
|
+
- **Generators-first**: every dummy-app artifact is generated, then customized.
|
|
334
|
+
- **The SaaS post-login flow ships as `pu:saas:welcome`** (welcome controller +
|
|
335
|
+
onboarding/entity-selection views + Rodauth redirects). This bridges
|
|
336
|
+
login → invite check → onboarding → entity selection → portal. It is built; do
|
|
337
|
+
not re-plan it.
|
|
248
338
|
|
|
249
339
|
## Skills System
|
|
250
340
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
## Release Process
|
|
341
|
+
`.claude/skills/` holds the AI-assistant skills. They are synced into
|
|
342
|
+
`docs/public/skills/` by `yarn docs:sync-skills` (run automatically by
|
|
343
|
+
`docs:dev` / `docs:build`) and shipped with the docs. Update the relevant skill
|
|
344
|
+
when you change related behavior; skills require a gem release to take effect for
|
|
345
|
+
consumers.
|
|
258
346
|
|
|
259
|
-
|
|
260
|
-
push to any registry — when the `vX.Y.Z` tag lands it only cuts the GitHub Release
|
|
261
|
-
(release notes + the built gem attached), so it can't race the local publish.
|
|
262
|
-
|
|
263
|
-
```bash
|
|
264
|
-
# 1. Bump version + regenerate changelog + rebuild assets, commit (no push).
|
|
265
|
-
# Version is optional — git-cliff computes it from conventional commits.
|
|
266
|
-
bundle exec rake release:prepare # or release:prepare[1.2.3]
|
|
347
|
+
## Adding Features
|
|
267
348
|
|
|
268
|
-
|
|
349
|
+
### New field type
|
|
350
|
+
1. Renderer in `lib/plutonium/ui/display/components/`
|
|
351
|
+
2. Input in `lib/plutonium/ui/form/components/`
|
|
352
|
+
3. Register in field-type mappings
|
|
353
|
+
4. Tests and docs
|
|
269
354
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
355
|
+
### New generator
|
|
356
|
+
1. Create in `lib/generators/pu/<category>/<name>_generator.rb`
|
|
357
|
+
2. Templates in `lib/generators/pu/<category>/<name>/templates/`
|
|
358
|
+
3. Tests in `test/generators/`
|
|
359
|
+
4. Document in `docs/reference/generators/`
|
|
275
360
|
|
|
276
|
-
|
|
361
|
+
### New interaction response
|
|
362
|
+
1. Response class in `lib/plutonium/interaction/response/`
|
|
363
|
+
2. Helper on `Plutonium::Interaction::Outcome::Success`
|
|
364
|
+
3. Document usage
|
|
277
365
|
|
|
278
366
|
## Key Files Reference
|
|
279
367
|
|
|
@@ -287,22 +375,6 @@ Helper: `rake release:version` shows the next version git-cliff would pick.
|
|
|
287
375
|
| Base definition | `lib/plutonium/resource/definition.rb` |
|
|
288
376
|
| Interactions | `lib/plutonium/interaction/base.rb` |
|
|
289
377
|
| Generators | `lib/generators/pu/` |
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
### Fix a bug in policies
|
|
294
|
-
1. Find the issue in `lib/plutonium/resource/policy.rb` or related
|
|
295
|
-
2. Write a failing test in `test/plutonium/resource/policy_test.rb`
|
|
296
|
-
3. Fix the bug
|
|
297
|
-
4. Update docs if behavior changed
|
|
298
|
-
|
|
299
|
-
### Add a generator option
|
|
300
|
-
1. Add option in generator class
|
|
301
|
-
2. Update templates to use option
|
|
302
|
-
3. Test with dummy app
|
|
303
|
-
4. Document in `docs/reference/generators/`
|
|
304
|
-
|
|
305
|
-
### Update documentation
|
|
306
|
-
1. Edit files in `docs/`
|
|
307
|
-
2. If user-facing behavior, update relevant skill in `.claude/skills/`
|
|
308
|
-
3. Run `yarn docs:build` to verify no broken links
|
|
378
|
+
| Release automation | `lib/tasks/release.rake` |
|
|
379
|
+
| Dummy DB config | `test/dummy/config/database.yml` |
|
|
380
|
+
| Generator test helper | `test/support/generator_test_helper.rb` |
|
data/SECURITY.md
CHANGED
|
@@ -12,7 +12,7 @@ please upgrade before reporting an issue to confirm it still reproduces.
|
|
|
12
12
|
|
|
13
13
|
| Version | Supported |
|
|
14
14
|
| ------- | ------------------ |
|
|
15
|
-
| Latest release (`0.
|
|
15
|
+
| Latest release (`0.65.x`) | :white_check_mark: |
|
|
16
16
|
| Older releases | :x: |
|
|
17
17
|
|
|
18
18
|
## Reporting a Vulnerability
|