active_interaction 4.0.5 → 5.0.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/CHANGELOG.md +149 -6
- data/README.md +67 -32
- data/lib/active_interaction/array_input.rb +77 -0
- data/lib/active_interaction/base.rb +14 -98
- data/lib/active_interaction/concerns/active_recordable.rb +3 -3
- data/lib/active_interaction/concerns/missable.rb +2 -2
- data/lib/active_interaction/errors.rb +6 -88
- data/lib/active_interaction/exceptions.rb +47 -0
- data/lib/active_interaction/filter/column.rb +59 -0
- data/lib/active_interaction/filter/error.rb +40 -0
- data/lib/active_interaction/filter.rb +44 -53
- data/lib/active_interaction/filters/abstract_date_time_filter.rb +9 -6
- data/lib/active_interaction/filters/abstract_numeric_filter.rb +7 -3
- data/lib/active_interaction/filters/array_filter.rb +36 -10
- data/lib/active_interaction/filters/boolean_filter.rb +4 -3
- data/lib/active_interaction/filters/date_filter.rb +1 -1
- data/lib/active_interaction/filters/date_time_filter.rb +1 -1
- data/lib/active_interaction/filters/decimal_filter.rb +1 -1
- data/lib/active_interaction/filters/float_filter.rb +1 -1
- data/lib/active_interaction/filters/hash_filter.rb +23 -15
- data/lib/active_interaction/filters/integer_filter.rb +1 -1
- data/lib/active_interaction/filters/interface_filter.rb +12 -12
- data/lib/active_interaction/filters/object_filter.rb +9 -3
- data/lib/active_interaction/filters/record_filter.rb +21 -11
- data/lib/active_interaction/filters/string_filter.rb +1 -1
- data/lib/active_interaction/filters/symbol_filter.rb +1 -1
- data/lib/active_interaction/filters/time_filter.rb +4 -4
- data/lib/active_interaction/hash_input.rb +43 -0
- data/lib/active_interaction/input.rb +23 -0
- data/lib/active_interaction/inputs.rb +157 -46
- data/lib/active_interaction/locale/en.yml +0 -1
- data/lib/active_interaction/locale/fr.yml +0 -1
- data/lib/active_interaction/locale/it.yml +0 -1
- data/lib/active_interaction/locale/ja.yml +0 -1
- data/lib/active_interaction/locale/pt-BR.yml +0 -1
- data/lib/active_interaction/modules/validation.rb +6 -17
- data/lib/active_interaction/version.rb +1 -1
- data/lib/active_interaction.rb +43 -36
- data/spec/active_interaction/array_input_spec.rb +166 -0
- data/spec/active_interaction/base_spec.rb +15 -240
- data/spec/active_interaction/concerns/active_modelable_spec.rb +3 -3
- data/spec/active_interaction/concerns/active_recordable_spec.rb +7 -7
- data/spec/active_interaction/concerns/hashable_spec.rb +8 -8
- data/spec/active_interaction/concerns/missable_spec.rb +9 -9
- data/spec/active_interaction/concerns/runnable_spec.rb +34 -32
- data/spec/active_interaction/errors_spec.rb +60 -43
- data/spec/active_interaction/{filter_column_spec.rb → filter/column_spec.rb} +3 -10
- data/spec/active_interaction/filter_spec.rb +6 -6
- data/spec/active_interaction/filters/abstract_date_time_filter_spec.rb +2 -2
- data/spec/active_interaction/filters/abstract_numeric_filter_spec.rb +2 -2
- data/spec/active_interaction/filters/array_filter_spec.rb +99 -24
- data/spec/active_interaction/filters/boolean_filter_spec.rb +12 -11
- data/spec/active_interaction/filters/date_filter_spec.rb +32 -27
- data/spec/active_interaction/filters/date_time_filter_spec.rb +34 -29
- data/spec/active_interaction/filters/decimal_filter_spec.rb +20 -18
- data/spec/active_interaction/filters/file_filter_spec.rb +7 -7
- data/spec/active_interaction/filters/float_filter_spec.rb +19 -17
- data/spec/active_interaction/filters/hash_filter_spec.rb +16 -18
- data/spec/active_interaction/filters/integer_filter_spec.rb +24 -22
- data/spec/active_interaction/filters/interface_filter_spec.rb +105 -82
- data/spec/active_interaction/filters/object_filter_spec.rb +52 -36
- data/spec/active_interaction/filters/record_filter_spec.rb +61 -39
- data/spec/active_interaction/filters/string_filter_spec.rb +7 -7
- data/spec/active_interaction/filters/symbol_filter_spec.rb +6 -6
- data/spec/active_interaction/filters/time_filter_spec.rb +57 -34
- data/spec/active_interaction/hash_input_spec.rb +58 -0
- data/spec/active_interaction/i18n_spec.rb +22 -17
- data/spec/active_interaction/inputs_spec.rb +167 -23
- data/spec/active_interaction/integration/array_interaction_spec.rb +3 -7
- data/spec/active_interaction/modules/validation_spec.rb +8 -31
- data/spec/spec_helper.rb +8 -0
- data/spec/support/concerns.rb +2 -2
- data/spec/support/filters.rb +27 -51
- data/spec/support/interactions.rb +4 -4
- metadata +45 -95
- data/lib/active_interaction/filter_column.rb +0 -57
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0cfed756a5463384a07d694ba40f14067dce231d2618af2f16e6fd429e31b97f
|
4
|
+
data.tar.gz: eb3b185d234762dec1799fbaa6e8acf290a47e0940b24c3da529f11172e6fde5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2ce2d6ef28bc84e2cc614dd9e6dde57c14a92efcafe706e8cb532b1c0fa9c192db8548771b94198236f3ab3ec6baa9633c2625c3f8b73cbcc1291752078be6c
|
7
|
+
data.tar.gz: 3f5f3c15059c19c7fec77256a260fc67f98444ab5dba48048f2797a441a348f7deea31b7b558e0b1af334ffd8a6e1685756745ecebdc87ee658dafc42f8043c4
|
data/CHANGELOG.md
CHANGED
@@ -1,32 +1,169 @@
|
|
1
|
+
# [5.0.0][] (2022-06-24)
|
2
|
+
|
3
|
+
## Changed
|
4
|
+
|
5
|
+
- Drop support for JRuby.
|
6
|
+
- Drop support for Ruby 2.5 and 2.6, adding support for 3.1
|
7
|
+
- Drop support for Rails 5.0 and 5.1
|
8
|
+
- `ActiveInteraction::Inputs` no longer inherits from `Hash` though it still has most of the methods
|
9
|
+
provided by `Hash` (methods that write were removed).
|
10
|
+
- Removed `Filter#clean` (use `Filter#process` and call `#value` on the result)
|
11
|
+
- The `given?` method has been moved onto `inputs`. ([how to upgrade](#given))
|
12
|
+
- [#503][] - The record filter now treats blank strings value as `nil`. This was missed in the 4.0 update.
|
13
|
+
- The `type_check` callback has been renamed to `filter` to better match the reality of what it does.
|
14
|
+
([how to upgrade](#filter-callback))
|
15
|
+
- `ActiveIneraction::FilterColumn` is now `ActiveInteraction::Filter::Column`
|
16
|
+
- Errors on the array filter will now be indexed if the Rails config `index_nested_attribute_errors`
|
17
|
+
is `true` or the `:index_errors` option is set to `true`. The `:index_errors` option always overrides
|
18
|
+
the Rails config.
|
19
|
+
- Invalid nested errors (`:invalid_nested`) are gone. Instead the nested errors will appear as they would
|
20
|
+
in Rails if they were a `has_many` relationship being assigned attributes through a parent.
|
21
|
+
([how to upgrade](#nested-hash-errors))
|
22
|
+
|
23
|
+
## Added
|
24
|
+
|
25
|
+
- `Filter#process` which returns an `Input`.
|
26
|
+
|
27
|
+
## Fixed
|
28
|
+
|
29
|
+
- When passing an `ActiveRecord::Relation` in an array filter with no inner
|
30
|
+
filter, the value returned was an `ActiveRecord::Relation` instead of an
|
31
|
+
Array.
|
32
|
+
|
33
|
+
## Upgrading
|
34
|
+
|
35
|
+
### `given?`
|
36
|
+
|
37
|
+
The `given?` method can now be found on `inputs`. It works the same as before.
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
# 4.1
|
41
|
+
class Example < ActiveInteraction::Base
|
42
|
+
string :name, default: nil
|
43
|
+
|
44
|
+
def execute
|
45
|
+
given?(:name)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# 5.0
|
50
|
+
class Example < ActiveInteraction::Base
|
51
|
+
string :name, default: nil
|
52
|
+
|
53
|
+
def execute
|
54
|
+
inputs.given?(:name)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
### Filter Callback
|
60
|
+
|
61
|
+
You'll need to rename any `:type_check` callbacks to `:filter`.
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
# 4.1
|
65
|
+
set_callback :type_check, :before, -> { puts 'before type check' }
|
66
|
+
|
67
|
+
# 5.0
|
68
|
+
set_callback :filter, :before, -> { puts 'before type check' }
|
69
|
+
```
|
70
|
+
|
71
|
+
### Nested Hash Errors
|
72
|
+
|
73
|
+
Nested hash errors no longer add an error as through it happened on the hash.
|
74
|
+
They now use the error in its original form and attach the name of the hash to
|
75
|
+
the error. It is also not limited to returning one error.
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
class HashInteraction < ActiveInteraction::Base
|
79
|
+
hash :mailing_lists do
|
80
|
+
boolean :marketing
|
81
|
+
boolean :product_updates
|
82
|
+
end
|
83
|
+
|
84
|
+
def execute
|
85
|
+
# ...
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
> outcome = HashInteraction.run(mailing_lists: {})
|
90
|
+
|
91
|
+
# 4.1
|
92
|
+
> outcome.errors.details
|
93
|
+
# => {:mailing_lists=>[{:error=>:invalid_nested, :name=>"\"marketing\"", :value=>"nil"}]},
|
94
|
+
> outcome.errors.messages
|
95
|
+
# => {:mailing_lists=>["has an invalid nested value (\"marketing\" => nil)"]}
|
96
|
+
> outcome.errors.full_messages
|
97
|
+
# => ["Mailing lists has an invalid nested value (\"marketing\" => nil)"]
|
98
|
+
|
99
|
+
# 5.0
|
100
|
+
> outcome.errors.details
|
101
|
+
# => {:"mailing_lists.marketing"=>[{:error=>:missing}], :"mailing_lists.product_updates"=>[{:error=>:missing}]}
|
102
|
+
> outcome.errors.messages
|
103
|
+
# => {:"mailing_lists.marketing"=>["is required"], :"mailing_lists.product_updates"=>["is required"]}
|
104
|
+
> outcome.errors.full_messages
|
105
|
+
# => ["Mailing lists marketing is required", "Mailing lists product updates is required"]
|
106
|
+
```
|
107
|
+
|
108
|
+
I18n can handle these values the same as nested values in Rails:
|
109
|
+
|
110
|
+
```yml
|
111
|
+
en:
|
112
|
+
active_interaction:
|
113
|
+
attributes:
|
114
|
+
hash_interaction/mailing_lists:
|
115
|
+
marketing: 'Mailing list "Marketing"'
|
116
|
+
product_updates: 'Mailing list "Product Updates"'
|
117
|
+
```
|
118
|
+
|
119
|
+
Using the same example from above:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
> outcome.errors.full_messages
|
123
|
+
# => ["Mailing list \"Marketing\" is required", "Mailing list \"Product Updates\" is required"]
|
124
|
+
```
|
125
|
+
|
126
|
+
# [4.1.0][] (2021-12-30)
|
127
|
+
|
128
|
+
## Added
|
129
|
+
|
130
|
+
- [#518][] - Add Rails 7 support
|
131
|
+
|
132
|
+
# [4.0.6][] (2021-10-13)
|
133
|
+
|
134
|
+
## Fixed
|
135
|
+
|
136
|
+
- [#515][] - Filters nested in arrays should accept default values as indicated in the documentation.
|
137
|
+
|
1
138
|
# [4.0.5][] (2021-07-11)
|
2
139
|
|
3
|
-
##
|
140
|
+
## Fixed
|
4
141
|
|
5
142
|
- [#480][] - Interfaces used inside hashes failed to recognize `nil` as a non-value.
|
6
143
|
|
7
144
|
# [4.0.4][] (2021-07-03)
|
8
145
|
|
9
|
-
##
|
146
|
+
## Fixed
|
10
147
|
|
11
148
|
- [#510][] - Hash parameters failed when working outside of Rails.
|
12
149
|
- [#511][] - Nested filters with options but no `:class` failed to have `:class` automatically added.
|
13
150
|
|
14
151
|
# [4.0.3][] (2021-06-24)
|
15
152
|
|
16
|
-
##
|
153
|
+
## Fixed
|
17
154
|
|
18
155
|
- [#499][] - `given?` now recognizes multi-part date inputs by their primary key name
|
19
156
|
- [#493][] - `compose` now properly accepts `Inputs`
|
20
157
|
|
21
158
|
# [4.0.2][] (2021-06-22)
|
22
159
|
|
23
|
-
##
|
160
|
+
## Fixed
|
24
161
|
|
25
162
|
- [#505][] - Nested Interface filters using the `:methods` option threw an error.
|
26
163
|
|
27
164
|
# [4.0.1][] (2021-05-26)
|
28
165
|
|
29
|
-
##
|
166
|
+
## Fixed
|
30
167
|
|
31
168
|
- Fix regression of filter name relaxing.
|
32
169
|
- [#495][] - Fix time filter ignoring time zones
|
@@ -56,7 +193,7 @@
|
|
56
193
|
|
57
194
|
## Added
|
58
195
|
|
59
|
-
- Implicit coercion of types are now supported in filters (e.g. to_str
|
196
|
+
- Implicit coercion of types are now supported in filters (e.g. `to_str`, `to_int`,
|
60
197
|
etc).
|
61
198
|
- The `interface` and `record` filters, when used as an inner filter for an
|
62
199
|
`array`, will have their `from/class` option set to a singularized version of
|
@@ -964,6 +1101,9 @@ Example.run
|
|
964
1101
|
|
965
1102
|
- Initial release.
|
966
1103
|
|
1104
|
+
[5.0.0]: https://github.com/AaronLasseigne/active_interaction/compare/v4.1.0...v5.0.0
|
1105
|
+
[4.1.0]: https://github.com/AaronLasseigne/active_interaction/compare/v4.0.6...v4.1.0
|
1106
|
+
[4.0.6]: https://github.com/AaronLasseigne/active_interaction/compare/v4.0.5...v4.0.6
|
967
1107
|
[4.0.5]: https://github.com/AaronLasseigne/active_interaction/compare/v4.0.4...v4.0.5
|
968
1108
|
[4.0.4]: https://github.com/AaronLasseigne/active_interaction/compare/v4.0.3...v4.0.4
|
969
1109
|
[4.0.3]: https://github.com/AaronLasseigne/active_interaction/compare/v4.0.2...v4.0.3
|
@@ -1182,3 +1322,6 @@ Example.run
|
|
1182
1322
|
[#511]: https://github.com/AaronLasseigne/active_interaction/issues/511
|
1183
1323
|
[#412]: https://github.com/AaronLasseigne/active_interaction/issues/412
|
1184
1324
|
[#480]: https://github.com/AaronLasseigne/active_interaction/issues/480
|
1325
|
+
[#515]: https://github.com/AaronLasseigne/active_interaction/issues/515
|
1326
|
+
[#518]: https://github.com/AaronLasseigne/active_interaction/issues/518
|
1327
|
+
[#503]: https://github.com/AaronLasseigne/active_interaction/issues/503
|
data/README.md
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
# [ActiveInteraction][]
|
2
2
|
|
3
3
|
ActiveInteraction manages application-specific business logic.
|
4
|
-
It's an implementation of
|
4
|
+
It's an implementation of service objects designed to blend seamlessly into Rails.
|
5
5
|
|
6
6
|
[](https://rubygems.org/gems/active_interaction)
|
7
7
|
[](https://github.com/AaronLasseigne/active_interaction/actions?query=workflow%3ATest)
|
8
|
-
[](https://codeclimate.com/github/orgsync/active_interaction)
|
9
8
|
|
10
9
|
---
|
11
10
|
|
@@ -52,7 +51,7 @@ handles your verbs.
|
|
52
51
|
- [Descriptions](#descriptions)
|
53
52
|
- [Errors](#errors)
|
54
53
|
- [Forms](#forms)
|
55
|
-
- [
|
54
|
+
- [Shared input options](#shared-input-options)
|
56
55
|
- [Optional inputs](#optional-inputs)
|
57
56
|
- [Translations](#translations)
|
58
57
|
- [Credits](#credits)
|
@@ -64,18 +63,17 @@ handles your verbs.
|
|
64
63
|
Add it to your Gemfile:
|
65
64
|
|
66
65
|
``` rb
|
67
|
-
gem 'active_interaction', '~>
|
66
|
+
gem 'active_interaction', '~> 5.0'
|
68
67
|
```
|
69
68
|
|
70
69
|
Or install it manually:
|
71
70
|
|
72
71
|
``` sh
|
73
|
-
$ gem install active_interaction --version '~>
|
72
|
+
$ gem install active_interaction --version '~> 5.0'
|
74
73
|
```
|
75
74
|
|
76
75
|
This project uses [Semantic Versioning][]. Check out [GitHub releases][] for a
|
77
|
-
detailed list of changes.
|
78
|
-
announcement post][].
|
76
|
+
detailed list of changes.
|
79
77
|
|
80
78
|
## Basic usage
|
81
79
|
|
@@ -90,7 +88,7 @@ you need to do two things:
|
|
90
88
|
2. **Define your business logic.** Do this by implementing the `#execute`
|
91
89
|
method. Each input you defined will be available as the type you specified.
|
92
90
|
If any of the inputs are invalid, `#execute` won't be run. Filters are
|
93
|
-
responsible for
|
91
|
+
responsible for checking your inputs. Check out [the validations
|
94
92
|
section](#validations) if you need more than that.
|
95
93
|
|
96
94
|
That covers the basics. Let's put it all together into a simple example that
|
@@ -142,7 +140,7 @@ Square.run!(x: 2.1)
|
|
142
140
|
|
143
141
|
### Validations
|
144
142
|
|
145
|
-
ActiveInteraction
|
143
|
+
ActiveInteraction checks your inputs. Often you'll want more than that.
|
146
144
|
For instance, you may want an input to be a string with at least one
|
147
145
|
non-whitespace character. Instead of writing your own validation for that, you
|
148
146
|
can use validations from ActiveModel.
|
@@ -165,7 +163,7 @@ end
|
|
165
163
|
```
|
166
164
|
|
167
165
|
When you run this interaction, two things will happen. **First
|
168
|
-
ActiveInteraction will
|
166
|
+
ActiveInteraction will check your inputs. Then ActiveModel will validate
|
169
167
|
them.** If both of those are happy, it will be executed.
|
170
168
|
|
171
169
|
``` rb
|
@@ -262,6 +260,32 @@ array :managers do
|
|
262
260
|
end
|
263
261
|
```
|
264
262
|
|
263
|
+
Errors that occur will be indexed based on the Rails configuration setting
|
264
|
+
`index_nested_attribute_errors`. You can also manually override this setting
|
265
|
+
with the `:index_errors` option. In this state is is possible to get multiple
|
266
|
+
errors from a single filter.
|
267
|
+
|
268
|
+
```ruby
|
269
|
+
class ArrayInteraction < ActiveInteraction::Base
|
270
|
+
array :favorite_numbers, index_errors: true do
|
271
|
+
integer
|
272
|
+
end
|
273
|
+
|
274
|
+
def execute
|
275
|
+
favorite_numbers
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
ArrayInteraction.run(favorite_numbers: [8, 'bazillion']).errors.details
|
280
|
+
=> {:"favorite_numbers[1]"=>[{:error=>:invalid_type, :type=>"array"}]}
|
281
|
+
```
|
282
|
+
|
283
|
+
With `:index_errors` set to `false` the error would have been:
|
284
|
+
|
285
|
+
```ruby
|
286
|
+
{:favorite_numbers=>[{:error=>:invalid_type, :type=>"array"}]}
|
287
|
+
```
|
288
|
+
|
265
289
|
### Boolean
|
266
290
|
|
267
291
|
Boolean filters convert the strings `"1"`, `"true"`, and `"on"`
|
@@ -465,8 +489,8 @@ not an instance of the class or one of its subclasses. The `converter` option
|
|
465
489
|
accepts a symbol that specifies a class method on the object class or a proc.
|
466
490
|
Both will be passed the value and any errors thrown inside the converter will
|
467
491
|
cause the value to be considered invalid. Any returned value that is not the
|
468
|
-
correct class will also be treated as invalid.
|
469
|
-
|
492
|
+
correct class will also be treated as invalid. Any `default` that is not an
|
493
|
+
instance of the class or subclass and is not `nil` will also be converted.
|
470
494
|
|
471
495
|
``` rb
|
472
496
|
class ObjectInteraction < ActiveInteraction::Base
|
@@ -493,8 +517,9 @@ of its subclasses) or a value that can be used to locate an instance of the
|
|
493
517
|
object. If the value does not match, it will call `find` on the class of the
|
494
518
|
record. This is particularly useful when working with ActiveRecord objects.
|
495
519
|
Like an object filter, the class is derived from the name passed but can be
|
496
|
-
specified with the `class` option.
|
497
|
-
also be found.
|
520
|
+
specified with the `class` option. Any `default` that is not an instance of the
|
521
|
+
class or subclass and is not `nil` will also be found. Blank strings passed in
|
522
|
+
will be treated as `nil`.
|
498
523
|
|
499
524
|
``` rb
|
500
525
|
class RecordInteraction < ActiveInteraction::Base
|
@@ -737,10 +762,7 @@ resourceful actions.
|
|
737
762
|
|
738
763
|
We recommend putting your interactions in `app/interactions`. It's also very
|
739
764
|
helpful to group them by model. That way you can look in
|
740
|
-
`app/interactions/accounts` for all the ways you can interact with accounts.
|
741
|
-
order to use this structure add
|
742
|
-
`config.autoload_paths += Dir.glob("#{config.root}/app/interactions/*")` in
|
743
|
-
your `application.rb`
|
765
|
+
`app/interactions/accounts` for all the ways you can interact with accounts.
|
744
766
|
|
745
767
|
```
|
746
768
|
- app/
|
@@ -843,7 +865,7 @@ end
|
|
843
865
|
```
|
844
866
|
|
845
867
|
Note that it's perfectly fine to add errors during execution. Not all errors
|
846
|
-
have to come from
|
868
|
+
have to come from checking or validation.
|
847
869
|
|
848
870
|
#### New
|
849
871
|
|
@@ -1020,13 +1042,13 @@ end
|
|
1020
1042
|
|
1021
1043
|
### Callbacks
|
1022
1044
|
|
1023
|
-
|
1024
|
-
ActiveInteraction
|
1025
|
-
|
1045
|
+
[ActiveSupport::Callbacks][] provides a powerful framework for defining callbacks.
|
1046
|
+
ActiveInteraction uses that framework to allow hooking into various parts of an
|
1047
|
+
interaction's lifecycle.
|
1026
1048
|
|
1027
1049
|
``` rb
|
1028
1050
|
class Increment < ActiveInteraction::Base
|
1029
|
-
set_callback :
|
1051
|
+
set_callback :filter, :before, -> { puts 'before filter' }
|
1030
1052
|
|
1031
1053
|
integer :x
|
1032
1054
|
|
@@ -1048,7 +1070,7 @@ class Increment < ActiveInteraction::Base
|
|
1048
1070
|
end
|
1049
1071
|
|
1050
1072
|
Increment.run!(x: 1)
|
1051
|
-
# before
|
1073
|
+
# before filter
|
1052
1074
|
# after validate
|
1053
1075
|
# >>>
|
1054
1076
|
# executing
|
@@ -1056,7 +1078,7 @@ Increment.run!(x: 1)
|
|
1056
1078
|
# => 2
|
1057
1079
|
```
|
1058
1080
|
|
1059
|
-
In order, the available callbacks are `
|
1081
|
+
In order, the available callbacks are `filter`, `validate`, and `execute`.
|
1060
1082
|
You can set `before`, `after`, or `around` on any of them.
|
1061
1083
|
|
1062
1084
|
### Composition
|
@@ -1179,7 +1201,7 @@ errors.
|
|
1179
1201
|
``` rb
|
1180
1202
|
outcome = BuyItem.run(item: 'Thing', options: { gift_wrapped: 'yes' })
|
1181
1203
|
outcome.errors.messages
|
1182
|
-
# => {:credit_card=>["is required"], :item=>["is not a valid object"], :options=>["
|
1204
|
+
# => {:credit_card=>["is required"], :item=>["is not a valid object"], :"options.gift_wrapped"=>["is not a valid boolean"]}
|
1183
1205
|
```
|
1184
1206
|
|
1185
1207
|
Determining the type of error based on the string is difficult if not
|
@@ -1188,7 +1210,7 @@ the same list of errors with a testable label representing the error.
|
|
1188
1210
|
|
1189
1211
|
``` rb
|
1190
1212
|
outcome.errors.details
|
1191
|
-
# => {:credit_card=>[{:error=>:missing}], :item=>[{:type=>"object"
|
1213
|
+
# => {:credit_card=>[{:error=>:missing}], :item=>[{:error=>:invalid_type, :type=>"object"}], :"options.gift_wrapped"=>[{:error=>:invalid_type, :type=>"boolean"}]}
|
1192
1214
|
```
|
1193
1215
|
|
1194
1216
|
Detailed errors can also be manually added during the execute call by passing a
|
@@ -1338,7 +1360,7 @@ used to define the inputs on your interaction will relay type information to
|
|
1338
1360
|
these gems. As a result, form fields will automatically use the appropriate
|
1339
1361
|
input type.
|
1340
1362
|
|
1341
|
-
###
|
1363
|
+
### Shared input options
|
1342
1364
|
|
1343
1365
|
It can be convenient to apply the same options to a bunch of inputs. One common
|
1344
1366
|
use case is making many inputs optional. Instead of setting `default: nil` on
|
@@ -1359,8 +1381,8 @@ Optional inputs can be defined by using the `:default` option as described in
|
|
1359
1381
|
are merged to create `inputs`. There are times where it is useful to know
|
1360
1382
|
whether a value was passed to `run` or the result of a filter default. In
|
1361
1383
|
particular, it is useful when `nil` is an acceptable value. For example, you
|
1362
|
-
may optionally track your users' birthdays. You can use the `given?` predicate
|
1363
|
-
to see if an input was even passed to `run`. With `given?` you can also check
|
1384
|
+
may optionally track your users' birthdays. You can use the `inputs.given?` predicate
|
1385
|
+
to see if an input was even passed to `run`. With `inputs.given?` you can also check
|
1364
1386
|
the input of a hash or array filter by passing a series of keys or indexes to
|
1365
1387
|
check.
|
1366
1388
|
|
@@ -1371,7 +1393,7 @@ class UpdateUser < ActiveInteraction::Base
|
|
1371
1393
|
default: nil
|
1372
1394
|
|
1373
1395
|
def execute
|
1374
|
-
user.birthday = birthday if given?(:birthday)
|
1396
|
+
user.birthday = birthday if inputs.given?(:birthday)
|
1375
1397
|
errors.merge!(user.errors) unless user.save
|
1376
1398
|
user
|
1377
1399
|
end
|
@@ -1425,7 +1447,6 @@ hsilgne:
|
|
1425
1447
|
errors:
|
1426
1448
|
messages:
|
1427
1449
|
invalid: dilavni si
|
1428
|
-
invalid_nested: (%{value} <= %{name}) eulav detsen dilavni na sah
|
1429
1450
|
invalid_type: '%{type} dilav a ton si'
|
1430
1451
|
missing: deriuqer si
|
1431
1452
|
```
|
@@ -1445,6 +1466,19 @@ I18nInteraction.run(name: false).errors.messages[:name]
|
|
1445
1466
|
# => ["gnirts dilav a ton si"]
|
1446
1467
|
```
|
1447
1468
|
|
1469
|
+
Everything else works like an `activerecord` entry. For example, to rename an
|
1470
|
+
attribute you can use `attributes`.
|
1471
|
+
|
1472
|
+
Here we'll rename the `num` attribute on an interaction named `product`:
|
1473
|
+
|
1474
|
+
``` yml
|
1475
|
+
en:
|
1476
|
+
active_interaction:
|
1477
|
+
attributes:
|
1478
|
+
product:
|
1479
|
+
num: 'Number'
|
1480
|
+
```
|
1481
|
+
|
1448
1482
|
## Credits
|
1449
1483
|
|
1450
1484
|
ActiveInteraction is brought to you by [Aaron Lasseigne][].
|
@@ -1471,3 +1505,4 @@ ActiveInteraction is licensed under [the MIT License][].
|
|
1471
1505
|
[the errors section]: #errors
|
1472
1506
|
[the optional inputs section]: #optional-inputs
|
1473
1507
|
[`with_options`]: http://api.rubyonrails.org/classes/Object.html#method-i-with_options
|
1508
|
+
[ActiveSupport::Callbacks]: https://api.rubyonrails.org/classes/ActiveSupport/Callbacks.html
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteraction
|
4
|
+
# Represents a processed array input.
|
5
|
+
class ArrayInput < Input
|
6
|
+
# @private
|
7
|
+
def initialize(filter, value: nil, error: nil, index_errors: false, children: [])
|
8
|
+
super(filter, value: value, error: error)
|
9
|
+
|
10
|
+
@filter = filter
|
11
|
+
@index_errors = index_errors
|
12
|
+
@children = children
|
13
|
+
end
|
14
|
+
|
15
|
+
# @overload children
|
16
|
+
# Child inputs if a nested filter is used.
|
17
|
+
#
|
18
|
+
# @return [Array<Input, ArrayInput, HashInput>]
|
19
|
+
attr_reader :children
|
20
|
+
|
21
|
+
# Any errors that occurred during processing.
|
22
|
+
#
|
23
|
+
# @return [Filter::Error]
|
24
|
+
def errors
|
25
|
+
return @errors if defined?(@errors)
|
26
|
+
|
27
|
+
return @errors = super if @error
|
28
|
+
|
29
|
+
child_errors = get_errors_by_index(children)
|
30
|
+
|
31
|
+
return @errors = super if child_errors.empty?
|
32
|
+
|
33
|
+
@errors ||=
|
34
|
+
if @index_errors
|
35
|
+
child_errors.map do |(error, i)|
|
36
|
+
name = attach_child_name(:"#{@filter.name}[#{i}]", error)
|
37
|
+
Filter::Error.new(error.filter, error.type, name: name)
|
38
|
+
end.freeze
|
39
|
+
else
|
40
|
+
error, = child_errors.first
|
41
|
+
[Filter::Error.new(@filter, error.type)].freeze
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def get_errors_by_index(children)
|
48
|
+
children.flat_map.with_index do |child, i|
|
49
|
+
child.errors.map do |error|
|
50
|
+
[error, i]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def attach_child_name(name, error)
|
56
|
+
return name unless error.name.present?
|
57
|
+
|
58
|
+
if children_are_arrays?(children)
|
59
|
+
:"#{name}#{error.name.to_s.sub(/\A[^\[]*/, '')}"
|
60
|
+
elsif children_are_hashes?(children)
|
61
|
+
:"#{name}.#{error.name.to_s[1..]}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def children_are_arrays?(children)
|
66
|
+
return @children_are_arrays if defined?(@children_are_arrays)
|
67
|
+
|
68
|
+
@children_are_arrays = children.first&.is_a?(ArrayInput)
|
69
|
+
end
|
70
|
+
|
71
|
+
def children_are_hashes?(children)
|
72
|
+
return @children_are_hashes if defined?(@children_are_hashes)
|
73
|
+
|
74
|
+
@children_are_hashes = children.first&.is_a?(HashInput)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|