plutonium 0.24.5 → 0.24.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1f9e098d47ef383cae732c39bef92132a9084e0dcda2119bcbb33c9726ae5d6b
4
- data.tar.gz: f43fe2a2794feb1bc1ee698587b9c6169459b08859a862a7e615f7364f174ec3
3
+ metadata.gz: cc9806660f2996abd33f6a5bfd149c489f6baa696500c1ff15d4a052d567c956
4
+ data.tar.gz: 5c5ce03afc23a152e9c1ff395bffff240a6ce5eb7b7be3e2b2ee49f080c20a68
5
5
  SHA512:
6
- metadata.gz: d6c70ccd4aca7091971465a5c55b465bf8b380d699b7ce0df4d265ee854efef660b3e85202593532f88d192f8487d8af8a241e30edbed9fb2b35e1c674bdd352
7
- data.tar.gz: 78a8129530a6d1d036b507cb71b66b152cc7bfe463a849cf2ab5fe2b0378a1893ec3570da1fbd45e9b81aadbd02561adb2b7d4fe697de413d1f5f361ac1262cf
6
+ metadata.gz: 2fcb9db510846fcb3208dc9c12c3672fa6cb32dd8df9a8db3a828eb4c733eb93bea5c2807bd798791b8abc7e8aad5fb79011dfd5ff86e39294482fccfa59a3ab
7
+ data.tar.gz: 68d70cc364a4963f8a1409bc642562dccac57393161ee28d3eb6b54c5e2c7c2487c6d19ab4f2b626e5be5defa33a083c440fcb261b982b0f448bd80b4b30810b
@@ -4,5 +4,9 @@ ActiveSupport.on_load(:active_record) do
4
4
  def jsonb(*args, **options)
5
5
  json(*args, **options)
6
6
  end
7
+
8
+ def uuid(*args, **options)
9
+ string(*args, **options)
10
+ end
7
11
  end
8
12
  end
@@ -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
@@ -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:
@@ -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
@@ -52,7 +52,7 @@ module Plutonium
52
52
  outcome.to_response.process(self) do |value|
53
53
  respond_to do |format|
54
54
  if outcome.success?
55
- return_url = redirect_url_after_action_on(resource_class)
55
+ return_url = redirect_url_after_action_on(resource_record!)
56
56
 
57
57
  format.any { redirect_to return_url, status: :see_other }
58
58
 
@@ -23,8 +23,14 @@ module Plutonium
23
23
  private
24
24
 
25
25
  def render_link
26
+ uri = URI.parse(@url)
27
+ params = Rack::Utils.parse_nested_query(uri.query)
28
+ params["return_to"] = request.original_url
29
+ uri.query = params.to_query
30
+ uri.to_s
31
+
26
32
  link_to(
27
- @url,
33
+ uri.to_s,
28
34
  class: button_classes,
29
35
  data: {turbo_frame: @action.turbo_frame}
30
36
  ) do
@@ -23,6 +23,7 @@ module Plutonium
23
23
  end
24
24
 
25
25
  delegate \
26
+ :main_app,
26
27
  :resource_class,
27
28
  :resource_record!,
28
29
  :resource_record?,
@@ -61,7 +61,7 @@ module Plutonium
61
61
  private
62
62
 
63
63
  def render_actions
64
- input name: :return_to, value: request.params[:return_to], type: :hidden, hidden: true
64
+ input name: "return_to", value: request.params[:return_to], type: :hidden, hidden: true
65
65
 
66
66
  actions_wrapper {
67
67
  render submit_button
@@ -1,5 +1,5 @@
1
1
  module Plutonium
2
- VERSION = "0.24.5"
2
+ VERSION = "0.24.7"
3
3
  NEXT_MAJOR_VERSION = VERSION.split(".").tap { |v|
4
4
  v[1] = v[1].to_i + 1
5
5
  v[2] = 0
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.5
4
+ version: 0.24.7
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-27 00:00:00.000000000 Z
11
+ date: 2025-07-01 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/sqlite_json_alias.rb
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