active_interaction 4.0.5 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Version](https://img.shields.io/gem/v/active_interaction.svg?style=flat-square)](https://rubygems.org/gems/active_interaction)
|
7
7
|
[![Test](https://img.shields.io/github/workflow/status/AaronLasseigne/active_interaction/Test?label=Test&style=flat-square)](https://github.com/AaronLasseigne/active_interaction/actions?query=workflow%3ATest)
|
8
|
-
[![Climate](https://img.shields.io/codeclimate/maintainability/orgsync/active_interaction.svg?style=flat-square)](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
|