plutonium 0.45.2 → 0.46.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 +146 -0
- data/.claude/skills/plutonium-assets/SKILL.md +248 -157
- data/.claude/skills/{plutonium-rodauth → plutonium-auth}/SKILL.md +195 -229
- data/.claude/skills/plutonium-controller/SKILL.md +9 -2
- data/.claude/skills/plutonium-create-resource/SKILL.md +22 -1
- data/.claude/skills/plutonium-definition/SKILL.md +521 -7
- data/.claude/skills/plutonium-entity-scoping/SKILL.md +317 -0
- data/.claude/skills/plutonium-forms/SKILL.md +8 -1
- data/.claude/skills/plutonium-installation/SKILL.md +25 -2
- data/.claude/skills/plutonium-interaction/SKILL.md +9 -2
- data/.claude/skills/plutonium-invites/SKILL.md +11 -7
- data/.claude/skills/plutonium-model/SKILL.md +50 -50
- data/.claude/skills/plutonium-nested-resources/SKILL.md +8 -1
- data/.claude/skills/plutonium-package/SKILL.md +8 -1
- data/.claude/skills/plutonium-policy/SKILL.md +69 -78
- data/.claude/skills/plutonium-portal/SKILL.md +26 -70
- data/.claude/skills/plutonium-views/SKILL.md +9 -2
- data/CHANGELOG.md +33 -0
- data/app/assets/plutonium.css +1 -1
- data/app/views/rodauth/_login_form.html.erb +0 -3
- data/app/views/rodauth/confirm_password.html.erb +0 -4
- data/app/views/rodauth/create_account.html.erb +0 -3
- data/app/views/rodauth/logout.html.erb +0 -3
- data/config/initializers/pagy.rb +1 -1
- data/docs/superpowers/plans/2026-04-08-plutonium-skills-overhaul.md +481 -0
- data/docs/superpowers/specs/2026-04-08-plutonium-skills-overhaul-design.md +236 -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/update/update_generator.rb +8 -0
- data/lib/generators/pu/gem/active_shrine/active_shrine_generator.rb +56 -0
- data/lib/generators/pu/invites/install_generator.rb +8 -1
- data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +43 -0
- data/lib/generators/pu/profile/concerns/profile_arguments.rb +10 -4
- data/lib/generators/pu/profile/conn_generator.rb +9 -12
- data/lib/generators/pu/profile/install_generator.rb +5 -2
- data/lib/generators/pu/rodauth/templates/app/rodauth/account_rodauth_plugin.rb.tt +3 -0
- data/lib/generators/pu/saas/portal_generator.rb +4 -9
- data/lib/generators/pu/saas/welcome/templates/app/views/welcome/onboarding.html.erb.tt +2 -2
- data/lib/plutonium/engine.rb +18 -5
- data/lib/plutonium/ui/layout/rodauth_layout.rb +6 -1
- data/lib/plutonium/version.rb +1 -1
- data/package.json +1 -1
- metadata +7 -8
- data/.claude/skills/plutonium/skill.md +0 -130
- data/.claude/skills/plutonium-definition-actions/SKILL.md +0 -424
- data/.claude/skills/plutonium-definition-query/SKILL.md +0 -364
- data/.claude/skills/plutonium-profile/SKILL.md +0 -276
- data/.claude/skills/plutonium-theming/SKILL.md +0 -424
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: plutonium
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.46.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stefan Froelich
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-04-
|
|
10
|
+
date: 2026-04-11 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: zeitwerk
|
|
@@ -400,11 +400,11 @@ extra_rdoc_files: []
|
|
|
400
400
|
files:
|
|
401
401
|
- "# Plutonium: The pre-alpha demo.md"
|
|
402
402
|
- ".claude/skills/plutonium-assets/SKILL.md"
|
|
403
|
+
- ".claude/skills/plutonium-auth/SKILL.md"
|
|
403
404
|
- ".claude/skills/plutonium-controller/SKILL.md"
|
|
404
405
|
- ".claude/skills/plutonium-create-resource/SKILL.md"
|
|
405
|
-
- ".claude/skills/plutonium-definition-actions/SKILL.md"
|
|
406
|
-
- ".claude/skills/plutonium-definition-query/SKILL.md"
|
|
407
406
|
- ".claude/skills/plutonium-definition/SKILL.md"
|
|
407
|
+
- ".claude/skills/plutonium-entity-scoping/SKILL.md"
|
|
408
408
|
- ".claude/skills/plutonium-forms/SKILL.md"
|
|
409
409
|
- ".claude/skills/plutonium-installation/SKILL.md"
|
|
410
410
|
- ".claude/skills/plutonium-interaction/SKILL.md"
|
|
@@ -414,11 +414,8 @@ files:
|
|
|
414
414
|
- ".claude/skills/plutonium-package/SKILL.md"
|
|
415
415
|
- ".claude/skills/plutonium-policy/SKILL.md"
|
|
416
416
|
- ".claude/skills/plutonium-portal/SKILL.md"
|
|
417
|
-
- ".claude/skills/plutonium-profile/SKILL.md"
|
|
418
|
-
- ".claude/skills/plutonium-rodauth/SKILL.md"
|
|
419
|
-
- ".claude/skills/plutonium-theming/SKILL.md"
|
|
420
417
|
- ".claude/skills/plutonium-views/SKILL.md"
|
|
421
|
-
- ".claude/skills/plutonium/
|
|
418
|
+
- ".claude/skills/plutonium/SKILL.md"
|
|
422
419
|
- ".cliff.toml"
|
|
423
420
|
- ".node-version"
|
|
424
421
|
- ".rspec"
|
|
@@ -579,6 +576,8 @@ files:
|
|
|
579
576
|
- docs/reference/portal/index.md
|
|
580
577
|
- docs/reference/views/forms.md
|
|
581
578
|
- docs/reference/views/index.md
|
|
579
|
+
- docs/superpowers/plans/2026-04-08-plutonium-skills-overhaul.md
|
|
580
|
+
- docs/superpowers/specs/2026-04-08-plutonium-skills-overhaul-design.md
|
|
582
581
|
- esbuild.config.js
|
|
583
582
|
- exe/pug
|
|
584
583
|
- gemfiles/rails_7.gemfile
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: plutonium
|
|
3
|
-
description: Use when starting work on a Plutonium app, unsure which skill to read, or need an overview of the resource architecture
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Plutonium Development Guide
|
|
7
|
-
|
|
8
|
-
Read this first when working on a Plutonium application.
|
|
9
|
-
|
|
10
|
-
## Core Rules
|
|
11
|
-
|
|
12
|
-
1. **Always use generators** - Never manually create resources, packages, or portals
|
|
13
|
-
2. **Check relevant skills first** - Each concept has a dedicated skill with details
|
|
14
|
-
3. **Definitions over controllers** - UI customization belongs in definitions, not controllers
|
|
15
|
-
4. **Policies for authorization** - All permission logic goes in policies
|
|
16
|
-
|
|
17
|
-
## Key Generators
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
rails g pu:res:scaffold Post title:string --dest=main_app # Create resource
|
|
21
|
-
rails g pu:res:conn Post --dest=admin_portal # Connect to portal
|
|
22
|
-
rails g pu:pkg:package blogging # Create feature package
|
|
23
|
-
rails g pu:pkg:portal admin # Create portal
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Always specify `--dest` to avoid interactive prompts.
|
|
27
|
-
|
|
28
|
-
## Resource Architecture
|
|
29
|
-
|
|
30
|
-
A **resource** is four layers working together for full CRUD with minimal code:
|
|
31
|
-
|
|
32
|
-
| Layer | File | Purpose | Customize when... |
|
|
33
|
-
|-------|------|---------|-------------------|
|
|
34
|
-
| **Model** | `app/models/post.rb` | Data, validations, associations | Adding business logic |
|
|
35
|
-
| **Definition** | `app/definitions/post_definition.rb` | UI - fields, actions, filters | Changing how things look/behave |
|
|
36
|
-
| **Policy** | `app/policies/post_policy.rb` | Authorization - who can do what | Restricting access |
|
|
37
|
-
| **Controller** | `app/controllers/posts_controller.rb` | Request handling | Rarely - use hooks if needed |
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
┌─────────────────────────────────────────────────────────────────┐
|
|
41
|
-
│ Resource │
|
|
42
|
-
├─────────────────────────────────────────────────────────────────┤
|
|
43
|
-
│ Model │ Definition │ Policy │ Controller│
|
|
44
|
-
│ (WHAT it is) │ (HOW it looks) │ (WHO can act) │ (HOW it │
|
|
45
|
-
│ │ │ │ responds) │
|
|
46
|
-
├─────────────────────────────────────────────────────────────────┤
|
|
47
|
-
│ - attributes │ - field types │ - permissions │ - CRUD │
|
|
48
|
-
│ - associations │ - inputs/forms │ - scoping │ - redirects│
|
|
49
|
-
│ - validations │ - displays │ - attributes │ - params │
|
|
50
|
-
│ - scopes │ - actions │ │ │
|
|
51
|
-
│ - callbacks │ - filters │ │ │
|
|
52
|
-
└─────────────────────────────────────────────────────────────────┘
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Auto-Detection
|
|
56
|
-
|
|
57
|
-
Plutonium automatically detects from your model:
|
|
58
|
-
- All database columns with appropriate field types
|
|
59
|
-
- Associations (belongs_to, has_one, has_many)
|
|
60
|
-
- Attachments (Active Storage)
|
|
61
|
-
- Enums
|
|
62
|
-
|
|
63
|
-
**You only need to declare when overriding defaults.**
|
|
64
|
-
|
|
65
|
-
## Creating Resources
|
|
66
|
-
|
|
67
|
-
### New Resources (from scratch)
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
rails g pu:res:scaffold Post user:belongs_to title:string 'content:text?' published:boolean --dest=main_app
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
See `plutonium-create-resource` skill for full generator options.
|
|
74
|
-
|
|
75
|
-
### From Existing Models
|
|
76
|
-
|
|
77
|
-
1. Include `Plutonium::Resource::Record` in your model (or inherit from a class that does)
|
|
78
|
-
2. Generate supporting files: `rails g pu:res:scaffold Post --no-migration`
|
|
79
|
-
3. Connect to a portal: `rails g pu:res:conn Post --dest=admin_portal`
|
|
80
|
-
|
|
81
|
-
## Connecting to Portals
|
|
82
|
-
|
|
83
|
-
Resources must be connected to a portal to be accessible:
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
rails g pu:res:conn Post --dest=admin_portal
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
See `plutonium-portal` skill for portal details.
|
|
90
|
-
|
|
91
|
-
## Portal-Specific Customization
|
|
92
|
-
|
|
93
|
-
Each portal can override the base definition, policy, and controller:
|
|
94
|
-
|
|
95
|
-
```ruby
|
|
96
|
-
# Admin portal sees more
|
|
97
|
-
class AdminPortal::PostDefinition < ::PostDefinition
|
|
98
|
-
scope :draft
|
|
99
|
-
scope :pending_review
|
|
100
|
-
action :feature, interaction: FeaturePostInteraction
|
|
101
|
-
end
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
## Workflow Summary
|
|
105
|
-
|
|
106
|
-
1. **Generate** - `rails g pu:res:scaffold Model attributes... --dest=main_app`
|
|
107
|
-
2. **Migrate** - `rails db:migrate`
|
|
108
|
-
3. **Connect** - `rails g pu:res:conn Model --dest=portal_name`
|
|
109
|
-
4. **Customize** - Edit definition/policy as needed
|
|
110
|
-
|
|
111
|
-
## Skill Reference
|
|
112
|
-
|
|
113
|
-
| Topic | Skill |
|
|
114
|
-
|-------|-------|
|
|
115
|
-
| Creating resources | `plutonium-create-resource` |
|
|
116
|
-
| Models & features | `plutonium-model` |
|
|
117
|
-
| Field configuration | `plutonium-definition` |
|
|
118
|
-
| Actions & interactions | `plutonium-definition-actions` |
|
|
119
|
-
| Search, filters, scopes | `plutonium-definition-query` |
|
|
120
|
-
| Authorization | `plutonium-policy` |
|
|
121
|
-
| Custom views | `plutonium-views` |
|
|
122
|
-
| Custom forms | `plutonium-forms` |
|
|
123
|
-
| Nested resources | `plutonium-nested-resources` |
|
|
124
|
-
| Packages & portals | `plutonium-package`, `plutonium-portal` |
|
|
125
|
-
| Authentication | `plutonium-rodauth` |
|
|
126
|
-
| Interactions | `plutonium-interaction` |
|
|
127
|
-
| Theming & assets | `plutonium-theming`, `plutonium-assets` |
|
|
128
|
-
| User profile | `plutonium-profile` |
|
|
129
|
-
| User invites | `plutonium-invites` |
|
|
130
|
-
| Installation | `plutonium-installation` |
|
|
@@ -1,424 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: plutonium-definition-actions
|
|
3
|
-
description: Use when adding custom actions, bulk operations, or wiring interactions into Plutonium resource definitions
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Definition Actions
|
|
7
|
-
|
|
8
|
-
Actions define custom operations that can be performed on resources. They can be simple (navigation) or interactive (with business logic via Interactions).
|
|
9
|
-
|
|
10
|
-
## Action Types
|
|
11
|
-
|
|
12
|
-
| Type | Shows In | Use Case |
|
|
13
|
-
|------|----------|----------|
|
|
14
|
-
| `resource_action` | Index page | Import, Export, Create |
|
|
15
|
-
| `record_action` | Show page | Edit, Delete, Archive |
|
|
16
|
-
| `collection_record_action` | Table rows | Quick actions per row |
|
|
17
|
-
| `bulk_action` | Selected records | Bulk operations |
|
|
18
|
-
|
|
19
|
-
## Simple Actions (Navigation)
|
|
20
|
-
|
|
21
|
-
Simple actions link to existing routes. **The target route must already exist** - these don't create new functionality, just navigation links.
|
|
22
|
-
|
|
23
|
-
```ruby
|
|
24
|
-
class PostDefinition < ResourceDefinition
|
|
25
|
-
# Link to external URL
|
|
26
|
-
action :documentation,
|
|
27
|
-
label: "Documentation",
|
|
28
|
-
route_options: {url: "https://docs.example.com"},
|
|
29
|
-
icon: Phlex::TablerIcons::Book,
|
|
30
|
-
resource_action: true
|
|
31
|
-
|
|
32
|
-
# Link to custom controller action (you must add the action + route yourself)
|
|
33
|
-
action :reports,
|
|
34
|
-
route_options: {action: :reports},
|
|
35
|
-
icon: Phlex::TablerIcons::ChartBar,
|
|
36
|
-
resource_action: true
|
|
37
|
-
end
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
**Important:** When adding custom routes for actions, always use the `as:` option to name them:
|
|
41
|
-
|
|
42
|
-
```ruby
|
|
43
|
-
# In your portal routes or config/routes.rb
|
|
44
|
-
resources :posts do
|
|
45
|
-
collection do
|
|
46
|
-
get :reports, as: :reports # Named route required!
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
This ensures `resource_url_for` can generate correct URLs, especially for nested resources.
|
|
52
|
-
|
|
53
|
-
**Note:** For custom operations with business logic, use **Interactive Actions** with an Interaction class instead. That's the recommended approach for most custom actions.
|
|
54
|
-
|
|
55
|
-
## Interactive Actions (with Interaction)
|
|
56
|
-
|
|
57
|
-
```ruby
|
|
58
|
-
class PostDefinition < ResourceDefinition
|
|
59
|
-
action :publish,
|
|
60
|
-
interaction: PublishInteraction,
|
|
61
|
-
icon: Phlex::TablerIcons::Send
|
|
62
|
-
|
|
63
|
-
action :archive,
|
|
64
|
-
interaction: ArchiveInteraction,
|
|
65
|
-
color: :danger,
|
|
66
|
-
category: :danger,
|
|
67
|
-
position: 1000,
|
|
68
|
-
confirmation: "Are you sure?"
|
|
69
|
-
end
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Action Options
|
|
73
|
-
|
|
74
|
-
```ruby
|
|
75
|
-
action :name,
|
|
76
|
-
# Display
|
|
77
|
-
label: "Custom Label", # Button text (default: name.titleize)
|
|
78
|
-
description: "What it does", # Tooltip/description
|
|
79
|
-
icon: Phlex::TablerIcons::Star, # Icon component
|
|
80
|
-
color: :danger, # :primary, :secondary, :danger
|
|
81
|
-
|
|
82
|
-
# Visibility
|
|
83
|
-
resource_action: true, # Show on index page
|
|
84
|
-
record_action: true, # Show on show page
|
|
85
|
-
collection_record_action: true, # Show in table rows
|
|
86
|
-
bulk_action: true, # For selected records
|
|
87
|
-
|
|
88
|
-
# Grouping
|
|
89
|
-
category: :primary, # :primary, :secondary, :danger
|
|
90
|
-
position: 50, # Order (lower = first)
|
|
91
|
-
|
|
92
|
-
# Behavior
|
|
93
|
-
confirmation: "Are you sure?", # Confirmation dialog
|
|
94
|
-
turbo_frame: "_top", # Turbo frame target
|
|
95
|
-
route_options: {action: :foo} # Route configuration
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
## Creating an Interaction
|
|
99
|
-
|
|
100
|
-
### Basic Structure
|
|
101
|
-
|
|
102
|
-
```ruby
|
|
103
|
-
# app/interactions/resource_interaction.rb (generated during install)
|
|
104
|
-
class ResourceInteraction < Plutonium::Resource::Interaction
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# app/interactions/archive_interaction.rb
|
|
108
|
-
class ArchiveInteraction < ResourceInteraction
|
|
109
|
-
presents label: "Archive",
|
|
110
|
-
icon: Phlex::TablerIcons::Archive,
|
|
111
|
-
description: "Archive this record"
|
|
112
|
-
|
|
113
|
-
attribute :resource # The record being acted on
|
|
114
|
-
|
|
115
|
-
def execute
|
|
116
|
-
resource.archived!
|
|
117
|
-
succeed(resource).with_message("Record archived successfully.")
|
|
118
|
-
rescue ActiveRecord::RecordInvalid => e
|
|
119
|
-
failed(e.record.errors)
|
|
120
|
-
rescue => error
|
|
121
|
-
failed("Archive failed. Please try again.")
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
### With Additional Inputs
|
|
127
|
-
|
|
128
|
-
```ruby
|
|
129
|
-
# app/interactions/company/invite_user_interaction.rb
|
|
130
|
-
class Company::InviteUserInteraction < Plutonium::Resource::Interaction
|
|
131
|
-
presents label: "Invite User", icon: Phlex::TablerIcons::Mail
|
|
132
|
-
|
|
133
|
-
attribute :resource # The company
|
|
134
|
-
attribute :email
|
|
135
|
-
attribute :role
|
|
136
|
-
|
|
137
|
-
# Configure form inputs
|
|
138
|
-
input :email, as: :email, hint: "User's email address"
|
|
139
|
-
input :role, as: :select, choices: %w[admin member viewer]
|
|
140
|
-
|
|
141
|
-
# Validations
|
|
142
|
-
validates :email, presence: true, format: {with: URI::MailTo::EMAIL_REGEXP}
|
|
143
|
-
validates :role, presence: true, inclusion: {in: %w[admin member viewer]}
|
|
144
|
-
|
|
145
|
-
def execute
|
|
146
|
-
UserInvite.create!(
|
|
147
|
-
company: resource,
|
|
148
|
-
email: email,
|
|
149
|
-
role: role,
|
|
150
|
-
invited_by: current_user
|
|
151
|
-
)
|
|
152
|
-
succeed(resource).with_message("Invitation sent to #{email}.")
|
|
153
|
-
rescue ActiveRecord::RecordInvalid => e
|
|
154
|
-
failed(e.record.errors)
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
### Bulk Action (Multiple Records)
|
|
160
|
-
|
|
161
|
-
Bulk actions operate on multiple selected records at once. When a definition has bulk actions, the resource table automatically shows:
|
|
162
|
-
- **Selection checkboxes** in each row
|
|
163
|
-
- **Bulk actions toolbar** that appears when records are selected
|
|
164
|
-
|
|
165
|
-
```ruby
|
|
166
|
-
# 1. Create the interaction (note: plural `resources` attribute)
|
|
167
|
-
class BulkArchiveInteraction < Plutonium::Resource::Interaction
|
|
168
|
-
presents label: "Archive Selected", icon: Phlex::TablerIcons::Archive
|
|
169
|
-
|
|
170
|
-
attribute :resources # Array of records (note: plural)
|
|
171
|
-
|
|
172
|
-
def execute
|
|
173
|
-
count = 0
|
|
174
|
-
resources.each do |record|
|
|
175
|
-
record.archived!
|
|
176
|
-
count += 1
|
|
177
|
-
end
|
|
178
|
-
succeed(resources).with_message("#{count} records archived.")
|
|
179
|
-
rescue => error
|
|
180
|
-
failed("Bulk archive failed: #{error.message}")
|
|
181
|
-
end
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
# 2. Register the action in the definition
|
|
185
|
-
class PostDefinition < ResourceDefinition
|
|
186
|
-
action :bulk_archive, interaction: BulkArchiveInteraction
|
|
187
|
-
# bulk_action: true is automatically inferred from `resources` attribute
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
# 3. Add policy method
|
|
191
|
-
class PostPolicy < ResourcePolicy
|
|
192
|
-
def bulk_archive?
|
|
193
|
-
create? # Or whatever permission level is appropriate
|
|
194
|
-
end
|
|
195
|
-
end
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
**Authorization for bulk actions:**
|
|
199
|
-
- Policy method (e.g., `bulk_archive?`) is checked **per record** - the backend fails the entire request if any selected record is not authorized
|
|
200
|
-
- Records are fetched via `current_authorized_scope` - only records the user can access are included
|
|
201
|
-
- The UI only shows action buttons that **all** selected records support (intersection of allowed actions)
|
|
202
|
-
|
|
203
|
-
### Resource Action (No Record)
|
|
204
|
-
|
|
205
|
-
```ruby
|
|
206
|
-
class ImportInteraction < Plutonium::Resource::Interaction
|
|
207
|
-
presents label: "Import CSV", icon: Phlex::TablerIcons::Upload
|
|
208
|
-
|
|
209
|
-
# No :resource or :resources attribute = resource action
|
|
210
|
-
attribute :file
|
|
211
|
-
|
|
212
|
-
input :file, as: :file
|
|
213
|
-
|
|
214
|
-
validates :file, presence: true
|
|
215
|
-
|
|
216
|
-
def execute
|
|
217
|
-
# Import logic...
|
|
218
|
-
succeed(nil).with_message("Import completed.")
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
## Interaction Responses
|
|
224
|
-
|
|
225
|
-
```ruby
|
|
226
|
-
def execute
|
|
227
|
-
# Success with message (redirects to resource automatically)
|
|
228
|
-
succeed(resource).with_message("Done!")
|
|
229
|
-
|
|
230
|
-
# Success with custom redirect (only if different from default)
|
|
231
|
-
succeed(resource)
|
|
232
|
-
.with_redirect_response(custom_dashboard_path)
|
|
233
|
-
.with_message("Redirecting...")
|
|
234
|
-
|
|
235
|
-
# Failure with field errors
|
|
236
|
-
failed(resource.errors)
|
|
237
|
-
|
|
238
|
-
# Failure with custom message
|
|
239
|
-
failed("Something went wrong")
|
|
240
|
-
|
|
241
|
-
# Failure with specific field
|
|
242
|
-
failed("Invalid value", :email)
|
|
243
|
-
end
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
**Note:** Redirect is automatic on success. Only use `with_redirect_response` for a different destination.
|
|
247
|
-
|
|
248
|
-
## Interaction Context
|
|
249
|
-
|
|
250
|
-
Inside an interaction:
|
|
251
|
-
- `current_user` - The authenticated user
|
|
252
|
-
- `view_context` - Access to helpers and view methods
|
|
253
|
-
|
|
254
|
-
```ruby
|
|
255
|
-
def execute
|
|
256
|
-
resource.update!(
|
|
257
|
-
archived_by: current_user,
|
|
258
|
-
archived_at: Time.current
|
|
259
|
-
)
|
|
260
|
-
succeed(resource)
|
|
261
|
-
end
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
## Defining in Definition
|
|
265
|
-
|
|
266
|
-
### Basic
|
|
267
|
-
|
|
268
|
-
```ruby
|
|
269
|
-
class PostDefinition < ResourceDefinition
|
|
270
|
-
action :publish, interaction: PublishInteraction
|
|
271
|
-
action :archive, interaction: ArchiveInteraction
|
|
272
|
-
end
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
### With Overrides
|
|
276
|
-
|
|
277
|
-
```ruby
|
|
278
|
-
class PostDefinition < ResourceDefinition
|
|
279
|
-
action :archive,
|
|
280
|
-
interaction: ArchiveInteraction,
|
|
281
|
-
collection_record_action: false, # Don't show in table
|
|
282
|
-
color: :danger,
|
|
283
|
-
position: 1000 # Show last
|
|
284
|
-
end
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
### Inherited Actions
|
|
288
|
-
|
|
289
|
-
Actions defined in `ResourceDefinition` (created during install) are inherited by all definitions:
|
|
290
|
-
|
|
291
|
-
```ruby
|
|
292
|
-
# app/definitions/resource_definition.rb (created during install)
|
|
293
|
-
class ResourceDefinition < Plutonium::Resource::Definition
|
|
294
|
-
action :archive, interaction: ArchiveInteraction, color: :danger, position: 1000
|
|
295
|
-
end
|
|
296
|
-
|
|
297
|
-
# All definitions inherit the archive action automatically
|
|
298
|
-
class PostDefinition < ResourceDefinition
|
|
299
|
-
end
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
### Portal-Specific Actions
|
|
303
|
-
|
|
304
|
-
Override actions for a specific portal:
|
|
305
|
-
|
|
306
|
-
```ruby
|
|
307
|
-
# packages/admin_portal/app/definitions/admin_portal/post_definition.rb
|
|
308
|
-
class AdminPortal::PostDefinition < ::PostDefinition
|
|
309
|
-
# Add admin-only actions
|
|
310
|
-
action :feature, interaction: FeaturePostInteraction
|
|
311
|
-
action :bulk_publish, interaction: BulkPublishInteraction
|
|
312
|
-
end
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
## Default CRUD Actions
|
|
316
|
-
|
|
317
|
-
Plutonium provides these by default:
|
|
318
|
-
|
|
319
|
-
```ruby
|
|
320
|
-
action :new, resource_action: true, position: 10
|
|
321
|
-
action :show, collection_record_action: true, position: 10
|
|
322
|
-
action :edit, record_action: true, position: 20
|
|
323
|
-
action :destroy, record_action: true, position: 100, category: :danger
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
## Authorization
|
|
327
|
-
|
|
328
|
-
Actions are authorized via policies:
|
|
329
|
-
|
|
330
|
-
```ruby
|
|
331
|
-
# app/policies/post_policy.rb
|
|
332
|
-
class PostPolicy < ResourcePolicy
|
|
333
|
-
def publish?
|
|
334
|
-
user.admin? || record.author == user
|
|
335
|
-
end
|
|
336
|
-
|
|
337
|
-
def archive?
|
|
338
|
-
user.admin?
|
|
339
|
-
end
|
|
340
|
-
end
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
The action only appears if the policy method returns `true`.
|
|
344
|
-
|
|
345
|
-
## Immediate vs Form Actions
|
|
346
|
-
|
|
347
|
-
**Immediate** - Executes without showing a form (when interaction has no extra inputs):
|
|
348
|
-
|
|
349
|
-
```ruby
|
|
350
|
-
class ArchiveInteraction < Plutonium::Resource::Interaction
|
|
351
|
-
attribute :resource # Only resource, no other inputs
|
|
352
|
-
# No input declarations
|
|
353
|
-
|
|
354
|
-
def execute
|
|
355
|
-
resource.archived!
|
|
356
|
-
succeed(resource)
|
|
357
|
-
end
|
|
358
|
-
end
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
**Form** - Shows a form first (when interaction has additional inputs):
|
|
362
|
-
|
|
363
|
-
```ruby
|
|
364
|
-
class InviteUserInteraction < Plutonium::Resource::Interaction
|
|
365
|
-
attribute :resource
|
|
366
|
-
attribute :email
|
|
367
|
-
attribute :role
|
|
368
|
-
|
|
369
|
-
input :email
|
|
370
|
-
input :role, as: :select, choices: %w[admin member]
|
|
371
|
-
# Has inputs = shows form first
|
|
372
|
-
end
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
## Common Patterns
|
|
376
|
-
|
|
377
|
-
### Archive/Restore
|
|
378
|
-
|
|
379
|
-
```ruby
|
|
380
|
-
class ArchiveInteraction < Plutonium::Resource::Interaction
|
|
381
|
-
presents label: "Archive", icon: Phlex::TablerIcons::Archive
|
|
382
|
-
attribute :resource
|
|
383
|
-
|
|
384
|
-
def execute
|
|
385
|
-
resource.archived!
|
|
386
|
-
succeed(resource).with_message("Archived.")
|
|
387
|
-
end
|
|
388
|
-
end
|
|
389
|
-
|
|
390
|
-
class RestoreInteraction < Plutonium::Resource::Interaction
|
|
391
|
-
presents label: "Restore", icon: Phlex::TablerIcons::Refresh
|
|
392
|
-
attribute :resource
|
|
393
|
-
|
|
394
|
-
def execute
|
|
395
|
-
resource.active!
|
|
396
|
-
succeed(resource).with_message("Restored.")
|
|
397
|
-
end
|
|
398
|
-
end
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
### Send Notification
|
|
402
|
-
|
|
403
|
-
```ruby
|
|
404
|
-
class SendReminderInteraction < Plutonium::Resource::Interaction
|
|
405
|
-
presents label: "Send Reminder", icon: Phlex::TablerIcons::Bell
|
|
406
|
-
attribute :resource
|
|
407
|
-
attribute :message
|
|
408
|
-
|
|
409
|
-
input :message, as: :text, hint: "Custom message (optional)"
|
|
410
|
-
|
|
411
|
-
def execute
|
|
412
|
-
ReminderMailer.with(record: resource, message: message).deliver_later
|
|
413
|
-
succeed(resource).with_message("Reminder sent.")
|
|
414
|
-
end
|
|
415
|
-
end
|
|
416
|
-
```
|
|
417
|
-
|
|
418
|
-
## Related Skills
|
|
419
|
-
|
|
420
|
-
- `plutonium-definition` - Overview and structure
|
|
421
|
-
- `plutonium-definition` - Fields, inputs, displays
|
|
422
|
-
- `plutonium-definition-query` - Search, filters, scopes
|
|
423
|
-
- `plutonium-interaction` - Writing interaction classes
|
|
424
|
-
- `plutonium-policy` - Controlling action access
|