plutonium 0.24.5 → 0.24.6
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/config/initializers/{sqlite_json_alias.rb → sqlite_alias.rb} +4 -0
- data/docs/modules/action.md +54 -0
- data/docs/modules/interaction.md +172 -0
- data/docs/modules/routing.md +49 -0
- data/lib/plutonium/definition/actions.rb +12 -0
- data/lib/plutonium/ui/component/methods.rb +1 -0
- data/lib/plutonium/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '08632486824ac0d80c9cb6b5a16bfc384563d3775dd0ce6fa95ac68428b0c13d'
|
4
|
+
data.tar.gz: 70aea071cc4be438c5cd765fbf42166b7f12ba174ec6253017599436790aaf84
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa4300f5335a1e2c0571508aabea5584dc6c06101d5b5afbbd0e62c6131f5a5534680c6541ab8ef38c5045278c4db7df20a88feeb06960cb912d5f22b7e030a2
|
7
|
+
data.tar.gz: 3d99dcb56823b1a46222c2166f347026c1475c0f0d6ff6bb8d03e257de7ee82fe9efa491e3de6588d89329b01075bfb0a6ce6c566291e3067b0ed65d66497dfb
|
data/docs/modules/action.md
CHANGED
@@ -113,6 +113,53 @@ action :documentation,
|
|
113
113
|
```
|
114
114
|
:::
|
115
115
|
|
116
|
+
### Dynamic Route Options
|
117
|
+
|
118
|
+
For actions that need dynamic URL generation based on the current record or context, use the `RouteOptions` class with a custom `url_resolver`:
|
119
|
+
|
120
|
+
::: code-group
|
121
|
+
```ruby [Dynamic Parent-Child Navigation]
|
122
|
+
# Navigate to create a child resource with the current record as parent
|
123
|
+
action :create_deployment,
|
124
|
+
label: "Create Deployment",
|
125
|
+
icon: Phlex::TablerIcons::Rocket,
|
126
|
+
record_action: true,
|
127
|
+
route_options: Plutonium::Action::RouteOptions.new(
|
128
|
+
url_resolver: ->(subject) {
|
129
|
+
resource_url_for(UniversalFlow::Deployment, action: :new, parent: subject)
|
130
|
+
}
|
131
|
+
)
|
132
|
+
```
|
133
|
+
```ruby [Conditional Routing]
|
134
|
+
# Different routes based on user permissions or record state
|
135
|
+
action :manage_settings,
|
136
|
+
label: "Manage Settings",
|
137
|
+
resource_action: true,
|
138
|
+
route_options: Plutonium::Action::RouteOptions.new(
|
139
|
+
url_resolver: ->(subject) {
|
140
|
+
if current_user.admin?
|
141
|
+
admin_settings_path(subject)
|
142
|
+
else
|
143
|
+
basic_settings_path(subject)
|
144
|
+
end
|
145
|
+
}
|
146
|
+
)
|
147
|
+
```
|
148
|
+
```ruby [External Integration]
|
149
|
+
# Dynamic external URLs based on record attributes
|
150
|
+
action :view_external,
|
151
|
+
label: "View in External System",
|
152
|
+
record_action: true,
|
153
|
+
route_options: Plutonium::Action::RouteOptions.new(
|
154
|
+
url_resolver: ->(subject) {
|
155
|
+
"https://external-system.com/items/#{subject.external_id}"
|
156
|
+
}
|
157
|
+
)
|
158
|
+
```
|
159
|
+
:::
|
160
|
+
|
161
|
+
The `url_resolver` lambda receives the current record (for record actions) or resource class (for resource actions) as the `subject` parameter, allowing you to generate URLs dynamically based on the context.
|
162
|
+
|
116
163
|
### Interactive Actions
|
117
164
|
|
118
165
|
Interactive actions are powered by an `Interaction` class and handle business logic. The action's properties (label, description, etc.) are often inferred from the interaction itself.
|
@@ -173,6 +220,13 @@ end
|
|
173
220
|
4. **Meaningful Confirmations**: Use confirmation messages for destructive actions
|
174
221
|
5. **Logical Positioning**: Order actions by importance and frequency of use
|
175
222
|
|
223
|
+
### Dynamic Route Actions
|
224
|
+
|
225
|
+
1. **Context Awareness**: Use the subject parameter to make routing decisions based on the current record or resource
|
226
|
+
2. **Error Handling**: Handle cases where dynamic URLs might fail (e.g., missing external IDs)
|
227
|
+
3. **Performance**: Keep url_resolver lambdas simple to avoid performance issues
|
228
|
+
4. **Security**: Validate permissions within the lambda when generating sensitive URLs
|
229
|
+
|
176
230
|
### Interactive Actions
|
177
231
|
|
178
232
|
1. **Single Purpose**: Each action should have a single, well-defined purpose
|
data/docs/modules/interaction.md
CHANGED
@@ -314,6 +314,178 @@ class PostDefinition < Plutonium::Resource::Definition
|
|
314
314
|
end
|
315
315
|
```
|
316
316
|
|
317
|
+
### Dynamic Route Actions
|
318
|
+
|
319
|
+
For actions that need dynamic URL generation, combine interactions with custom route options:
|
320
|
+
|
321
|
+
```ruby
|
322
|
+
class PostDefinition < Plutonium::Resource::Definition
|
323
|
+
# Simple interaction with static route
|
324
|
+
action :publish, interaction: PublishPostInteraction
|
325
|
+
|
326
|
+
# Complex action with dynamic route generation using RouteOptions
|
327
|
+
action :create_deployment,
|
328
|
+
label: "Create Deployment",
|
329
|
+
icon: Phlex::TablerIcons::Rocket,
|
330
|
+
record_action: true,
|
331
|
+
interaction: CreateDeploymentInteraction,
|
332
|
+
route_options: Plutonium::Action::RouteOptions.new(
|
333
|
+
url_resolver: ->(subject) {
|
334
|
+
resource_url_for(Deployment, action: :new, parent: subject)
|
335
|
+
}
|
336
|
+
)
|
337
|
+
|
338
|
+
# Conditional routing based on user permissions
|
339
|
+
action :manage_advanced_settings,
|
340
|
+
label: "Advanced Settings",
|
341
|
+
resource_action: true,
|
342
|
+
interaction: ManageAdvancedSettingsInteraction,
|
343
|
+
route_options: Plutonium::Action::RouteOptions.new(
|
344
|
+
url_resolver: ->(subject) {
|
345
|
+
if current_user.admin?
|
346
|
+
admin_settings_path(subject)
|
347
|
+
else
|
348
|
+
basic_settings_path(subject)
|
349
|
+
end
|
350
|
+
}
|
351
|
+
)
|
352
|
+
|
353
|
+
# External system integration with dynamic URLs
|
354
|
+
action :sync_with_external,
|
355
|
+
label: "Sync External",
|
356
|
+
record_action: true,
|
357
|
+
interaction: SyncExternalInteraction,
|
358
|
+
route_options: Plutonium::Action::RouteOptions.new(
|
359
|
+
url_resolver: ->(subject) {
|
360
|
+
"https://api.external-system.com/sync/#{subject.external_id}"
|
361
|
+
}
|
362
|
+
)
|
363
|
+
end
|
364
|
+
```
|
365
|
+
|
366
|
+
The `url_resolver` lambda provides powerful flexibility:
|
367
|
+
- **Record Actions**: Receive the current record as `subject`
|
368
|
+
- **Resource Actions**: Receive the resource class as `subject`
|
369
|
+
- **Bulk Actions**: Receive the resource class with selected records available in params
|
370
|
+
- **Context Access**: Full access to controller context including `current_user`, helper methods, etc.
|
371
|
+
|
372
|
+
### Advanced Dynamic Routing Examples
|
373
|
+
|
374
|
+
```ruby
|
375
|
+
class ProjectDefinition < Plutonium::Resource::Definition
|
376
|
+
# Multi-step workflow routing
|
377
|
+
action :start_workflow,
|
378
|
+
label: "Start Workflow",
|
379
|
+
record_action: true,
|
380
|
+
interaction: StartWorkflowInteraction,
|
381
|
+
route_options: Plutonium::Action::RouteOptions.new(
|
382
|
+
url_resolver: ->(subject) {
|
383
|
+
case subject.status
|
384
|
+
when 'draft'
|
385
|
+
new_project_review_path(subject)
|
386
|
+
when 'review'
|
387
|
+
project_approval_path(subject)
|
388
|
+
else
|
389
|
+
project_path(subject)
|
390
|
+
end
|
391
|
+
}
|
392
|
+
)
|
393
|
+
|
394
|
+
# Dynamic nested resource creation
|
395
|
+
action :add_team_member,
|
396
|
+
label: "Add Team Member",
|
397
|
+
record_action: true,
|
398
|
+
interaction: AddTeamMemberInteraction,
|
399
|
+
route_options: Plutonium::Action::RouteOptions.new(
|
400
|
+
url_resolver: ->(subject) {
|
401
|
+
if subject.team.full?
|
402
|
+
project_team_waitlist_path(subject)
|
403
|
+
else
|
404
|
+
new_project_team_member_path(subject)
|
405
|
+
end
|
406
|
+
}
|
407
|
+
)
|
408
|
+
|
409
|
+
# Conditional external redirects
|
410
|
+
action :open_in_ide,
|
411
|
+
label: "Open in IDE",
|
412
|
+
record_action: true,
|
413
|
+
interaction: OpenInIDEInteraction,
|
414
|
+
route_options: Plutonium::Action::RouteOptions.new(
|
415
|
+
url_resolver: ->(subject) {
|
416
|
+
if subject.repository_url.present?
|
417
|
+
"vscode://vscode.git/clone?url=#{subject.repository_url}"
|
418
|
+
else
|
419
|
+
project_repository_setup_path(subject)
|
420
|
+
end
|
421
|
+
}
|
422
|
+
)
|
423
|
+
end
|
424
|
+
```
|
425
|
+
|
426
|
+
### Custom Interaction with Dynamic Routing
|
427
|
+
|
428
|
+
```ruby
|
429
|
+
class CreateChildResourceInteraction < Plutonium::Interaction::Base
|
430
|
+
attribute :parent_id, :integer
|
431
|
+
attribute :resource_type, :string
|
432
|
+
attribute :attributes, :hash
|
433
|
+
|
434
|
+
validates :parent_id, :resource_type, presence: true
|
435
|
+
|
436
|
+
private
|
437
|
+
|
438
|
+
def execute
|
439
|
+
parent = find_parent_resource
|
440
|
+
child_class = resource_type.constantize
|
441
|
+
child = child_class.new(attributes.merge(parent_key => parent))
|
442
|
+
|
443
|
+
if child.save
|
444
|
+
succeed(child)
|
445
|
+
.with_message("#{resource_type} created successfully")
|
446
|
+
.with_redirect_response(resource_url_for(child, parent: parent))
|
447
|
+
else
|
448
|
+
failed(child.errors)
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
def find_parent_resource
|
453
|
+
# Dynamic parent resolution based on context
|
454
|
+
case resource_type
|
455
|
+
when 'Deployment'
|
456
|
+
Project.find(parent_id)
|
457
|
+
when 'Task'
|
458
|
+
Project.find(parent_id)
|
459
|
+
else
|
460
|
+
raise ArgumentError, "Unknown resource type: #{resource_type}"
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
def parent_key
|
465
|
+
case resource_type
|
466
|
+
when 'Deployment', 'Task'
|
467
|
+
:project_id
|
468
|
+
else
|
469
|
+
raise ArgumentError, "Unknown parent key for: #{resource_type}"
|
470
|
+
end
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
# Usage in resource definition
|
475
|
+
class ProjectDefinition < Plutonium::Resource::Definition
|
476
|
+
action :create_deployment,
|
477
|
+
label: "Create Deployment",
|
478
|
+
record_action: true,
|
479
|
+
interaction: CreateChildResourceInteraction,
|
480
|
+
route_options: Plutonium::Action::RouteOptions.new(
|
481
|
+
url_resolver: ->(subject) {
|
482
|
+
# The subject here will be the Project record
|
483
|
+
new_deployment_path(project_id: subject.id)
|
484
|
+
}
|
485
|
+
)
|
486
|
+
end
|
487
|
+
```
|
488
|
+
|
317
489
|
### Controller Integration
|
318
490
|
|
319
491
|
Controllers can call interactions directly, but this requires manual setup:
|
data/docs/modules/routing.md
CHANGED
@@ -373,6 +373,55 @@ bulk_action_url(Post, :archive, ids: [1, 2, 3])
|
|
373
373
|
# => "/posts/bulk_actions/archive?ids[]=1&ids[]=2&ids[]=3"
|
374
374
|
```
|
375
375
|
|
376
|
+
### Dynamic URL Generation for Actions
|
377
|
+
|
378
|
+
For actions that need context-aware URL generation, use `RouteOptions` with custom `url_resolver`:
|
379
|
+
|
380
|
+
```ruby
|
381
|
+
# In a resource definition
|
382
|
+
class ProjectDefinition < Plutonium::Resource::Definition
|
383
|
+
# Dynamic parent-child navigation
|
384
|
+
action :create_deployment,
|
385
|
+
label: "Create Deployment",
|
386
|
+
icon: Phlex::TablerIcons::Rocket,
|
387
|
+
record_action: true,
|
388
|
+
route_options: Plutonium::Action::RouteOptions.new(
|
389
|
+
url_resolver: ->(subject) {
|
390
|
+
resource_url_for(UniversalFlow::Deployment, action: :new, parent: subject)
|
391
|
+
}
|
392
|
+
)
|
393
|
+
|
394
|
+
# Conditional routing based on permissions
|
395
|
+
action :manage_settings,
|
396
|
+
label: "Settings",
|
397
|
+
resource_action: true,
|
398
|
+
route_options: Plutonium::Action::RouteOptions.new(
|
399
|
+
url_resolver: ->(subject) {
|
400
|
+
if current_user.admin?
|
401
|
+
admin_project_settings_path(subject)
|
402
|
+
else
|
403
|
+
project_settings_path(subject)
|
404
|
+
end
|
405
|
+
}
|
406
|
+
)
|
407
|
+
|
408
|
+
# External system integration
|
409
|
+
action :view_in_external_system,
|
410
|
+
label: "View Externally",
|
411
|
+
record_action: true,
|
412
|
+
route_options: Plutonium::Action::RouteOptions.new(
|
413
|
+
url_resolver: ->(subject) {
|
414
|
+
"https://external-system.com/projects/#{subject.external_id}"
|
415
|
+
}
|
416
|
+
)
|
417
|
+
end
|
418
|
+
```
|
419
|
+
|
420
|
+
The `url_resolver` lambda receives:
|
421
|
+
- **For record actions**: The current record instance
|
422
|
+
- **For resource actions**: The resource class
|
423
|
+
- **For bulk actions**: The resource class (with selected IDs available in params)
|
424
|
+
|
376
425
|
### Context-Aware URL Generation
|
377
426
|
|
378
427
|
In nested controller contexts, URLs automatically include proper context:
|
@@ -48,6 +48,18 @@ module Plutonium
|
|
48
48
|
record_action: true, collection_record_action: true, category: :danger,
|
49
49
|
icon: Phlex::TablerIcons::Trash, position: 100,
|
50
50
|
confirmation: "Are you sure?", turbo_frame: "_top")
|
51
|
+
|
52
|
+
# Example of dynamic route options using custom url_resolver:
|
53
|
+
#
|
54
|
+
# action(:create_deployment,
|
55
|
+
# label: "Create Deployment",
|
56
|
+
# icon: Phlex::TablerIcons::Rocket,
|
57
|
+
# record_action: true,
|
58
|
+
# route_options: Plutonium::Action::RouteOptions.new(
|
59
|
+
# url_resolver: ->(subject) {
|
60
|
+
# resource_url_for(UniversalFlow::Deployment, action: :new, parent: subject)
|
61
|
+
# }
|
62
|
+
# ))
|
51
63
|
end
|
52
64
|
end
|
53
65
|
end
|
data/lib/plutonium/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plutonium
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.24.
|
4
|
+
version: 0.24.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stefan Froelich
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-06-
|
11
|
+
date: 2025-06-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: zeitwerk
|
@@ -488,7 +488,7 @@ files:
|
|
488
488
|
- config/initializers/hotwire_turbo_monkey_patches.rb
|
489
489
|
- config/initializers/pagy.rb
|
490
490
|
- config/initializers/rabl.rb
|
491
|
-
- config/initializers/
|
491
|
+
- config/initializers/sqlite_alias.rb
|
492
492
|
- docs/.vitepress/config.ts
|
493
493
|
- docs/.vitepress/theme/custom.css
|
494
494
|
- docs/.vitepress/theme/index.ts
|