active_interaction 3.7.1 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +190 -0
  3. data/CONTRIBUTING.md +1 -1
  4. data/README.md +96 -90
  5. data/lib/active_interaction.rb +1 -7
  6. data/lib/active_interaction/base.rb +48 -33
  7. data/lib/active_interaction/concerns/active_modelable.rb +1 -3
  8. data/lib/active_interaction/concerns/active_recordable.rb +1 -6
  9. data/lib/active_interaction/concerns/hashable.rb +0 -1
  10. data/lib/active_interaction/concerns/missable.rb +0 -1
  11. data/lib/active_interaction/concerns/runnable.rb +16 -28
  12. data/lib/active_interaction/errors.rb +8 -7
  13. data/lib/active_interaction/filter.rb +51 -37
  14. data/lib/active_interaction/filter_column.rb +0 -3
  15. data/lib/active_interaction/filters/abstract_date_time_filter.rb +34 -36
  16. data/lib/active_interaction/filters/abstract_numeric_filter.rb +27 -17
  17. data/lib/active_interaction/filters/array_filter.rb +57 -36
  18. data/lib/active_interaction/filters/boolean_filter.rb +26 -12
  19. data/lib/active_interaction/filters/date_filter.rb +1 -2
  20. data/lib/active_interaction/filters/date_time_filter.rb +1 -2
  21. data/lib/active_interaction/filters/decimal_filter.rb +10 -28
  22. data/lib/active_interaction/filters/file_filter.rb +6 -5
  23. data/lib/active_interaction/filters/float_filter.rb +1 -2
  24. data/lib/active_interaction/filters/hash_filter.rb +37 -27
  25. data/lib/active_interaction/filters/integer_filter.rb +7 -8
  26. data/lib/active_interaction/filters/interface_filter.rb +48 -14
  27. data/lib/active_interaction/filters/object_filter.rb +23 -50
  28. data/lib/active_interaction/filters/record_filter.rb +10 -35
  29. data/lib/active_interaction/filters/string_filter.rb +21 -12
  30. data/lib/active_interaction/filters/symbol_filter.rb +13 -7
  31. data/lib/active_interaction/filters/time_filter.rb +19 -22
  32. data/lib/active_interaction/grouped_input.rb +0 -3
  33. data/lib/active_interaction/inputs.rb +89 -0
  34. data/lib/active_interaction/locale/ja.yml +24 -0
  35. data/lib/active_interaction/modules/input_processor.rb +9 -6
  36. data/lib/active_interaction/modules/validation.rb +9 -12
  37. data/lib/active_interaction/version.rb +1 -3
  38. data/spec/active_interaction/base_spec.rb +95 -35
  39. data/spec/active_interaction/concerns/active_modelable_spec.rb +0 -2
  40. data/spec/active_interaction/concerns/active_recordable_spec.rb +0 -2
  41. data/spec/active_interaction/concerns/hashable_spec.rb +1 -3
  42. data/spec/active_interaction/concerns/missable_spec.rb +0 -2
  43. data/spec/active_interaction/concerns/runnable_spec.rb +32 -12
  44. data/spec/active_interaction/errors_spec.rb +49 -22
  45. data/spec/active_interaction/filter_column_spec.rb +0 -2
  46. data/spec/active_interaction/filter_spec.rb +0 -2
  47. data/spec/active_interaction/filters/abstract_date_time_filter_spec.rb +1 -3
  48. data/spec/active_interaction/filters/abstract_numeric_filter_spec.rb +1 -3
  49. data/spec/active_interaction/filters/array_filter_spec.rb +41 -15
  50. data/spec/active_interaction/filters/boolean_filter_spec.rb +58 -4
  51. data/spec/active_interaction/filters/date_filter_spec.rb +43 -3
  52. data/spec/active_interaction/filters/date_time_filter_spec.rb +43 -3
  53. data/spec/active_interaction/filters/decimal_filter_spec.rb +57 -3
  54. data/spec/active_interaction/filters/file_filter_spec.rb +1 -3
  55. data/spec/active_interaction/filters/float_filter_spec.rb +60 -4
  56. data/spec/active_interaction/filters/hash_filter_spec.rb +19 -9
  57. data/spec/active_interaction/filters/integer_filter_spec.rb +49 -7
  58. data/spec/active_interaction/filters/interface_filter_spec.rb +397 -24
  59. data/spec/active_interaction/filters/object_filter_spec.rb +23 -59
  60. data/spec/active_interaction/filters/record_filter_spec.rb +23 -49
  61. data/spec/active_interaction/filters/string_filter_spec.rb +15 -3
  62. data/spec/active_interaction/filters/symbol_filter_spec.rb +15 -3
  63. data/spec/active_interaction/filters/time_filter_spec.rb +65 -3
  64. data/spec/active_interaction/grouped_input_spec.rb +0 -2
  65. data/spec/active_interaction/i18n_spec.rb +3 -7
  66. data/spec/active_interaction/{modules/input_processor_spec.rb → inputs_spec.rb} +5 -5
  67. data/spec/active_interaction/integration/array_interaction_spec.rb +23 -12
  68. data/spec/active_interaction/integration/boolean_interaction_spec.rb +0 -2
  69. data/spec/active_interaction/integration/date_interaction_spec.rb +0 -2
  70. data/spec/active_interaction/integration/date_time_interaction_spec.rb +0 -2
  71. data/spec/active_interaction/integration/file_interaction_spec.rb +0 -2
  72. data/spec/active_interaction/integration/float_interaction_spec.rb +0 -2
  73. data/spec/active_interaction/integration/hash_interaction_spec.rb +0 -2
  74. data/spec/active_interaction/integration/integer_interaction_spec.rb +0 -2
  75. data/spec/active_interaction/integration/interface_interaction_spec.rb +1 -3
  76. data/spec/active_interaction/integration/object_interaction_spec.rb +0 -2
  77. data/spec/active_interaction/integration/string_interaction_spec.rb +0 -2
  78. data/spec/active_interaction/integration/symbol_interaction_spec.rb +0 -2
  79. data/spec/active_interaction/integration/time_interaction_spec.rb +14 -18
  80. data/spec/active_interaction/modules/validation_spec.rb +1 -3
  81. data/spec/spec_helper.rb +2 -6
  82. data/spec/support/concerns.rb +0 -2
  83. data/spec/support/filters.rb +13 -9
  84. data/spec/support/interactions.rb +22 -14
  85. metadata +106 -52
  86. data/lib/active_interaction/backports.rb +0 -59
  87. data/lib/active_interaction/filters/abstract_filter.rb +0 -19
  88. data/spec/active_interaction/filters/abstract_filter_spec.rb +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: af64e2c9d517eda9f5f86be8927f2d8d82b53f905112379ca49bb6e42e487e99
4
- data.tar.gz: 9f59937f4722d923d79611a9c99b81a0ee920e405352ee48cf01050000bc06d3
3
+ metadata.gz: acc5f59cdd588429a58556cea7b70aa0c43a6a13c523a682cac266cfc7ab76a1
4
+ data.tar.gz: 9a58db1735b436ae91a70baf9c2eab9ef4851a20a9e51c05c6f243af6e3c282b
5
5
  SHA512:
6
- metadata.gz: eb7c7b81e0105785bba793aa3ca5466daea5f49e197a5461689c45b1b5f637fdd18f1d58ed394afa7f23a28a1d8a39d16c7c146ad05055e577f89760508ee879
7
- data.tar.gz: 277a4bebd07bb0c8465851104a95b0ea9c3d7d07de4386c20082551b4bd2ce09a822687d9d891322825625daf76ec5e4606b19b97308475ec735a5ad73f42356
6
+ metadata.gz: c78b1f82d8593a238a2076f7351146e51079d163d963cc830d9e086bbc82bd791fbc95527d4fc829e3c38039bb77ce06fefe82520f5b6f2148a64384a8e3a339
7
+ data.tar.gz: 362a8b86512c8ef05d9582a377000e06e2b8b0855c112458020911fa77477c3c98cd10555e81d259cfc767f79c1eb8b03e9ec12124cfca38c552208006e1a70a
@@ -1,3 +1,182 @@
1
+ # [4.0.0][] (2021-01-10)
2
+
3
+ ## Changed
4
+
5
+ - drop support for Ruby < 2.5, added support for Ruby 3.0
6
+ - drop support for Rails < 5.0, added support for Rails 6.1
7
+ - [#398][] - Predicate methods have been removed.
8
+ ([how to upgrade](#predicate-methods))
9
+ - [#412][] - Filters will now treat blank string values as `nil`
10
+ (except `string` and `symbol`). ([how to upgrade](#blank-values-treated-as-nil-for-filters))
11
+ - [#392][] - Integer parsing now defaults the base to 10.
12
+ ([how to upgrade](#integer-parsing-base-now-10))
13
+ - The `inputs` method now returns an `ActiveInteraction::Input` instead of a
14
+ hash. The `ActiveInteraction::Input` class still responds to all hash methods.
15
+ - The `object` and `record` filters now only accept an instance of the correct
16
+ class type or a subclass of the correct class. They no longer allow you to
17
+ check for included modules. ([how to upgrade](#object-and-record-filter-changes))
18
+ - The `interface` filter will now look for an ancestor of the value passed
19
+ based on the name of the interface or the value passed in the `from` option.
20
+ - The `InvalidClassError` has been replaced by `InvalidNameError`.
21
+ - When introspecting an array filter, the inner filter is referenced by :'0'
22
+ instead of the singularized version of the array filter name.
23
+
24
+ ## Added
25
+
26
+ - Implicit coercion of types are now supported in filters (e.g. to_str, to_int,
27
+ etc).
28
+ - The `interface` and `record` filters, when used as an inner filter for an
29
+ `array`, will have their `from/class` option set to a singularized version of
30
+ the `array` filter name.
31
+
32
+ ## Upgrading
33
+
34
+ ### Predicate Methods
35
+
36
+ We've removed the predicate methods that were automatically generated for each
37
+ input. They would return true if an input was not `nil`. They can be manually
38
+ replaced with that same check.
39
+
40
+ ```ruby
41
+ # v3.8
42
+ class Example < ActiveInteraction::Base
43
+ string :first_name
44
+
45
+ validates :first_name,
46
+ presence: true,
47
+ if: :first_name?
48
+
49
+ def execute
50
+ # ...
51
+ end
52
+ end
53
+
54
+ # v4.0
55
+ class Example < ActiveInteraction::Base
56
+ string :first_name
57
+
58
+ validates :first_name,
59
+ presence: true,
60
+ unless: 'first_name.nil?'
61
+
62
+ def execute
63
+ # ...
64
+ end
65
+ end
66
+ ```
67
+
68
+ ## Blank Values Treated As `nil` For Filters
69
+
70
+ In an effort to improve form support, strings that are `blank?` will
71
+ be converted into `nil` for all filters except `string` and `symbol`.
72
+ Previously, blank strings would have cased `:invalid_type` errors but
73
+ they'll now cause a `:missing` error which should be more form
74
+ friendly. If the filter has a default, the blank string will cause
75
+ the default to be used.
76
+
77
+ ```ruby
78
+ class Example < ActiveInteraction::Base
79
+ integer :i
80
+ boolean :b, default: false
81
+
82
+ def execute
83
+ [i, b]
84
+ end
85
+ end
86
+
87
+ # v3.8
88
+ Example.run(i: '', b: '').errors.details
89
+ => {:i=>[{:error=>:invalid_type, :type=>"integer"}], :b=>[{:error=>:invalid_type, :type=>"boolean"}]}
90
+
91
+ # v4.0
92
+ Example.run(i: '', b: '').errors.details
93
+ => {:i=>[{:error=>:missing}]}
94
+
95
+ # v3.8
96
+ Example.run(i: 0, b: '').errors.details
97
+ => {:b=>[{:error=>:invalid_type, :type=>"boolean"}]}
98
+
99
+ # v4.0
100
+ Example.run(i: 0, b: '').errors.details
101
+ => {}
102
+
103
+ Example.run(i: 0, b: '').result
104
+ => [0, false] # the default is used for `:b`
105
+ ```
106
+
107
+ ### Integer Parsing Base Now 10
108
+
109
+ Integers are parsed using `Integer`. By default this meant that when
110
+ strings were parsed, radix indicators (0, 0b, and 0x) were honored. Now
111
+ we're defaulting the base to `10`. This means all strings will be parsed
112
+ as though they are base 10.
113
+
114
+ ```ruby
115
+ class Example < ActiveInteraction::Base
116
+ integer :x
117
+
118
+ def execute
119
+ x
120
+ end
121
+ end
122
+
123
+ # v3.8
124
+ Example.run!(x: '010')
125
+ # => 8
126
+
127
+ # v4.0
128
+ Example.run!(x: '010')
129
+ # => 10
130
+ ```
131
+
132
+ If you want the old behavior that respected the radix you can pass `0`
133
+ as the base.
134
+
135
+ ```diff
136
+ - integer :x
137
+ + integer :x, base: 0
138
+ ```
139
+
140
+ With that change, we can see the radix is respected again.
141
+
142
+ ```ruby
143
+ # v4.0.0
144
+ Example.run!(x: '010')
145
+ # => 8
146
+ ```
147
+
148
+ ### Object and Record Filter Changes
149
+
150
+ The `object` and `record` filters used to be able to check for included modules
151
+ in addition to a class type. This has been removed. If you want any object that
152
+ has a particular module included, you'll need to use the newly expanded
153
+ `interface` filter.
154
+
155
+ # [3.8.3][] (2020-04-22)
156
+
157
+ ## Fixed
158
+
159
+ - [486][] `valid?` returns true if block not called and error added in execute around callback.
160
+
161
+ # [3.8.2][] (2020-04-22)
162
+
163
+ ## Fixed
164
+
165
+ - [479][] Composed interactions that throw errors now show a complete backtrace instead of ending at the `run!` of the outermost interaction.
166
+
167
+ # [3.8.1][] (2020-04-04)
168
+
169
+ ## Fixed
170
+
171
+ - The implementation for providing a failing interaction on `InvalidInteractionError` was a breaking API change. It now works without breaking the API.
172
+
173
+ # [3.8.0][] (2020-02-28)
174
+
175
+ ## Added
176
+
177
+ - [#477][] `InvalidInteractionError` now provides access to the failing interaction by calling `interaction`.
178
+ - [#476][] Update `given?` to check for items in an array by passing an index.
179
+
1
180
  # [3.7.1][] (2019-03-20)
2
181
 
3
182
  ## Fixed
@@ -752,6 +931,11 @@ Example.run
752
931
 
753
932
  - Initial release.
754
933
 
934
+ [4.0.0]: https://github.com/AaronLasseigne/active_interaction/compare/v3.8.3...v4.0.0
935
+ [3.8.3]: https://github.com/AaronLasseigne/active_interaction/compare/v3.8.2...v3.8.3
936
+ [3.8.2]: https://github.com/AaronLasseigne/active_interaction/compare/v3.8.1...v3.8.2
937
+ [3.8.1]: https://github.com/AaronLasseigne/active_interaction/compare/v3.8.0...v3.8.1
938
+ [3.8.0]: https://github.com/AaronLasseigne/active_interaction/compare/v3.7.1...v3.8.0
755
939
  [3.7.1]: https://github.com/AaronLasseigne/active_interaction/compare/v3.7.0...v3.7.1
756
940
  [3.7.0]: https://github.com/AaronLasseigne/active_interaction/compare/v3.6.2...v3.7.0
757
941
  [3.6.2]: https://github.com/AaronLasseigne/active_interaction/compare/v3.6.1...v3.6.2
@@ -946,3 +1130,9 @@ Example.run
946
1130
  [#454]: https://github.com/AaronLasseigne/active_interaction/pull/454
947
1131
  [#455]: https://github.com/AaronLasseigne/active_interaction/pull/455
948
1132
  [#457]: https://github.com/AaronLasseigne/active_interaction/issues/457
1133
+ [#477]: https://github.com/AaronLasseigne/active_interaction/issues/477
1134
+ [#476]: https://github.com/AaronLasseigne/active_interaction/issues/476
1135
+ [#479]: https://github.com/AaronLasseigne/active_interaction/issues/479
1136
+ [#486]: https://github.com/AaronLasseigne/active_interaction/issues/486
1137
+ [#392]: https://github.com/AaronLasseigne/active_interaction/issues/392
1138
+ [#398]: https://github.com/AaronLasseigne/active_interaction/issues/398
@@ -10,4 +10,4 @@
10
10
 
11
11
  Running the tests using `rake` (with no args) will also check for style issues in the code. Ideally all submissions would pass these checks. If the code style is causing issues (particularly the method or class size) we can work with you to correct it. Don't let it stop you from contributing.
12
12
 
13
- [fork]: https://github.com/orgsync/active_interaction/fork
13
+ [fork]: https://github.com/AaronLasseigne/active_interaction/fork
data/README.md CHANGED
@@ -4,7 +4,7 @@ ActiveInteraction manages application-specific business logic.
4
4
  It's an implementation of the command pattern in Ruby.
5
5
 
6
6
  [![Version](https://img.shields.io/gem/v/active_interaction.svg?style=flat-square)](https://rubygems.org/gems/active_interaction)
7
- [![Build](https://img.shields.io/travis/AaronLasseigne/active_interaction.svg?style=flat-square)](https://travis-ci.org/AaronLasseigne/active_interaction)
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
8
  [![Coverage](https://img.shields.io/coveralls/github/AaronLasseigne/active_interaction.svg?style=flat-square)](https://coveralls.io/r/orgsync/active_interaction)
9
9
  [![Climate](https://img.shields.io/codeclimate/maintainability/orgsync/active_interaction.svg?style=flat-square)](https://codeclimate.com/github/orgsync/active_interaction)
10
10
 
@@ -55,7 +55,6 @@ handles your verbs.
55
55
  - [Forms](#forms)
56
56
  - [Grouped inputs](#grouped-inputs)
57
57
  - [Optional inputs](#optional-inputs)
58
- - [Predicates](#predicates)
59
58
  - [Translations](#translations)
60
59
  - [Credits](#credits)
61
60
 
@@ -66,23 +65,19 @@ handles your verbs.
66
65
  Add it to your Gemfile:
67
66
 
68
67
  ``` rb
69
- gem 'active_interaction', '~> 3.7'
68
+ gem 'active_interaction', '~> 4.0'
70
69
  ```
71
70
 
72
71
  Or install it manually:
73
72
 
74
73
  ``` sh
75
- $ gem install active_interaction --version '~> 3.7'
74
+ $ gem install active_interaction --version '~> 4.0'
76
75
  ```
77
76
 
78
77
  This project uses [Semantic Versioning][]. Check out [GitHub releases][] for a
79
78
  detailed list of changes. For help upgrading to version 2, please read [the
80
79
  announcement post][].
81
80
 
82
- ActiveInteraction works with Ruby 2.0 through 2.6 and ActiveModel 4.0 through
83
- 6.0. If you want to use ActiveInteraction with an older version of Ruby or
84
- ActiveModel, use ActiveInteraction < 3.0.0.
85
-
86
81
  ## Basic usage
87
82
 
88
83
  To define an interaction, create a subclass of `ActiveInteraction::Base`. Then
@@ -241,7 +236,8 @@ ArrayInteraction.run!(toppings: [:cheese, 'pepperoni'])
241
236
  # => 2
242
237
  ```
243
238
 
244
- Use a block to constrain the types of elements an array can contain.
239
+ Use a block to constrain the types of elements an array can contain. Note that
240
+ you can only have one filter inside an array block, and it must not have a name.
245
241
 
246
242
  ``` rb
247
243
  array :birthdays do
@@ -249,19 +245,29 @@ array :birthdays do
249
245
  end
250
246
  ```
251
247
 
252
- Note that you can only have one filter inside an array block, and it must not
253
- have a name.
248
+ For `interface`, `object`, and `record` filters, the name of the array filter
249
+ will be singularized and used to determine the type of value passed. In the
250
+ example below, the objects passed would need to be of type `Cow`.
254
251
 
255
252
  ``` rb
256
253
  array :cows do
257
- object class: Cow
254
+ object
255
+ end
256
+ ```
257
+
258
+ You can override this by passing the necessary information to the inner filter.
259
+
260
+ ```ruby
261
+ array :managers do
262
+ object class: People
258
263
  end
259
264
  ```
260
265
 
261
266
  ### Boolean
262
267
 
263
- Boolean filters convert the strings `"1"` and `"true"` (case-insensitive) into
264
- `true`. They also convert `"0"` and `"false"` into `false`.
268
+ Boolean filters convert the strings `"1"`, `"true"`, and `"on"`
269
+ (case-insensitive) into `true`. They also convert `"0"`, `"false"`, and `"off"`
270
+ into `false`. Blank strings will be treated as `nil`.
265
271
 
266
272
  ``` rb
267
273
  class BooleanInteraction < ActiveInteraction::Base
@@ -353,8 +359,45 @@ hash :stuff,
353
359
 
354
360
  ### Interface
355
361
 
356
- Interface filters allow you to specify that an object must respond to a certain
357
- set of methods. This allows you to do duck typing with interactions.
362
+ Interface filters allow you to specify an interface that the passed value must
363
+ meet in order to pass. The name of the interface is used to look for a constant
364
+ inside the ancestor listing for the passed value. This allows for a variety of
365
+ checks depending on what's passed. Class instances are checked for an included
366
+ module or an inherited ancestor class. Classes are checked for an extended
367
+ module or an inherited ancestor class. Modules are checked for an extended
368
+ module.
369
+
370
+ ``` rb
371
+ class InterfaceInteraction < ActiveInteraction::Base
372
+ interface :exception
373
+
374
+ def execute
375
+ exception
376
+ end
377
+ end
378
+
379
+ InterfaceInteraction.run!(exception: Exception)
380
+ # ActiveInteraction::InvalidInteractionError: Exception is not a valid interface
381
+ InterfaceInteraction.run!(exception: NameError) # a subclass of Exception
382
+ # => NameError
383
+ ```
384
+
385
+ You can use `:from` to specify a class or module. This would be the equivalent
386
+ of what's above.
387
+
388
+ ```rb
389
+ class InterfaceInteraction < ActiveInteraction::Base
390
+ interface :error,
391
+ from: Exception
392
+
393
+ def execute
394
+ error
395
+ end
396
+ end
397
+ ```
398
+
399
+ You can also create an anonymous interface on the fly by passing the `methods`
400
+ option.
358
401
 
359
402
  ``` rb
360
403
  class InterfaceInteraction < ActiveInteraction::Base
@@ -380,9 +423,8 @@ InterfaceInteraction.run!(serializer: JSON)
380
423
 
381
424
  ### Object
382
425
 
383
- Object filters allow you to require an instance of a particular class. It
384
- checks either `#is_a?` on the instance or `.===` on the class. Because of that,
385
- it also works with classes that have mixed modules in with `include`.
426
+ Object filters allow you to require an instance of a particular class or one of
427
+ its subclasses.
386
428
 
387
429
  ``` rb
388
430
  class Cow
@@ -419,8 +461,8 @@ object :dolly3,
419
461
  ```
420
462
 
421
463
  If you have value objects or you would like to build one object from another,
422
- you can use the `converter` option. It is only called if the value provided does
423
- not pass `#is_a?` and `.===` for the object class. The `converter` option
464
+ you can use the `converter` option. It is only called if the value provided is
465
+ not an instance of the class or one of its subclasses. The `converter` option
424
466
  accepts a symbol that specifies a class method on the object class or a proc.
425
467
  Both will be passed the value and any errors thrown inside the converter will
426
468
  cause the value to be considered invalid. Any returned value that is not the
@@ -447,13 +489,13 @@ ObjectInteraction.run!(ip_address: 1)
447
489
 
448
490
  ### Record
449
491
 
450
- Record filters allow you to require an instance of a particular class or a value
451
- that can be used to locate an instance of the object. It checks either `#is_a?`
452
- on the instance or `.===` on the class. If the value does not match, it will
453
- call `find` on the class of the record. This is particularly useful when working
454
- with ActiveRecord objects. Like an object filter, the class is derived from the
455
- name passed but can be specified with the `class` option. The value given to the
456
- `default` option will also be found.
492
+ Record filters allow you to require an instance of a particular class (or one
493
+ of its subclasses) or a value that can be used to locate an instance of the
494
+ object. If the value does not match, it will call `find` on the class of the
495
+ record. This is particularly useful when working with ActiveRecord objects.
496
+ Like an object filter, the class is derived from the name passed but can be
497
+ specified with the `class` option. The value given to the `default` option will
498
+ also be found.
457
499
 
458
500
  ``` rb
459
501
  class RecordInteraction < ActiveInteraction::Base
@@ -523,9 +565,10 @@ SymbolInteraction.run!(method: :object_id)
523
565
  ### Dates and times
524
566
 
525
567
  Filters that work with dates and times behave similarly. By default, they all
526
- convert strings into their expected data types using `.parse`. If you give the
527
- `format` option, they will instead convert strings using `.strptime`. Note that
528
- formats won't work with `DateTime` and `Time` filters if a time zone is set.
568
+ convert strings into their expected data types using `.parse`. Blank strings
569
+ will be treated as `nil`. If you give the `format` option, they will instead
570
+ convert strings using `.strptime`. Note that formats won't work with `DateTime`
571
+ and `Time` filters if a time zone is set.
529
572
 
530
573
  #### Date
531
574
 
@@ -599,7 +642,8 @@ time :start,
599
642
  ### Numbers
600
643
 
601
644
  All numeric filters accept numeric input. They will also convert strings using
602
- the appropriate method from `Kernel` (like `.Float`).
645
+ the appropriate method from `Kernel` (like `.Float`). Blank strings will be
646
+ treated as `nil`.
603
647
 
604
648
  #### Decimal
605
649
 
@@ -660,24 +704,27 @@ IntegerInteraction.run!(limit: 10)
660
704
  ```
661
705
 
662
706
  When a `String` is passed into an `integer` input, the value will be coerced.
663
- Coercion is based on `Kernel#Integer` which attempts to detect the base being used.
664
- However, you may want to specify the `base` for the conversion to something more
665
- sensible (e.g. `base: 10`).
707
+ A default base of `10` is used though it may be overridden with the `base` option.
708
+ If a base of `0` is provided, the coercion will respect radix indicators present
709
+ in the string.
666
710
 
667
711
  ``` rb
668
712
  class IntegerInteraction < ActiveInteraction::Base
669
- integer :limit1, base: 10
670
- integer :limit2
671
-
713
+ integer :limit1
714
+ integer :limit2, base: 8
715
+ integer :limit3, base: 0
716
+
672
717
  def execute
673
- [limit1, limit2]
718
+ [limit1, limit2, limit3]
674
719
  end
675
720
  end
676
721
 
677
- IntegerInteraction.run!(limit1: "08", limit2: 8)
678
- # => [8, 8]
679
- IntegerInteraction.run!(limit1: "08", limit2: "08")
680
- # ArgumentError: invalid value for Integer(): "08"
722
+ IntegerInteraction.run!(limit1: 71, limit2: 71, limit3: 71)
723
+ # => [71, 71, 71]
724
+ IntegerInteraction.run!(limit1: "071", limit2: "071", limit3: "0x71")
725
+ # => [71, 57, 113]
726
+ IntegerInteraction.run!(limit1: "08", limit2: "08", limit3: "08")
727
+ ActiveInteraction::InvalidInteractionError: Limit2 is not a valid integer, Limit3 is not a valid integer
681
728
  ```
682
729
 
683
730
  ## Rails
@@ -921,10 +968,6 @@ The interaction that updates accounts is more complicated than the others. It
921
968
  requires an account to update, but the other inputs are optional. If they're
922
969
  missing, it'll ignore those attributes. If they're present, it'll update them.
923
970
 
924
- ActiveInteraction generates predicate methods (like `#first_name?`) for your
925
- inputs. They will return `false` if the input is `nil` and `true` otherwise.
926
- Skip to [the predicates section](#predicates) for more information about them.
927
-
928
971
  ``` rb
929
972
  class UpdateAccount < ActiveInteraction::Base
930
973
  object :account
@@ -934,14 +977,14 @@ class UpdateAccount < ActiveInteraction::Base
934
977
 
935
978
  validates :first_name,
936
979
  presence: true,
937
- if: :first_name?
980
+ unless: 'first_name.nil?'
938
981
  validates :last_name,
939
982
  presence: true,
940
- if: :last_name?
983
+ unless: 'last_name.nil?'
941
984
 
942
985
  def execute
943
- account.first_name = first_name if first_name?
944
- account.last_name = last_name if last_name?
986
+ account.first_name = first_name if first_name.present?
987
+ account.last_name = last_name if last_name.present?
945
988
 
946
989
  unless account.save
947
990
  errors.merge!(account.errors)
@@ -1158,9 +1201,6 @@ def execute
1158
1201
  end
1159
1202
  ```
1160
1203
 
1161
- These types of errors will become standard with Rails 5. ActiveInteraction's
1162
- implementation is based off of [active_model-errors_details][].
1163
-
1164
1204
  ActiveInteraction also supports merging errors. This is useful if you want to
1165
1205
  delegate validation to some other object. For example, if you have an
1166
1206
  interaction that updates a record, you might want that record to validate
@@ -1322,7 +1362,8 @@ whether a value was passed to `run` or the result of a filter default. In
1322
1362
  particular, it is useful when `nil` is an acceptable value. For example, you
1323
1363
  may optionally track your users' birthdays. You can use the `given?` predicate
1324
1364
  to see if an input was even passed to `run`. With `given?` you can also check
1325
- the input of a hash filter by passing a series of keys to check.
1365
+ the input of a hash or array filter by passing a series of keys or indexes to
1366
+ check.
1326
1367
 
1327
1368
  ``` rb
1328
1369
  class UpdateUser < ActiveInteraction::Base
@@ -1355,41 +1396,6 @@ UpdateUser.run!(user: user, birthday: nil)
1355
1396
  UpdateUser.run!(user: user, birthday: Date.new(2000, 1, 2))
1356
1397
  ```
1357
1398
 
1358
- ### Predicates
1359
-
1360
- ActiveInteraction creates a predicate method for every input defined by a
1361
- filter. So if you have an input called `foo`, there will be a predicate method
1362
- called `#foo?`. That method will tell you if the input was given (that is, if
1363
- it was not `nil`).
1364
-
1365
- ``` rb
1366
- class SayHello < ActiveInteraction::Base
1367
- string :name,
1368
- default: nil
1369
-
1370
- def execute
1371
- if name?
1372
- "Hello, #{name}!"
1373
- else
1374
- "Howdy, stranger!"
1375
- end
1376
- end
1377
- end
1378
-
1379
- SayHello.run!(name: nil)
1380
- # => "Howdy, stranger!"
1381
- SayHello.run!(name: 'Taylor')
1382
- # => "Hello, Taylor!"
1383
- ```
1384
-
1385
- This can be confusing for boolean inputs. If you have some boolean input `foo`,
1386
- then the actual value of that input is available through `foo`. The associated
1387
- predicate method, `#foo?`, will tell you if that value is not `nil`. So it will
1388
- only be `false` if the input is optional and happens to be `nil`.
1389
-
1390
- See [the optional inputs section][] for help on determining if an input was
1391
- present in the input hash instead of just `nil`.
1392
-
1393
1399
  ### Translations
1394
1400
 
1395
1401
  ActiveInteraction is i18n aware out of the box! All you have to do is add