plutonium 0.21.0 → 0.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/plutonium.css +1 -1
- data/app/assets/plutonium.js +19 -144
- data/app/assets/plutonium.js.map +4 -4
- data/app/assets/plutonium.min.js +41 -41
- data/app/assets/plutonium.min.js.map +4 -4
- data/lib/plutonium/interaction/README.md +114 -2
- data/lib/plutonium/interaction/base.rb +3 -0
- data/lib/plutonium/interaction/nested_attributes.rb +93 -0
- data/lib/plutonium/resource/controllers/crud_actions.rb +28 -14
- data/lib/plutonium/resource/controllers/interactive_actions.rb +38 -18
- data/lib/plutonium/resource/record/associated_with.rb +2 -2
- data/lib/plutonium/ui/form/base.rb +6 -0
- data/lib/plutonium/ui/form/resource.rb +22 -7
- data/lib/plutonium/ui/form/theme.rb +1 -1
- data/lib/plutonium/version.rb +1 -1
- data/package.json +1 -1
- data/src/js/controllers/form_controller.js +17 -1
- metadata +3 -2
@@ -262,6 +262,120 @@ class MyInteraction < Plutonium::Interaction::Base
|
|
262
262
|
end
|
263
263
|
```
|
264
264
|
|
265
|
+
### Interactions with Nested Attributes
|
266
|
+
|
267
|
+
This example demonstrates how to handle nested attributes—specifically,
|
268
|
+
a `User` with multiple `Contact` and `UserAddress` records using
|
269
|
+
a Plutonium `Interaction`.
|
270
|
+
|
271
|
+
#### Key Highlights
|
272
|
+
|
273
|
+
The model definitions are included here for completeness, but the primary focus
|
274
|
+
remains on demonstrating how to build interactions that handle nested
|
275
|
+
attributes.
|
276
|
+
|
277
|
+
- Core user attributes (`first_name`, `last_name`, `email`) are declared and
|
278
|
+
validated at the top level of the interaction.
|
279
|
+
|
280
|
+
- Nested associations (`contacts`, `addresses`) are managed via
|
281
|
+
`accepts_nested_attributes_for`. The optional `reject_if` condition is used
|
282
|
+
to discard entries that lack required fields—helping ensure data integrity at
|
283
|
+
the input level.
|
284
|
+
|
285
|
+
- The `nested_input` DSL provides a declarative way to structure nested inputs,
|
286
|
+
specifying accepted fields and mapping them to their respective definition
|
287
|
+
classes (`ContactDefinition` and `UserAddressDefinition`).
|
288
|
+
|
289
|
+
- During execution, a `User` instance is initialized with both top-level and
|
290
|
+
nested attributes, then persisted with all applicable validations.
|
291
|
+
|
292
|
+
**Note:** The `class_name` option is explicitly defined in the interaction's
|
293
|
+
`accepts_nested_attributes_for` macro because the `addresses` association does
|
294
|
+
not directly map to its underlying model name. Simply provide the class name,
|
295
|
+
for example, `class_name: "UserAddress"`, to ensure the correct model is used.
|
296
|
+
|
297
|
+
**This is essential only when the association name differs from the actual
|
298
|
+
class name.**
|
299
|
+
|
300
|
+
This approach enables seamless handling of complex nested input from forms or
|
301
|
+
API requests, while keeping validation logic clean, maintainable, and modular.
|
302
|
+
|
303
|
+
```ruby
|
304
|
+
# app/models/user.rb
|
305
|
+
class User < ApplicationRecord
|
306
|
+
include Plutonium::Resource::Record
|
307
|
+
|
308
|
+
has_many :contacts
|
309
|
+
has_many :addresses, class_name: "UserAddress"
|
310
|
+
|
311
|
+
accepts_nested_attributes_for :contacts, :addresses
|
312
|
+
end
|
313
|
+
|
314
|
+
# app/models/contact.rb
|
315
|
+
class Contact < ApplicationRecord
|
316
|
+
include Plutonium::Resource::Record
|
317
|
+
|
318
|
+
belongs_to :user
|
319
|
+
validates :label, :phone_number, presence: true
|
320
|
+
end
|
321
|
+
|
322
|
+
# app/models/user_address.rb
|
323
|
+
class UserAddress < ApplicationRecord
|
324
|
+
include Plutonium::Resource::Record
|
325
|
+
|
326
|
+
belongs_to :user
|
327
|
+
validates :label, :map_url, presence: true
|
328
|
+
end
|
329
|
+
|
330
|
+
# app/interactions/users/interactions/create_user_interaction.rb
|
331
|
+
module Users
|
332
|
+
module Interactions
|
333
|
+
class CreateUserInteraction < Plutonium::Interaction::Base
|
334
|
+
include Plutonium::Definition::Presentable
|
335
|
+
|
336
|
+
presents label: "Add a new user", icon: Phlex::Tabler::UserPlus
|
337
|
+
|
338
|
+
attribute :first_name, :string
|
339
|
+
attribute :last_name, :string
|
340
|
+
attribute :email, :string
|
341
|
+
attribute :contacts
|
342
|
+
attribute :addresses
|
343
|
+
|
344
|
+
validates :first_name, :last_name, presence: true
|
345
|
+
validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
|
346
|
+
|
347
|
+
accepts_nested_attributes_for :contacts,
|
348
|
+
reject_if: proc { |attributes| attributes[:label].blank? }
|
349
|
+
|
350
|
+
accepts_nested_attributes_for :addresses, class_name: "UserAddress",
|
351
|
+
reject_if: proc { |attributes| attributes[:label].blank? }
|
352
|
+
|
353
|
+
nested_input :contacts,
|
354
|
+
using: ContactDefinition,
|
355
|
+
fields: %i[label phone_number],
|
356
|
+
description: "Add one or more contacts for this user."
|
357
|
+
|
358
|
+
nested_input :addresses,
|
359
|
+
using: UserAddressDefinition,
|
360
|
+
fields: %i[label map_url],
|
361
|
+
description: "Add one or more addresses for this user."
|
362
|
+
|
363
|
+
private
|
364
|
+
|
365
|
+
def execute
|
366
|
+
user = User.new(self.attributes)
|
367
|
+
|
368
|
+
if user.save
|
369
|
+
success(user).with_message("User created successfully")
|
370
|
+
else
|
371
|
+
failed(user.errors)
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|
377
|
+
```
|
378
|
+
|
265
379
|
## Examples
|
266
380
|
|
267
381
|
### Chaining Operations
|
@@ -311,7 +425,6 @@ This example demonstrates how to chain multiple operations, handle potential fai
|
|
311
425
|
|
312
426
|
By following these guidelines and examples, you can effectively implement and use the Use Case Driven Design pattern in your Rails applications, leading to more maintainable and testable code.
|
313
427
|
|
314
|
-
|
315
428
|
### Example interaction with workflow
|
316
429
|
|
317
430
|
```ruby
|
@@ -367,7 +480,6 @@ module Orders
|
|
367
480
|
end
|
368
481
|
```
|
369
482
|
|
370
|
-
|
371
483
|
class Sample < Phlex::HTML
|
372
484
|
def view_template
|
373
485
|
p { "my custom template" }
|
@@ -25,6 +25,8 @@ module Plutonium
|
|
25
25
|
include Plutonium::Definition::DefineableProps
|
26
26
|
include Plutonium::Definition::ConfigAttr
|
27
27
|
include Plutonium::Definition::Presentable
|
28
|
+
include Plutonium::Definition::NestedInputs
|
29
|
+
include Plutonium::Interaction::NestedAttributes
|
28
30
|
# include Plutonium::Interaction::Concerns::WorkflowDSL
|
29
31
|
|
30
32
|
class Form < Plutonium::UI::Form::Interaction; end
|
@@ -89,6 +91,7 @@ module Plutonium
|
|
89
91
|
def succeed(value = nil)
|
90
92
|
Plutonium::Interaction::Outcome::Success.new(value)
|
91
93
|
end
|
94
|
+
|
92
95
|
alias_method :success, :succeed
|
93
96
|
|
94
97
|
def failed(errors = nil, attribute = :base)
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plutonium
|
4
|
+
module Interaction
|
5
|
+
module NestedAttributes
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
class_methods do
|
9
|
+
# Dynamically defines writer and reader methods for handling nested
|
10
|
+
# attributes in form objects or interaction classes, mimicking the
|
11
|
+
# behavior of ActiveRecord's `accepts_nested_attributes_for`.
|
12
|
+
#
|
13
|
+
# This method allows you to pass in nested data (e.g. from a form) and
|
14
|
+
# automatically build or destroy associated records based on that input.
|
15
|
+
#
|
16
|
+
# === Example 1: Basic usage with default naming
|
17
|
+
# # If `Contact` is the associated model inferred from the
|
18
|
+
# `:contacts` association:
|
19
|
+
#
|
20
|
+
# `accepts_nested_attributes_for :contacts`
|
21
|
+
#
|
22
|
+
# === Example 2: When association name and model name differ
|
23
|
+
# Suppose the `User` model has a `has_many :contacts` association
|
24
|
+
# pointing to a `UserContactInfo` model. You need to specify the
|
25
|
+
# model name.
|
26
|
+
#
|
27
|
+
# `accepts_nested_attributes_for :contacts, class_name: "UserAddress"`
|
28
|
+
#
|
29
|
+
# This macro defines:
|
30
|
+
# - `contacts_attributes=` — used to assign nested attributes,
|
31
|
+
# including support for `_destroy`
|
32
|
+
# - `contacts_attributes` — returns the current attributes of
|
33
|
+
# associated records
|
34
|
+
#
|
35
|
+
# @param association [Symbol] The association name. (e.g., `:contacts`).
|
36
|
+
# @param class_name [String, nil] Required if association reflection
|
37
|
+
# is needed to determine the associated model class (e.g. when the
|
38
|
+
# association name doesn't match the class name).
|
39
|
+
# @param reject_if [Proc, Symbol, nil] Used to skip building association
|
40
|
+
# records when the condition returns true.
|
41
|
+
def accepts_nested_attributes_for(
|
42
|
+
association,
|
43
|
+
class_name: nil,
|
44
|
+
reject_if: nil
|
45
|
+
)
|
46
|
+
destroy_values = [1, "1", "true", true]
|
47
|
+
|
48
|
+
should_destroy = ->(value) { destroy_values.include?(value) }
|
49
|
+
|
50
|
+
should_reject =
|
51
|
+
lambda do |attrs|
|
52
|
+
case reject_if
|
53
|
+
when Symbol
|
54
|
+
send(reject_if, attrs)
|
55
|
+
when Proc
|
56
|
+
reject_if.call(attrs)
|
57
|
+
else
|
58
|
+
false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
assoc_class =
|
63
|
+
class_name&.constantize || association.to_s.classify.constantize
|
64
|
+
|
65
|
+
define_method(:"#{association}_attributes=") do |attributes|
|
66
|
+
result =
|
67
|
+
case attributes
|
68
|
+
when Hash
|
69
|
+
attrs = attributes.except(:_destroy)
|
70
|
+
unless should_destroy.call(attributes[:_destroy]) ||
|
71
|
+
should_reject.call(attrs)
|
72
|
+
assoc_class.new(attrs)
|
73
|
+
end
|
74
|
+
when Array
|
75
|
+
attributes.filter_map do |attrs|
|
76
|
+
unless should_destroy.call(attrs[:_destroy]) ||
|
77
|
+
should_reject.call(attrs)
|
78
|
+
assoc_class.new(attrs.except(:_destroy))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
send(:"#{association}=", result)
|
84
|
+
end
|
85
|
+
|
86
|
+
define_method(:"#{association}_attributes") do
|
87
|
+
Array(send(association)).map(&:attributes)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -46,16 +46,20 @@ module Plutonium
|
|
46
46
|
@resource_record = resource_class.new resource_params
|
47
47
|
|
48
48
|
respond_to do |format|
|
49
|
-
if
|
49
|
+
if params[:pre_submit]
|
50
|
+
format.html { render :new, status: :unprocessable_entity }
|
51
|
+
elsif resource_record!.save
|
50
52
|
format.html do
|
51
53
|
redirect_to redirect_url_after_submit,
|
52
54
|
notice: "#{resource_class.model_name.human} was successfully created."
|
53
55
|
end
|
54
|
-
format.any
|
55
|
-
|
56
|
-
|
57
|
-
|
56
|
+
format.any do
|
57
|
+
render :show,
|
58
|
+
status: :created,
|
59
|
+
location: redirect_url_after_submit
|
58
60
|
end
|
61
|
+
else
|
62
|
+
format.html { render :new, status: :unprocessable_entity }
|
59
63
|
format.any do
|
60
64
|
@errors = resource_record!.errors
|
61
65
|
render "errors", status: :unprocessable_entity
|
@@ -79,17 +83,23 @@ module Plutonium
|
|
79
83
|
authorize_current! resource_record!
|
80
84
|
set_page_title "Update #{resource_record!.to_label.titleize}"
|
81
85
|
|
86
|
+
resource_record!.attributes = resource_params
|
87
|
+
|
82
88
|
respond_to do |format|
|
83
|
-
if
|
89
|
+
if params[:pre_submit]
|
90
|
+
format.html { render :edit, status: :unprocessable_entity }
|
91
|
+
elsif resource_record!.save
|
84
92
|
format.html do
|
85
|
-
redirect_to redirect_url_after_submit,
|
93
|
+
redirect_to redirect_url_after_submit,
|
94
|
+
notice:
|
95
|
+
"#{resource_class.model_name.human} was successfully updated.",
|
86
96
|
status: :see_other
|
87
97
|
end
|
88
|
-
format.any
|
89
|
-
|
90
|
-
format.html do
|
91
|
-
render :edit, status: :unprocessable_entity
|
98
|
+
format.any do
|
99
|
+
render :show, status: :ok, location: redirect_url_after_submit
|
92
100
|
end
|
101
|
+
else
|
102
|
+
format.html { render :edit, status: :unprocessable_entity }
|
93
103
|
format.any do
|
94
104
|
@errors = resource_record!.errors
|
95
105
|
render "errors", status: :unprocessable_entity
|
@@ -107,17 +117,21 @@ module Plutonium
|
|
107
117
|
|
108
118
|
format.html do
|
109
119
|
redirect_to redirect_url_after_destroy,
|
110
|
-
notice:
|
120
|
+
notice:
|
121
|
+
"#{resource_class.model_name.human} was successfully deleted."
|
111
122
|
end
|
112
123
|
format.json { head :no_content }
|
113
124
|
rescue ActiveRecord::InvalidForeignKey
|
114
125
|
format.html do
|
115
126
|
redirect_to resource_url_for(resource_record!),
|
116
|
-
alert:
|
127
|
+
alert:
|
128
|
+
"#{resource_class.model_name.human} is referenced by other records."
|
117
129
|
end
|
118
130
|
format.any do
|
119
131
|
@errors = ActiveModel::Errors.new resource_record!
|
120
|
-
@errors.add :base,
|
132
|
+
@errors.add :base,
|
133
|
+
:existing_references,
|
134
|
+
message: "is referenced by other records"
|
121
135
|
|
122
136
|
render "errors", status: :unprocessable_entity
|
123
137
|
end
|
@@ -38,12 +38,24 @@ module Plutonium
|
|
38
38
|
def commit_interactive_record_action
|
39
39
|
build_interactive_record_action_interaction
|
40
40
|
|
41
|
+
if params[:pre_submit]
|
42
|
+
respond_to do |format|
|
43
|
+
format.html do
|
44
|
+
render :interactive_record_action, status: :unprocessable_entity
|
45
|
+
end
|
46
|
+
end
|
47
|
+
return
|
48
|
+
end
|
49
|
+
|
41
50
|
outcome = @interaction.call
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
51
|
+
|
52
|
+
outcome.to_response.process(self) do |value|
|
53
|
+
respond_to do |format|
|
54
|
+
if outcome.success?
|
55
|
+
return_url = redirect_url_after_action_on(resource_class)
|
56
|
+
|
46
57
|
format.any { redirect_to return_url, status: :see_other }
|
58
|
+
|
47
59
|
if helpers.current_turbo_frame == "modal"
|
48
60
|
format.turbo_stream do
|
49
61
|
render turbo_stream: [
|
@@ -51,18 +63,16 @@ module Plutonium
|
|
51
63
|
]
|
52
64
|
end
|
53
65
|
end
|
54
|
-
|
55
|
-
end
|
56
|
-
else
|
57
|
-
outcome.to_response.process(self) do
|
58
|
-
respond_to do |format|
|
66
|
+
else
|
59
67
|
format.html do
|
60
68
|
render :interactive_record_action, status: :unprocessable_entity
|
61
69
|
end
|
70
|
+
|
62
71
|
format.any do
|
63
72
|
@errors = @interaction.errors
|
64
73
|
render "errors", status: :unprocessable_entity
|
65
74
|
end
|
75
|
+
|
66
76
|
if helpers.current_turbo_frame == "modal"
|
67
77
|
format.turbo_stream do
|
68
78
|
render turbo_stream: [
|
@@ -92,12 +102,24 @@ module Plutonium
|
|
92
102
|
skip_verify_current_authorized_scope!
|
93
103
|
build_interactive_resource_action_interaction
|
94
104
|
|
105
|
+
if params[:pre_submit]
|
106
|
+
respond_to do |format|
|
107
|
+
format.html do
|
108
|
+
render :interactive_resource_action, status: :unprocessable_entity
|
109
|
+
end
|
110
|
+
end
|
111
|
+
return
|
112
|
+
end
|
113
|
+
|
95
114
|
outcome = @interaction.call
|
96
|
-
|
97
|
-
|
98
|
-
|
115
|
+
|
116
|
+
outcome.to_response.process(self) do |value|
|
117
|
+
respond_to do |format|
|
118
|
+
if outcome.success?
|
99
119
|
return_url = redirect_url_after_action_on(resource_class)
|
120
|
+
|
100
121
|
format.any { redirect_to return_url, status: :see_other }
|
122
|
+
|
101
123
|
if helpers.current_turbo_frame == "modal"
|
102
124
|
format.turbo_stream do
|
103
125
|
render turbo_stream: [
|
@@ -105,18 +127,16 @@ module Plutonium
|
|
105
127
|
]
|
106
128
|
end
|
107
129
|
end
|
108
|
-
|
109
|
-
end
|
110
|
-
else
|
111
|
-
outcome.to_response.process(self) do
|
112
|
-
respond_to do |format|
|
130
|
+
else
|
113
131
|
format.html do
|
114
|
-
render :
|
132
|
+
render :interactive_resource_action, status: :unprocessable_entity
|
115
133
|
end
|
134
|
+
|
116
135
|
format.any do
|
117
136
|
@errors = @interaction.errors
|
118
137
|
render "errors", status: :unprocessable_entity
|
119
138
|
end
|
139
|
+
|
120
140
|
if helpers.current_turbo_frame == "modal"
|
121
141
|
format.turbo_stream do
|
122
142
|
render turbo_stream: [
|
@@ -59,11 +59,11 @@ module Plutonium
|
|
59
59
|
def query_based_on_association(assoc, record)
|
60
60
|
case assoc.macro
|
61
61
|
when :has_one
|
62
|
-
joins(assoc.name).where(assoc.name => {record.class.primary_key => record
|
62
|
+
joins(assoc.name).where(assoc.name => {record.class.primary_key => record})
|
63
63
|
when :belongs_to
|
64
64
|
where(assoc.name => record)
|
65
65
|
when :has_many
|
66
|
-
joins(assoc.name).where(assoc.
|
66
|
+
joins(assoc.name).where(assoc.name => {record.class.primary_key => record})
|
67
67
|
else
|
68
68
|
raise NotImplementedError, "associated_with->##{assoc.macro}"
|
69
69
|
end
|
@@ -8,6 +8,8 @@ module Plutonium
|
|
8
8
|
|
9
9
|
attr_reader :resource_fields, :resource_definition
|
10
10
|
|
11
|
+
alias_method :record, :object
|
12
|
+
|
11
13
|
def initialize(*, resource_fields:, resource_definition:, **, &)
|
12
14
|
super(*, **, &)
|
13
15
|
@resource_fields = resource_fields
|
@@ -59,12 +61,23 @@ module Plutonium
|
|
59
61
|
input_definition = definition.defined_inputs[name] || {}
|
60
62
|
input_options = input_definition[:options] || {}
|
61
63
|
|
64
|
+
condition = input_options[:condition] || field_options[:condition]
|
65
|
+
return if condition && !instance_exec(&condition)
|
66
|
+
|
62
67
|
tag = input_options[:as] || field_options[:as]
|
63
|
-
tag_attributes =
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
+
tag_attributes =
|
69
|
+
input_options.except(:wrapper, :as, :pre_submit, :condition)
|
70
|
+
if input_options[:pre_submit]
|
71
|
+
tag_attributes[
|
72
|
+
"data-action"
|
73
|
+
] = "change->form#preSubmit"
|
74
|
+
end
|
75
|
+
tag_block =
|
76
|
+
input_definition[:block] ||
|
77
|
+
->(f) do
|
78
|
+
tag ||= f.inferred_field_component
|
79
|
+
f.send(:"#{tag}_tag", **tag_attributes)
|
80
|
+
end
|
68
81
|
|
69
82
|
wrapper_options = input_options[:wrapper] || {}
|
70
83
|
if !wrapper_options[:class] || !wrapper_options[:class].include?("col-span")
|
@@ -73,8 +86,10 @@ module Plutonium
|
|
73
86
|
wrapper_options[:class] = tokens("col-span-full", wrapper_options[:class])
|
74
87
|
end
|
75
88
|
|
76
|
-
field_options = field_options.except(:as)
|
77
|
-
render form.field(name, **field_options).wrapped(
|
89
|
+
field_options = field_options.except(:as, :condition)
|
90
|
+
render form.field(name, **field_options).wrapped(
|
91
|
+
**wrapper_options
|
92
|
+
) do |f|
|
78
93
|
render instance_exec(f, &tag_block)
|
79
94
|
end
|
80
95
|
end
|
@@ -37,7 +37,7 @@ module Plutonium
|
|
37
37
|
# file
|
38
38
|
# file: "w-full border rounded-md shadow-sm font-medium text-sm dark:bg-gray-700 focus:outline-none",
|
39
39
|
# hint themes
|
40
|
-
hint: "mt-2 text-sm text-gray-500 dark:text-gray-200",
|
40
|
+
hint: "mt-2 text-sm text-gray-500 dark:text-gray-200 whitespace-pre",
|
41
41
|
# error themes
|
42
42
|
error: "mt-2 text-sm text-red-600 dark:text-red-500",
|
43
43
|
# button themes
|
data/lib/plutonium/version.rb
CHANGED
data/package.json
CHANGED
@@ -1,11 +1,27 @@
|
|
1
1
|
import { Controller } from "@hotwired/stimulus"
|
2
|
-
import debounce from "lodash.debounce";
|
3
2
|
|
4
3
|
// Connects to data-controller="form"
|
5
4
|
export default class extends Controller {
|
6
5
|
connect() {
|
7
6
|
}
|
7
|
+
|
8
|
+
preSubmit() {
|
9
|
+
// Create a hidden input field
|
10
|
+
const hiddenField = document.createElement('input');
|
11
|
+
hiddenField.type = 'hidden';
|
12
|
+
hiddenField.name = 'pre_submit';
|
13
|
+
hiddenField.value = 'true';
|
14
|
+
|
15
|
+
// Append it to the form
|
16
|
+
this.element.appendChild(hiddenField);
|
8
17
|
|
18
|
+
// Skip validation by setting novalidate attribute
|
19
|
+
this.element.setAttribute('novalidate', '');
|
20
|
+
|
21
|
+
// Submit the form
|
22
|
+
this.submit();
|
23
|
+
}
|
24
|
+
|
9
25
|
submit() {
|
10
26
|
this.element.requestSubmit()
|
11
27
|
}
|
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.21.
|
4
|
+
version: 0.21.1
|
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-04-
|
11
|
+
date: 2025-04-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: zeitwerk
|
@@ -750,6 +750,7 @@ files:
|
|
750
750
|
- lib/plutonium/interaction/README.md
|
751
751
|
- lib/plutonium/interaction/base.rb
|
752
752
|
- lib/plutonium/interaction/concerns/workflow_dsl.rb
|
753
|
+
- lib/plutonium/interaction/nested_attributes.rb
|
753
754
|
- lib/plutonium/interaction/outcome.rb
|
754
755
|
- lib/plutonium/interaction/response/base.rb
|
755
756
|
- lib/plutonium/interaction/response/failure.rb
|