active_interaction 3.8.3 → 4.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +193 -0
  3. data/README.md +97 -116
  4. data/lib/active_interaction.rb +2 -7
  5. data/lib/active_interaction/base.rb +44 -67
  6. data/lib/active_interaction/concerns/active_modelable.rb +1 -3
  7. data/lib/active_interaction/concerns/active_recordable.rb +1 -6
  8. data/lib/active_interaction/concerns/hashable.rb +0 -1
  9. data/lib/active_interaction/concerns/missable.rb +0 -1
  10. data/lib/active_interaction/concerns/runnable.rb +6 -12
  11. data/lib/active_interaction/errors.rb +4 -19
  12. data/lib/active_interaction/filter.rb +66 -37
  13. data/lib/active_interaction/filter_column.rb +0 -3
  14. data/lib/active_interaction/filters/abstract_date_time_filter.rb +38 -36
  15. data/lib/active_interaction/filters/abstract_numeric_filter.rb +27 -17
  16. data/lib/active_interaction/filters/array_filter.rb +59 -36
  17. data/lib/active_interaction/filters/boolean_filter.rb +26 -12
  18. data/lib/active_interaction/filters/date_filter.rb +1 -2
  19. data/lib/active_interaction/filters/date_time_filter.rb +1 -2
  20. data/lib/active_interaction/filters/decimal_filter.rb +10 -28
  21. data/lib/active_interaction/filters/file_filter.rb +6 -5
  22. data/lib/active_interaction/filters/float_filter.rb +1 -2
  23. data/lib/active_interaction/filters/hash_filter.rb +37 -27
  24. data/lib/active_interaction/filters/integer_filter.rb +7 -8
  25. data/lib/active_interaction/filters/interface_filter.rb +48 -14
  26. data/lib/active_interaction/filters/object_filter.rb +23 -50
  27. data/lib/active_interaction/filters/record_filter.rb +10 -35
  28. data/lib/active_interaction/filters/string_filter.rb +21 -12
  29. data/lib/active_interaction/filters/symbol_filter.rb +13 -7
  30. data/lib/active_interaction/filters/time_filter.rb +24 -19
  31. data/lib/active_interaction/grouped_input.rb +0 -3
  32. data/lib/active_interaction/inputs.rb +120 -0
  33. data/lib/active_interaction/modules/validation.rb +9 -21
  34. data/lib/active_interaction/version.rb +1 -3
  35. data/spec/active_interaction/base_spec.rb +38 -99
  36. data/spec/active_interaction/concerns/active_modelable_spec.rb +0 -2
  37. data/spec/active_interaction/concerns/active_recordable_spec.rb +0 -2
  38. data/spec/active_interaction/concerns/hashable_spec.rb +0 -2
  39. data/spec/active_interaction/concerns/missable_spec.rb +0 -2
  40. data/spec/active_interaction/concerns/runnable_spec.rb +9 -13
  41. data/spec/active_interaction/errors_spec.rb +4 -25
  42. data/spec/active_interaction/filter_column_spec.rb +0 -2
  43. data/spec/active_interaction/filter_spec.rb +0 -2
  44. data/spec/active_interaction/filters/abstract_date_time_filter_spec.rb +1 -3
  45. data/spec/active_interaction/filters/abstract_numeric_filter_spec.rb +1 -3
  46. data/spec/active_interaction/filters/array_filter_spec.rb +62 -13
  47. data/spec/active_interaction/filters/boolean_filter_spec.rb +58 -4
  48. data/spec/active_interaction/filters/date_filter_spec.rb +43 -3
  49. data/spec/active_interaction/filters/date_time_filter_spec.rb +43 -3
  50. data/spec/active_interaction/filters/decimal_filter_spec.rb +57 -3
  51. data/spec/active_interaction/filters/file_filter_spec.rb +1 -3
  52. data/spec/active_interaction/filters/float_filter_spec.rb +60 -4
  53. data/spec/active_interaction/filters/hash_filter_spec.rb +19 -9
  54. data/spec/active_interaction/filters/integer_filter_spec.rb +49 -7
  55. data/spec/active_interaction/filters/interface_filter_spec.rb +397 -24
  56. data/spec/active_interaction/filters/object_filter_spec.rb +23 -59
  57. data/spec/active_interaction/filters/record_filter_spec.rb +23 -49
  58. data/spec/active_interaction/filters/string_filter_spec.rb +15 -3
  59. data/spec/active_interaction/filters/symbol_filter_spec.rb +15 -3
  60. data/spec/active_interaction/filters/time_filter_spec.rb +65 -3
  61. data/spec/active_interaction/grouped_input_spec.rb +0 -2
  62. data/spec/active_interaction/i18n_spec.rb +3 -7
  63. data/spec/active_interaction/{modules/input_processor_spec.rb → inputs_spec.rb} +35 -5
  64. data/spec/active_interaction/integration/array_interaction_spec.rb +18 -22
  65. data/spec/active_interaction/integration/boolean_interaction_spec.rb +0 -2
  66. data/spec/active_interaction/integration/date_interaction_spec.rb +0 -2
  67. data/spec/active_interaction/integration/date_time_interaction_spec.rb +0 -2
  68. data/spec/active_interaction/integration/file_interaction_spec.rb +0 -2
  69. data/spec/active_interaction/integration/float_interaction_spec.rb +0 -2
  70. data/spec/active_interaction/integration/hash_interaction_spec.rb +1 -3
  71. data/spec/active_interaction/integration/integer_interaction_spec.rb +0 -2
  72. data/spec/active_interaction/integration/interface_interaction_spec.rb +1 -3
  73. data/spec/active_interaction/integration/object_interaction_spec.rb +0 -2
  74. data/spec/active_interaction/integration/string_interaction_spec.rb +0 -2
  75. data/spec/active_interaction/integration/symbol_interaction_spec.rb +0 -2
  76. data/spec/active_interaction/integration/time_interaction_spec.rb +14 -18
  77. data/spec/active_interaction/modules/validation_spec.rb +1 -3
  78. data/spec/spec_helper.rb +2 -12
  79. data/spec/support/concerns.rb +0 -2
  80. data/spec/support/filters.rb +13 -9
  81. data/spec/support/interactions.rb +22 -14
  82. metadata +92 -62
  83. data/lib/active_interaction/backports.rb +0 -59
  84. data/lib/active_interaction/filters/abstract_filter.rb +0 -19
  85. data/lib/active_interaction/modules/input_processor.rb +0 -52
  86. 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: 883e2c22dbe4db00730bd2d917d7812e6f79898f41cfdcde16bc685b1ae1de29
4
- data.tar.gz: 4c1af0be75d96c6801b288b450d260580b3d12f330ebeb69b1f6b20d88da5241
3
+ metadata.gz: 24af31782cca3b666b2f211506cb3ca39e362ffcb665ae509bda892b13e65934
4
+ data.tar.gz: 3a139e271e9d19ac23cdea62a5bbe882889f50483647951289623b8f3aaaf97f
5
5
  SHA512:
6
- metadata.gz: d2426683e3249c63760a59a25c0a7e8d21780bedf31218676ffca3d75903db0f4b73c18d80b78bbc568997ca67106490bac774bf43f68dacbe5ba00541e08e78
7
- data.tar.gz: 412ae553643fd1449c9063058f5d4b8f39ea0bb97f4f166db73bee7ae6bf70a5f01b4ca8ee08a867417b8f7d83d57845d5168383c5ac69d2a931df7490527056
6
+ metadata.gz: 574d84a7a67526dcb2b7f64ca885d6cccb3115612e731b4091bd1bb122446a88ce81ad791196c6bd33555122e9b2c8b07ac1924cecbe400f4d541906274fcd3c
7
+ data.tar.gz: 1260eae401d19342ef4b8e2ada334553e7b99672b3257ec9b5880d0c6566eb8dc86e8de9fc5c5a3d0ed37485f86fc88cdb3adc2abe6299ac60d049f1d0713474
data/CHANGELOG.md CHANGED
@@ -1,3 +1,184 @@
1
+ # [4.0.4][] (2021-07-03)
2
+
3
+ ## Fix
4
+
5
+ - [#510][] - Hash parameters failed when working outside of Rails.
6
+ - [#511][] - Nested filters with options but no `:class` failed to have `:class` automatically added.
7
+
8
+ # [4.0.3][] (2021-06-24)
9
+
10
+ ## Fix
11
+
12
+ - [#499][] - `given?` now recognizes multi-part date inputs by their primary key name
13
+ - [#493][] - `compose` now properly accepts `Inputs`
14
+
15
+ # [4.0.2][] (2021-06-22)
16
+
17
+ ## Fix
18
+
19
+ - [#505][] - Nested Interface filters using the `:methods` option threw an error.
20
+
21
+ # [4.0.1][] (2021-05-26)
22
+
23
+ ## Fix
24
+
25
+ - Fix regression of filter name relaxing.
26
+ - [#495][] - Fix time filter ignoring time zones
27
+
28
+ # [4.0.0][] (2021-01-10)
29
+
30
+ ## Changed
31
+
32
+ - drop support for Ruby < 2.5, added support for Ruby 3.0
33
+ - drop support for Rails < 5.0, added support for Rails 6.1
34
+ - [#398][] - Predicate methods have been removed.
35
+ ([how to upgrade](#predicate-methods))
36
+ - [#412][] - Filters will now treat blank string values as `nil`
37
+ (except `string` and `symbol`). ([how to upgrade](#blank-values-treated-as-nil-for-filters))
38
+ - [#392][] - Integer parsing now defaults the base to 10.
39
+ ([how to upgrade](#integer-parsing-base-now-10))
40
+ - The `inputs` method now returns an `ActiveInteraction::Input` instead of a
41
+ hash. The `ActiveInteraction::Input` class still responds to all hash methods.
42
+ - The `object` and `record` filters now only accept an instance of the correct
43
+ class type or a subclass of the correct class. They no longer allow you to
44
+ check for included modules. ([how to upgrade](#object-and-record-filter-changes))
45
+ - The `interface` filter will now look for an ancestor of the value passed
46
+ based on the name of the interface or the value passed in the `from` option.
47
+ - The `InvalidClassError` has been replaced by `InvalidNameError`.
48
+ - When introspecting an array filter, the inner filter is referenced by :'0'
49
+ instead of the singularized version of the array filter name.
50
+
51
+ ## Added
52
+
53
+ - Implicit coercion of types are now supported in filters (e.g. to_str, to_int,
54
+ etc).
55
+ - The `interface` and `record` filters, when used as an inner filter for an
56
+ `array`, will have their `from/class` option set to a singularized version of
57
+ the `array` filter name.
58
+
59
+ ## Upgrading
60
+
61
+ ### Predicate Methods
62
+
63
+ We've removed the predicate methods that were automatically generated for each
64
+ input. They would return true if an input was not `nil`. They can be manually
65
+ replaced with that same check.
66
+
67
+ ```ruby
68
+ # v3.8
69
+ class Example < ActiveInteraction::Base
70
+ string :first_name
71
+
72
+ validates :first_name,
73
+ presence: true,
74
+ if: :first_name?
75
+
76
+ def execute
77
+ # ...
78
+ end
79
+ end
80
+
81
+ # v4.0
82
+ class Example < ActiveInteraction::Base
83
+ string :first_name
84
+
85
+ validates :first_name,
86
+ presence: true,
87
+ unless: -> { first_name.nil? }
88
+
89
+ def execute
90
+ # ...
91
+ end
92
+ end
93
+ ```
94
+
95
+ ## Blank Values Treated As `nil` For Filters
96
+
97
+ In an effort to improve form support, strings that are `blank?` will
98
+ be converted into `nil` for all filters except `string` and `symbol`.
99
+ Previously, blank strings would have cased `:invalid_type` errors but
100
+ they'll now cause a `:missing` error which should be more form
101
+ friendly. If the filter has a default, the blank string will cause
102
+ the default to be used.
103
+
104
+ ```ruby
105
+ class Example < ActiveInteraction::Base
106
+ integer :i
107
+ boolean :b, default: false
108
+
109
+ def execute
110
+ [i, b]
111
+ end
112
+ end
113
+
114
+ # v3.8
115
+ Example.run(i: '', b: '').errors.details
116
+ => {:i=>[{:error=>:invalid_type, :type=>"integer"}], :b=>[{:error=>:invalid_type, :type=>"boolean"}]}
117
+
118
+ # v4.0
119
+ Example.run(i: '', b: '').errors.details
120
+ => {:i=>[{:error=>:missing}]}
121
+
122
+ # v3.8
123
+ Example.run(i: 0, b: '').errors.details
124
+ => {:b=>[{:error=>:invalid_type, :type=>"boolean"}]}
125
+
126
+ # v4.0
127
+ Example.run(i: 0, b: '').errors.details
128
+ => {}
129
+
130
+ Example.run(i: 0, b: '').result
131
+ => [0, false] # the default is used for `:b`
132
+ ```
133
+
134
+ ### Integer Parsing Base Now 10
135
+
136
+ Integers are parsed using `Integer`. By default this meant that when
137
+ strings were parsed, radix indicators (0, 0b, and 0x) were honored. Now
138
+ we're defaulting the base to `10`. This means all strings will be parsed
139
+ as though they are base 10.
140
+
141
+ ```ruby
142
+ class Example < ActiveInteraction::Base
143
+ integer :x
144
+
145
+ def execute
146
+ x
147
+ end
148
+ end
149
+
150
+ # v3.8
151
+ Example.run!(x: '010')
152
+ # => 8
153
+
154
+ # v4.0
155
+ Example.run!(x: '010')
156
+ # => 10
157
+ ```
158
+
159
+ If you want the old behavior that respected the radix you can pass `0`
160
+ as the base.
161
+
162
+ ```diff
163
+ - integer :x
164
+ + integer :x, base: 0
165
+ ```
166
+
167
+ With that change, we can see the radix is respected again.
168
+
169
+ ```ruby
170
+ # v4.0.0
171
+ Example.run!(x: '010')
172
+ # => 8
173
+ ```
174
+
175
+ ### Object and Record Filter Changes
176
+
177
+ The `object` and `record` filters used to be able to check for included modules
178
+ in addition to a class type. This has been removed. If you want any object that
179
+ has a particular module included, you'll need to use the newly expanded
180
+ `interface` filter.
181
+
1
182
  # [3.8.3][] (2020-04-22)
2
183
 
3
184
  ## Fixed
@@ -777,6 +958,11 @@ Example.run
777
958
 
778
959
  - Initial release.
779
960
 
961
+ [4.0.4]: https://github.com/AaronLasseigne/active_interaction/compare/v4.0.3...v4.0.4
962
+ [4.0.3]: https://github.com/AaronLasseigne/active_interaction/compare/v4.0.2...v4.0.3
963
+ [4.0.2]: https://github.com/AaronLasseigne/active_interaction/compare/v4.0.1...v4.0.2
964
+ [4.0.1]: https://github.com/AaronLasseigne/active_interaction/compare/v4.0.0...v4.0.1
965
+ [4.0.0]: https://github.com/AaronLasseigne/active_interaction/compare/v3.8.3...v4.0.0
780
966
  [3.8.3]: https://github.com/AaronLasseigne/active_interaction/compare/v3.8.2...v3.8.3
781
967
  [3.8.2]: https://github.com/AaronLasseigne/active_interaction/compare/v3.8.1...v3.8.2
782
968
  [3.8.1]: https://github.com/AaronLasseigne/active_interaction/compare/v3.8.0...v3.8.1
@@ -979,3 +1165,10 @@ Example.run
979
1165
  [#476]: https://github.com/AaronLasseigne/active_interaction/issues/476
980
1166
  [#479]: https://github.com/AaronLasseigne/active_interaction/issues/479
981
1167
  [#486]: https://github.com/AaronLasseigne/active_interaction/issues/486
1168
+ [#392]: https://github.com/AaronLasseigne/active_interaction/issues/392
1169
+ [#398]: https://github.com/AaronLasseigne/active_interaction/issues/398
1170
+ [#495]: https://github.com/AaronLasseigne/active_interaction/issues/495
1171
+ [#505]: https://github.com/AaronLasseigne/active_interaction/issues/505
1172
+ [#499]: https://github.com/AaronLasseigne/active_interaction/issues/499
1173
+ [#493]: https://github.com/AaronLasseigne/active_interaction/issues/493
1174
+ [#510]: https://github.com/AaronLasseigne/active_interaction/issues/510
data/README.md CHANGED
@@ -4,8 +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)
8
- [![Coverage](https://img.shields.io/coveralls/github/AaronLasseigne/active_interaction.svg?style=flat-square)](https://coveralls.io/r/orgsync/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)
9
8
  [![Climate](https://img.shields.io/codeclimate/maintainability/orgsync/active_interaction.svg?style=flat-square)](https://codeclimate.com/github/orgsync/active_interaction)
10
9
 
11
10
  ---
@@ -55,34 +54,29 @@ handles your verbs.
55
54
  - [Forms](#forms)
56
55
  - [Grouped inputs](#grouped-inputs)
57
56
  - [Optional inputs](#optional-inputs)
58
- - [Predicates](#predicates)
59
57
  - [Translations](#translations)
60
58
  - [Credits](#credits)
61
59
 
62
- [Full Documentation][]
60
+ [API Documentation][]
63
61
 
64
62
  ## Installation
65
63
 
66
64
  Add it to your Gemfile:
67
65
 
68
66
  ``` rb
69
- gem 'active_interaction', '~> 3.8'
67
+ gem 'active_interaction', '~> 4.0'
70
68
  ```
71
69
 
72
70
  Or install it manually:
73
71
 
74
72
  ``` sh
75
- $ gem install active_interaction --version '~> 3.8'
73
+ $ gem install active_interaction --version '~> 4.0'
76
74
  ```
77
75
 
78
76
  This project uses [Semantic Versioning][]. Check out [GitHub releases][] for a
79
77
  detailed list of changes. For help upgrading to version 2, please read [the
80
78
  announcement post][].
81
79
 
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
80
  ## Basic usage
87
81
 
88
82
  To define an interaction, create a subclass of `ActiveInteraction::Base`. Then
@@ -241,7 +235,8 @@ ArrayInteraction.run!(toppings: [:cheese, 'pepperoni'])
241
235
  # => 2
242
236
  ```
243
237
 
244
- Use a block to constrain the types of elements an array can contain.
238
+ Use a block to constrain the types of elements an array can contain. Note that
239
+ you can only have one filter inside an array block, and it must not have a name.
245
240
 
246
241
  ``` rb
247
242
  array :birthdays do
@@ -249,19 +244,29 @@ array :birthdays do
249
244
  end
250
245
  ```
251
246
 
252
- Note that you can only have one filter inside an array block, and it must not
253
- have a name.
247
+ For `interface`, `object`, and `record` filters, the name of the array filter
248
+ will be singularized and used to determine the type of value passed. In the
249
+ example below, the objects passed would need to be of type `Cow`.
254
250
 
255
251
  ``` rb
256
252
  array :cows do
257
- object class: Cow
253
+ object
254
+ end
255
+ ```
256
+
257
+ You can override this by passing the necessary information to the inner filter.
258
+
259
+ ```ruby
260
+ array :managers do
261
+ object class: People
258
262
  end
259
263
  ```
260
264
 
261
265
  ### Boolean
262
266
 
263
- Boolean filters convert the strings `"1"` and `"true"` (case-insensitive) into
264
- `true`. They also convert `"0"` and `"false"` into `false`.
267
+ Boolean filters convert the strings `"1"`, `"true"`, and `"on"`
268
+ (case-insensitive) into `true`. They also convert `"0"`, `"false"`, and `"off"`
269
+ into `false`. Blank strings will be treated as `nil`.
265
270
 
266
271
  ``` rb
267
272
  class BooleanInteraction < ActiveInteraction::Base
@@ -353,8 +358,45 @@ hash :stuff,
353
358
 
354
359
  ### Interface
355
360
 
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.
361
+ Interface filters allow you to specify an interface that the passed value must
362
+ meet in order to pass. The name of the interface is used to look for a constant
363
+ inside the ancestor listing for the passed value. This allows for a variety of
364
+ checks depending on what's passed. Class instances are checked for an included
365
+ module or an inherited ancestor class. Classes are checked for an extended
366
+ module or an inherited ancestor class. Modules are checked for an extended
367
+ module.
368
+
369
+ ``` rb
370
+ class InterfaceInteraction < ActiveInteraction::Base
371
+ interface :exception
372
+
373
+ def execute
374
+ exception
375
+ end
376
+ end
377
+
378
+ InterfaceInteraction.run!(exception: Exception)
379
+ # ActiveInteraction::InvalidInteractionError: Exception is not a valid interface
380
+ InterfaceInteraction.run!(exception: NameError) # a subclass of Exception
381
+ # => NameError
382
+ ```
383
+
384
+ You can use `:from` to specify a class or module. This would be the equivalent
385
+ of what's above.
386
+
387
+ ```rb
388
+ class InterfaceInteraction < ActiveInteraction::Base
389
+ interface :error,
390
+ from: Exception
391
+
392
+ def execute
393
+ error
394
+ end
395
+ end
396
+ ```
397
+
398
+ You can also create an anonymous interface on the fly by passing the `methods`
399
+ option.
358
400
 
359
401
  ``` rb
360
402
  class InterfaceInteraction < ActiveInteraction::Base
@@ -378,30 +420,10 @@ InterfaceInteraction.run!(serializer: JSON)
378
420
  # => "{\"is_json\":true}"
379
421
  ```
380
422
 
381
- NOTE: The `methods` option is optional.
382
-
383
- ```rb
384
- class InterfaceInteraction < ActiveInteraction::Base
385
- interface :anything
386
-
387
- def execute
388
- anything.class
389
- end
390
- end
391
-
392
- require 'json'
393
-
394
- InterfaceInteraction.run!(anything: Hash.new)
395
- # => Hash
396
- InterfaceInteraction.run!
397
- # => NilClass
398
- ```
399
-
400
423
  ### Object
401
424
 
402
- Object filters allow you to require an instance of a particular class. It
403
- checks either `#is_a?` on the instance or `.===` on the class. Because of that,
404
- it also works with classes that have mixed modules in with `include`.
425
+ Object filters allow you to require an instance of a particular class or one of
426
+ its subclasses.
405
427
 
406
428
  ``` rb
407
429
  class Cow
@@ -438,8 +460,8 @@ object :dolly3,
438
460
  ```
439
461
 
440
462
  If you have value objects or you would like to build one object from another,
441
- you can use the `converter` option. It is only called if the value provided does
442
- not pass `#is_a?` and `.===` for the object class. The `converter` option
463
+ you can use the `converter` option. It is only called if the value provided is
464
+ not an instance of the class or one of its subclasses. The `converter` option
443
465
  accepts a symbol that specifies a class method on the object class or a proc.
444
466
  Both will be passed the value and any errors thrown inside the converter will
445
467
  cause the value to be considered invalid. Any returned value that is not the
@@ -466,13 +488,13 @@ ObjectInteraction.run!(ip_address: 1)
466
488
 
467
489
  ### Record
468
490
 
469
- Record filters allow you to require an instance of a particular class or a value
470
- that can be used to locate an instance of the object. It checks either `#is_a?`
471
- on the instance or `.===` on the class. If the value does not match, it will
472
- call `find` on the class of the record. This is particularly useful when working
473
- with ActiveRecord objects. Like an object filter, the class is derived from the
474
- name passed but can be specified with the `class` option. The value given to the
475
- `default` option will also be found.
491
+ Record filters allow you to require an instance of a particular class (or one
492
+ of its subclasses) or a value that can be used to locate an instance of the
493
+ object. If the value does not match, it will call `find` on the class of the
494
+ record. This is particularly useful when working with ActiveRecord objects.
495
+ Like an object filter, the class is derived from the name passed but can be
496
+ specified with the `class` option. The value given to the `default` option will
497
+ also be found.
476
498
 
477
499
  ``` rb
478
500
  class RecordInteraction < ActiveInteraction::Base
@@ -542,9 +564,10 @@ SymbolInteraction.run!(method: :object_id)
542
564
  ### Dates and times
543
565
 
544
566
  Filters that work with dates and times behave similarly. By default, they all
545
- convert strings into their expected data types using `.parse`. If you give the
546
- `format` option, they will instead convert strings using `.strptime`. Note that
547
- formats won't work with `DateTime` and `Time` filters if a time zone is set.
567
+ convert strings into their expected data types using `.parse`. Blank strings
568
+ will be treated as `nil`. If you give the `format` option, they will instead
569
+ convert strings using `.strptime`. Note that formats won't work with `DateTime`
570
+ and `Time` filters if a time zone is set.
548
571
 
549
572
  #### Date
550
573
 
@@ -618,7 +641,8 @@ time :start,
618
641
  ### Numbers
619
642
 
620
643
  All numeric filters accept numeric input. They will also convert strings using
621
- the appropriate method from `Kernel` (like `.Float`).
644
+ the appropriate method from `Kernel` (like `.Float`). Blank strings will be
645
+ treated as `nil`.
622
646
 
623
647
  #### Decimal
624
648
 
@@ -679,24 +703,27 @@ IntegerInteraction.run!(limit: 10)
679
703
  ```
680
704
 
681
705
  When a `String` is passed into an `integer` input, the value will be coerced.
682
- Coercion is based on `Kernel#Integer` which attempts to detect the base being used.
683
- However, you may want to specify the `base` for the conversion to something more
684
- sensible (e.g. `base: 10`).
706
+ A default base of `10` is used though it may be overridden with the `base` option.
707
+ If a base of `0` is provided, the coercion will respect radix indicators present
708
+ in the string.
685
709
 
686
710
  ``` rb
687
711
  class IntegerInteraction < ActiveInteraction::Base
688
- integer :limit1, base: 10
689
- integer :limit2
712
+ integer :limit1
713
+ integer :limit2, base: 8
714
+ integer :limit3, base: 0
690
715
 
691
716
  def execute
692
- [limit1, limit2]
717
+ [limit1, limit2, limit3]
693
718
  end
694
719
  end
695
720
 
696
- IntegerInteraction.run!(limit1: "08", limit2: 8)
697
- # => [8, 8]
698
- IntegerInteraction.run!(limit1: "08", limit2: "08")
699
- # ArgumentError: invalid value for Integer(): "08"
721
+ IntegerInteraction.run!(limit1: 71, limit2: 71, limit3: 71)
722
+ # => [71, 71, 71]
723
+ IntegerInteraction.run!(limit1: "071", limit2: "071", limit3: "0x71")
724
+ # => [71, 57, 113]
725
+ IntegerInteraction.run!(limit1: "08", limit2: "08", limit3: "08")
726
+ ActiveInteraction::InvalidInteractionError: Limit2 is not a valid integer, Limit3 is not a valid integer
700
727
  ```
701
728
 
702
729
  ## Rails
@@ -940,10 +967,6 @@ The interaction that updates accounts is more complicated than the others. It
940
967
  requires an account to update, but the other inputs are optional. If they're
941
968
  missing, it'll ignore those attributes. If they're present, it'll update them.
942
969
 
943
- ActiveInteraction generates predicate methods (like `#first_name?`) for your
944
- inputs. They will return `false` if the input is `nil` and `true` otherwise.
945
- Skip to [the predicates section](#predicates) for more information about them.
946
-
947
970
  ``` rb
948
971
  class UpdateAccount < ActiveInteraction::Base
949
972
  object :account
@@ -953,14 +976,14 @@ class UpdateAccount < ActiveInteraction::Base
953
976
 
954
977
  validates :first_name,
955
978
  presence: true,
956
- if: :first_name?
979
+ unless: -> { first_name.nil? }
957
980
  validates :last_name,
958
981
  presence: true,
959
- if: :last_name?
982
+ unless: -> { last_name.nil? }
960
983
 
961
984
  def execute
962
- account.first_name = first_name if first_name?
963
- account.last_name = last_name if last_name?
985
+ account.first_name = first_name if first_name.present?
986
+ account.last_name = last_name if last_name.present?
964
987
 
965
988
  unless account.save
966
989
  errors.merge!(account.errors)
@@ -1177,9 +1200,6 @@ def execute
1177
1200
  end
1178
1201
  ```
1179
1202
 
1180
- These types of errors will become standard with Rails 5. ActiveInteraction's
1181
- implementation is based off of [active_model-errors_details][].
1182
-
1183
1203
  ActiveInteraction also supports merging errors. This is useful if you want to
1184
1204
  delegate validation to some other object. For example, if you have an
1185
1205
  interaction that updates a record, you might want that record to validate
@@ -1375,41 +1395,6 @@ UpdateUser.run!(user: user, birthday: nil)
1375
1395
  UpdateUser.run!(user: user, birthday: Date.new(2000, 1, 2))
1376
1396
  ```
1377
1397
 
1378
- ### Predicates
1379
-
1380
- ActiveInteraction creates a predicate method for every input defined by a
1381
- filter. So if you have an input called `foo`, there will be a predicate method
1382
- called `#foo?`. That method will tell you if the input was given (that is, if
1383
- it was not `nil`).
1384
-
1385
- ``` rb
1386
- class SayHello < ActiveInteraction::Base
1387
- string :name,
1388
- default: nil
1389
-
1390
- def execute
1391
- if name?
1392
- "Hello, #{name}!"
1393
- else
1394
- "Howdy, stranger!"
1395
- end
1396
- end
1397
- end
1398
-
1399
- SayHello.run!(name: nil)
1400
- # => "Howdy, stranger!"
1401
- SayHello.run!(name: 'Taylor')
1402
- # => "Hello, Taylor!"
1403
- ```
1404
-
1405
- This can be confusing for boolean inputs. If you have some boolean input `foo`,
1406
- then the actual value of that input is available through `foo`. The associated
1407
- predicate method, `#foo?`, will tell you if that value is not `nil`. So it will
1408
- only be `false` if the input is optional and happens to be `nil`.
1409
-
1410
- See [the optional inputs section][] for help on determining if an input was
1411
- present in the input hash instead of just `nil`.
1412
-
1413
1398
  ### Translations
1414
1399
 
1415
1400
  ActiveInteraction is i18n aware out of the box! All you have to do is add
@@ -1462,8 +1447,8 @@ I18nInteraction.run(name: false).errors.messages[:name]
1462
1447
 
1463
1448
  ## Credits
1464
1449
 
1465
- ActiveInteraction is brought to you by [Aaron Lasseigne][] and
1466
- [Taylor Fausak][] and was originally built at [OrgSync][].
1450
+ ActiveInteraction is brought to you by [Aaron Lasseigne][].
1451
+ Along with Aaron, [Taylor Fausak][] helped create and maintain ActiveInteraction but has since moved on.
1467
1452
 
1468
1453
  If you want to contribute to ActiveInteraction, please read
1469
1454
  [our contribution guidelines][]. A [complete list of contributors][] is
@@ -1472,14 +1457,11 @@ available on GitHub.
1472
1457
  ActiveInteraction is licensed under [the MIT License][].
1473
1458
 
1474
1459
  [activeinteraction]: https://github.com/AaronLasseigne/active_interaction
1475
- [Full Documentation]: http://rubydoc.info/github/orgsync/active_interaction
1460
+ [API Documentation]: http://rubydoc.info/github/AaronLasseigne/active_interaction
1476
1461
  [semantic versioning]: http://semver.org/spec/v2.0.0.html
1477
1462
  [GitHub releases]: https://github.com/AaronLasseigne/active_interaction/releases
1478
- [the announcement post]: http://devblog.orgsync.com/2015/05/06/announcing-active-interaction-2/
1479
- [active_model-errors_details]: https://github.com/cowbell/active_model-errors_details
1480
1463
  [aaron lasseigne]: https://github.com/AaronLasseigne
1481
1464
  [taylor fausak]: https://github.com/tfausak
1482
- [orgsync]: https://github.com/orgsync
1483
1465
  [our contribution guidelines]: CONTRIBUTING.md
1484
1466
  [complete list of contributors]: https://github.com/AaronLasseigne/active_interaction/graphs/contributors
1485
1467
  [the mit license]: LICENSE.md
@@ -1488,5 +1470,4 @@ ActiveInteraction is licensed under [the MIT License][].
1488
1470
  [the filters section]: #filters
1489
1471
  [the errors section]: #errors
1490
1472
  [the optional inputs section]: #optional-inputs
1491
- [aire]: example
1492
1473
  [`with_options`]: http://api.rubyonrails.org/classes/Object.html#method-i-with_options