active_interaction 5.0.0 → 5.1.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 +14 -0
- data/CONTRIBUTING.md +11 -3
- data/README.md +200 -194
- data/lib/active_interaction/filters/array_filter.rb +11 -5
- data/lib/active_interaction/inputs.rb +6 -2
- data/lib/active_interaction/version.rb +1 -1
- data/lib/active_interaction.rb +0 -2
- data/spec/active_interaction/base_spec.rb +19 -8
- data/spec/active_interaction/errors_spec.rb +3 -3
- data/spec/active_interaction/filters/array_filter_spec.rb +14 -4
- data/spec/active_interaction/inputs_spec.rb +8 -0
- data/spec/active_interaction/integration/record_integration_spec.rb +5 -0
- data/spec/spec_helper.rb +4 -3
- metadata +53 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c740eef14e1c0bca42f4bbb5b3718c879bc3af038f40953a60a53007611916e5
|
4
|
+
data.tar.gz: b0a3738aa38869c7be559d72f23e958d0f9e66c0f490652bd8a92e1ef449e48e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f998483eb21ec13a43dcf3c7b6805f7865b3dd7b4d8fbc9846263338d676c41c7991a602dd3a82e13a9b93a027cbeccd5648a87fef688964124935d38d9ef6c4
|
7
|
+
data.tar.gz: 4669a1e76a427af4a62415f242bfdc3fa45f7a63c5a43de704b7849639841f365e83168b5c1db68ab067ca2104f8f7ec580baa487e5caa6035196607033c79f8
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
# [5.1.0][] (2022-07-28)
|
2
|
+
|
3
|
+
## Added
|
4
|
+
|
5
|
+
- Limit dependencies to the minimum requirements.
|
6
|
+
|
7
|
+
## Fixed
|
8
|
+
|
9
|
+
- [#536][] - `compose` accepts `Inputs`.
|
10
|
+
- [#537][] - Arrays with nested filters returned the wrong value.
|
11
|
+
|
1
12
|
# [5.0.0][] (2022-06-24)
|
2
13
|
|
3
14
|
## Changed
|
@@ -1101,6 +1112,7 @@ Example.run
|
|
1101
1112
|
|
1102
1113
|
- Initial release.
|
1103
1114
|
|
1115
|
+
[5.1.0]: https://github.com/AaronLasseigne/active_interaction/compare/v5.0.0...v5.1.0
|
1104
1116
|
[5.0.0]: https://github.com/AaronLasseigne/active_interaction/compare/v4.1.0...v5.0.0
|
1105
1117
|
[4.1.0]: https://github.com/AaronLasseigne/active_interaction/compare/v4.0.6...v4.1.0
|
1106
1118
|
[4.0.6]: https://github.com/AaronLasseigne/active_interaction/compare/v4.0.5...v4.0.6
|
@@ -1325,3 +1337,5 @@ Example.run
|
|
1325
1337
|
[#515]: https://github.com/AaronLasseigne/active_interaction/issues/515
|
1326
1338
|
[#518]: https://github.com/AaronLasseigne/active_interaction/issues/518
|
1327
1339
|
[#503]: https://github.com/AaronLasseigne/active_interaction/issues/503
|
1340
|
+
[#536]: https://github.com/AaronLasseigne/active_interaction/issues/536
|
1341
|
+
[#537]: https://github.com/AaronLasseigne/active_interaction/issues/537
|
data/CONTRIBUTING.md
CHANGED
@@ -1,13 +1,21 @@
|
|
1
|
-
#
|
1
|
+
# Contributing
|
2
|
+
|
3
|
+
Bug fixes and optimizations are welcome.
|
4
|
+
For optimizations please provide some reproducable proof of the improvement.
|
5
|
+
|
6
|
+
For features please open a [discussion][] and we can make sure the feature fits with the gem before working on it.
|
7
|
+
|
8
|
+
## Steps
|
2
9
|
|
3
10
|
1. [Fork][] the repo.
|
4
11
|
2. Add a breaking test for your change.
|
5
12
|
3. Make the tests pass.
|
6
13
|
4. Push your fork.
|
7
|
-
5. Submit a pull request.
|
14
|
+
5. Submit a pull request against the `next` branch.
|
8
15
|
|
9
|
-
|
16
|
+
## Code Style
|
10
17
|
|
11
18
|
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
19
|
|
13
20
|
[fork]: https://github.com/AaronLasseigne/active_interaction/fork
|
21
|
+
[discussion]: https://github.com/AaronLasseigne/active_interaction/discussions/categories/ideas
|
data/README.md
CHANGED
@@ -17,23 +17,25 @@ handles your verbs.
|
|
17
17
|
- [Basic usage](#basic-usage)
|
18
18
|
- [Validations](#validations)
|
19
19
|
- [Filters](#filters)
|
20
|
-
- [
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
- [
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
- [
|
20
|
+
- [Basic Filters](#basic-filters)
|
21
|
+
- [Array](#array)
|
22
|
+
- [Boolean](#boolean)
|
23
|
+
- [File](#file)
|
24
|
+
- [Hash](#hash)
|
25
|
+
- [String](#string)
|
26
|
+
- [Symbol](#symbol)
|
27
|
+
- [Dates and times](#dates-and-times)
|
28
|
+
- [Date](#date)
|
29
|
+
- [DateTime](#datetime)
|
30
|
+
- [Time](#time)
|
31
|
+
- [Numbers](#numbers)
|
32
|
+
- [Decimal](#decimal)
|
33
|
+
- [Float](#float)
|
34
|
+
- [Integer](#integer)
|
35
|
+
- [Advanced Filters](#advanced-filters)
|
36
|
+
- [Interface](#interface)
|
37
|
+
- [Object](#object)
|
38
|
+
- [Record](#record)
|
37
39
|
- [Rails](#rails)
|
38
40
|
- [Setup](#setup)
|
39
41
|
- [Controller](#controller)
|
@@ -63,13 +65,13 @@ handles your verbs.
|
|
63
65
|
Add it to your Gemfile:
|
64
66
|
|
65
67
|
``` rb
|
66
|
-
gem 'active_interaction', '~> 5.
|
68
|
+
gem 'active_interaction', '~> 5.1'
|
67
69
|
```
|
68
70
|
|
69
71
|
Or install it manually:
|
70
72
|
|
71
73
|
``` sh
|
72
|
-
$ gem install active_interaction --version '~> 5.
|
74
|
+
$ gem install active_interaction --version '~> 5.1'
|
73
75
|
```
|
74
76
|
|
75
77
|
This project uses [Semantic Versioning][]. Check out [GitHub releases][] for a
|
@@ -213,7 +215,9 @@ alternatives that can be reasonably coerced. Typically the coercions come from
|
|
213
215
|
Rails, so `"1"` can be interpreted as the boolean value `true`, the string
|
214
216
|
`"1"`, or the number `1`.
|
215
217
|
|
216
|
-
###
|
218
|
+
### Basic Filters
|
219
|
+
|
220
|
+
#### Array
|
217
221
|
|
218
222
|
In addition to accepting arrays, array inputs will convert
|
219
223
|
`ActiveRecord::Relation`s into arrays.
|
@@ -286,7 +290,7 @@ With `:index_errors` set to `false` the error would have been:
|
|
286
290
|
{:favorite_numbers=>[{:error=>:invalid_type, :type=>"array"}]}
|
287
291
|
```
|
288
292
|
|
289
|
-
|
293
|
+
#### Boolean
|
290
294
|
|
291
295
|
Boolean filters convert the strings `"1"`, `"true"`, and `"on"`
|
292
296
|
(case-insensitive) into `true`. They also convert `"0"`, `"false"`, and `"off"`
|
@@ -307,7 +311,7 @@ BooleanInteraction.run!(kool_aid: true)
|
|
307
311
|
# => "Oh yeah!"
|
308
312
|
```
|
309
313
|
|
310
|
-
|
314
|
+
#### File
|
311
315
|
|
312
316
|
File filters also accept `TempFile`s and anything that responds to `#rewind`.
|
313
317
|
That means that you can pass the `params` from uploading files via forms in
|
@@ -328,7 +332,7 @@ FileInteraction.run!(readme: File.open('README.md'))
|
|
328
332
|
# => 21563
|
329
333
|
```
|
330
334
|
|
331
|
-
|
335
|
+
#### Hash
|
332
336
|
|
333
337
|
Hash filters accept hashes. The expected value types are given by passing a
|
334
338
|
block and nesting other filters. You can have any number of filters inside a
|
@@ -380,166 +384,7 @@ hash :stuff,
|
|
380
384
|
strip: false
|
381
385
|
```
|
382
386
|
|
383
|
-
|
384
|
-
|
385
|
-
Interface filters allow you to specify an interface that the passed value must
|
386
|
-
meet in order to pass. The name of the interface is used to look for a constant
|
387
|
-
inside the ancestor listing for the passed value. This allows for a variety of
|
388
|
-
checks depending on what's passed. Class instances are checked for an included
|
389
|
-
module or an inherited ancestor class. Classes are checked for an extended
|
390
|
-
module or an inherited ancestor class. Modules are checked for an extended
|
391
|
-
module.
|
392
|
-
|
393
|
-
``` rb
|
394
|
-
class InterfaceInteraction < ActiveInteraction::Base
|
395
|
-
interface :exception
|
396
|
-
|
397
|
-
def execute
|
398
|
-
exception
|
399
|
-
end
|
400
|
-
end
|
401
|
-
|
402
|
-
InterfaceInteraction.run!(exception: Exception)
|
403
|
-
# ActiveInteraction::InvalidInteractionError: Exception is not a valid interface
|
404
|
-
InterfaceInteraction.run!(exception: NameError) # a subclass of Exception
|
405
|
-
# => NameError
|
406
|
-
```
|
407
|
-
|
408
|
-
You can use `:from` to specify a class or module. This would be the equivalent
|
409
|
-
of what's above.
|
410
|
-
|
411
|
-
```rb
|
412
|
-
class InterfaceInteraction < ActiveInteraction::Base
|
413
|
-
interface :error,
|
414
|
-
from: Exception
|
415
|
-
|
416
|
-
def execute
|
417
|
-
error
|
418
|
-
end
|
419
|
-
end
|
420
|
-
```
|
421
|
-
|
422
|
-
You can also create an anonymous interface on the fly by passing the `methods`
|
423
|
-
option.
|
424
|
-
|
425
|
-
``` rb
|
426
|
-
class InterfaceInteraction < ActiveInteraction::Base
|
427
|
-
interface :serializer,
|
428
|
-
methods: %i[dump load]
|
429
|
-
|
430
|
-
def execute
|
431
|
-
input = '{ "is_json" : true }'
|
432
|
-
object = serializer.load(input)
|
433
|
-
output = serializer.dump(object)
|
434
|
-
|
435
|
-
output
|
436
|
-
end
|
437
|
-
end
|
438
|
-
|
439
|
-
require 'json'
|
440
|
-
|
441
|
-
InterfaceInteraction.run!(serializer: Object.new)
|
442
|
-
# ActiveInteraction::InvalidInteractionError: Serializer is not a valid interface
|
443
|
-
InterfaceInteraction.run!(serializer: JSON)
|
444
|
-
# => "{\"is_json\":true}"
|
445
|
-
```
|
446
|
-
|
447
|
-
### Object
|
448
|
-
|
449
|
-
Object filters allow you to require an instance of a particular class or one of
|
450
|
-
its subclasses.
|
451
|
-
|
452
|
-
``` rb
|
453
|
-
class Cow
|
454
|
-
def moo
|
455
|
-
'Moo!'
|
456
|
-
end
|
457
|
-
end
|
458
|
-
|
459
|
-
class ObjectInteraction < ActiveInteraction::Base
|
460
|
-
object :cow
|
461
|
-
|
462
|
-
def execute
|
463
|
-
cow.moo
|
464
|
-
end
|
465
|
-
end
|
466
|
-
|
467
|
-
ObjectInteraction.run!(cow: Object.new)
|
468
|
-
# ActiveInteraction::InvalidInteractionError: Cow is not a valid object
|
469
|
-
ObjectInteraction.run!(cow: Cow.new)
|
470
|
-
# => "Moo!"
|
471
|
-
```
|
472
|
-
|
473
|
-
The class name is automatically determined by the filter name. If your filter
|
474
|
-
name is different than your class name, use the `class` option. It can be
|
475
|
-
either the class, a string, or a symbol.
|
476
|
-
|
477
|
-
``` rb
|
478
|
-
object :dolly1,
|
479
|
-
class: Sheep
|
480
|
-
object :dolly2,
|
481
|
-
class: 'Sheep'
|
482
|
-
object :dolly3,
|
483
|
-
class: :Sheep
|
484
|
-
```
|
485
|
-
|
486
|
-
If you have value objects or you would like to build one object from another,
|
487
|
-
you can use the `converter` option. It is only called if the value provided is
|
488
|
-
not an instance of the class or one of its subclasses. The `converter` option
|
489
|
-
accepts a symbol that specifies a class method on the object class or a proc.
|
490
|
-
Both will be passed the value and any errors thrown inside the converter will
|
491
|
-
cause the value to be considered invalid. Any returned value that is not the
|
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.
|
494
|
-
|
495
|
-
``` rb
|
496
|
-
class ObjectInteraction < ActiveInteraction::Base
|
497
|
-
object :ip_address,
|
498
|
-
class: IPAddr,
|
499
|
-
converter: :new
|
500
|
-
|
501
|
-
def execute
|
502
|
-
ip_address
|
503
|
-
end
|
504
|
-
end
|
505
|
-
|
506
|
-
ObjectInteraction.run!(ip_address: '192.168.1.1')
|
507
|
-
# #<IPAddr: IPv4:192.168.1.1/255.255.255.255>
|
508
|
-
|
509
|
-
ObjectInteraction.run!(ip_address: 1)
|
510
|
-
# ActiveInteraction::InvalidInteractionError: Ip address is not a valid object
|
511
|
-
```
|
512
|
-
|
513
|
-
### Record
|
514
|
-
|
515
|
-
Record filters allow you to require an instance of a particular class (or one
|
516
|
-
of its subclasses) or a value that can be used to locate an instance of the
|
517
|
-
object. If the value does not match, it will call `find` on the class of the
|
518
|
-
record. This is particularly useful when working with ActiveRecord objects.
|
519
|
-
Like an object filter, the class is derived from the name passed but can be
|
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`.
|
523
|
-
|
524
|
-
``` rb
|
525
|
-
class RecordInteraction < ActiveInteraction::Base
|
526
|
-
record :encoding
|
527
|
-
|
528
|
-
def execute
|
529
|
-
encoding
|
530
|
-
end
|
531
|
-
end
|
532
|
-
|
533
|
-
> RecordInteraction.run!(encoding: Encoding::US_ASCII)
|
534
|
-
=> #<Encoding:US-ASCII>
|
535
|
-
|
536
|
-
> RecordInteraction.run!(encoding: 'ascii')
|
537
|
-
=> #<Encoding:US-ASCII>
|
538
|
-
```
|
539
|
-
|
540
|
-
A different method can be specified by providing a symbol to the `finder` option.
|
541
|
-
|
542
|
-
### String
|
387
|
+
#### String
|
543
388
|
|
544
389
|
String filters define inputs that only accept strings.
|
545
390
|
|
@@ -566,7 +411,7 @@ string :comment,
|
|
566
411
|
strip: false
|
567
412
|
```
|
568
413
|
|
569
|
-
|
414
|
+
#### Symbol
|
570
415
|
|
571
416
|
Symbol filters define inputs that accept symbols. Strings will be converted
|
572
417
|
into symbols.
|
@@ -586,7 +431,7 @@ SymbolInteraction.run!(method: :object_id)
|
|
586
431
|
# => #<Proc:0x007fdc9ba94118>
|
587
432
|
```
|
588
433
|
|
589
|
-
|
434
|
+
#### Dates and times
|
590
435
|
|
591
436
|
Filters that work with dates and times behave similarly. By default, they all
|
592
437
|
convert strings into their expected data types using `.parse`. Blank strings
|
@@ -594,7 +439,7 @@ will be treated as `nil`. If you give the `format` option, they will instead
|
|
594
439
|
convert strings using `.strptime`. Note that formats won't work with `DateTime`
|
595
440
|
and `Time` filters if a time zone is set.
|
596
441
|
|
597
|
-
|
442
|
+
##### Date
|
598
443
|
|
599
444
|
``` rb
|
600
445
|
class DateInteraction < ActiveInteraction::Base
|
@@ -616,7 +461,7 @@ date :birthday,
|
|
616
461
|
format: '%Y-%m-%d'
|
617
462
|
```
|
618
463
|
|
619
|
-
|
464
|
+
##### DateTime
|
620
465
|
|
621
466
|
``` rb
|
622
467
|
class DateTimeInteraction < ActiveInteraction::Base
|
@@ -638,7 +483,7 @@ date_time :start,
|
|
638
483
|
format: '%Y-%m-%dT%H:%M:%S'
|
639
484
|
```
|
640
485
|
|
641
|
-
|
486
|
+
##### Time
|
642
487
|
|
643
488
|
In addition to converting strings with `.parse` (or `.strptime`), time filters
|
644
489
|
convert numbers with `.at`.
|
@@ -663,13 +508,13 @@ time :start,
|
|
663
508
|
format: '%Y-%m-%dT%H:%M:%S'
|
664
509
|
```
|
665
510
|
|
666
|
-
|
511
|
+
#### Numbers
|
667
512
|
|
668
513
|
All numeric filters accept numeric input. They will also convert strings using
|
669
514
|
the appropriate method from `Kernel` (like `.Float`). Blank strings will be
|
670
515
|
treated as `nil`.
|
671
516
|
|
672
|
-
|
517
|
+
##### Decimal
|
673
518
|
|
674
519
|
``` rb
|
675
520
|
class DecimalInteraction < ActiveInteraction::Base
|
@@ -693,7 +538,7 @@ decimal :dollars,
|
|
693
538
|
digits: 2
|
694
539
|
```
|
695
540
|
|
696
|
-
|
541
|
+
##### Float
|
697
542
|
|
698
543
|
``` rb
|
699
544
|
class FloatInteraction < ActiveInteraction::Base
|
@@ -710,7 +555,7 @@ FloatInteraction.run!(x: 2.1)
|
|
710
555
|
# => 4.41
|
711
556
|
```
|
712
557
|
|
713
|
-
|
558
|
+
##### Integer
|
714
559
|
|
715
560
|
``` rb
|
716
561
|
class IntegerInteraction < ActiveInteraction::Base
|
@@ -751,6 +596,167 @@ IntegerInteraction.run!(limit1: "08", limit2: "08", limit3: "08")
|
|
751
596
|
ActiveInteraction::InvalidInteractionError: Limit2 is not a valid integer, Limit3 is not a valid integer
|
752
597
|
```
|
753
598
|
|
599
|
+
### Advanced Filters
|
600
|
+
|
601
|
+
#### Interface
|
602
|
+
|
603
|
+
Interface filters allow you to specify an interface that the passed value must
|
604
|
+
meet in order to pass. The name of the interface is used to look for a constant
|
605
|
+
inside the ancestor listing for the passed value. This allows for a variety of
|
606
|
+
checks depending on what's passed. Class instances are checked for an included
|
607
|
+
module or an inherited ancestor class. Classes are checked for an extended
|
608
|
+
module or an inherited ancestor class. Modules are checked for an extended
|
609
|
+
module.
|
610
|
+
|
611
|
+
``` rb
|
612
|
+
class InterfaceInteraction < ActiveInteraction::Base
|
613
|
+
interface :exception
|
614
|
+
|
615
|
+
def execute
|
616
|
+
exception
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
InterfaceInteraction.run!(exception: Exception)
|
621
|
+
# ActiveInteraction::InvalidInteractionError: Exception is not a valid interface
|
622
|
+
InterfaceInteraction.run!(exception: NameError) # a subclass of Exception
|
623
|
+
# => NameError
|
624
|
+
```
|
625
|
+
|
626
|
+
You can use `:from` to specify a class or module. This would be the equivalent
|
627
|
+
of what's above.
|
628
|
+
|
629
|
+
```rb
|
630
|
+
class InterfaceInteraction < ActiveInteraction::Base
|
631
|
+
interface :error,
|
632
|
+
from: Exception
|
633
|
+
|
634
|
+
def execute
|
635
|
+
error
|
636
|
+
end
|
637
|
+
end
|
638
|
+
```
|
639
|
+
|
640
|
+
You can also create an anonymous interface on the fly by passing the `methods`
|
641
|
+
option.
|
642
|
+
|
643
|
+
``` rb
|
644
|
+
class InterfaceInteraction < ActiveInteraction::Base
|
645
|
+
interface :serializer,
|
646
|
+
methods: %i[dump load]
|
647
|
+
|
648
|
+
def execute
|
649
|
+
input = '{ "is_json" : true }'
|
650
|
+
object = serializer.load(input)
|
651
|
+
output = serializer.dump(object)
|
652
|
+
|
653
|
+
output
|
654
|
+
end
|
655
|
+
end
|
656
|
+
|
657
|
+
require 'json'
|
658
|
+
|
659
|
+
InterfaceInteraction.run!(serializer: Object.new)
|
660
|
+
# ActiveInteraction::InvalidInteractionError: Serializer is not a valid interface
|
661
|
+
InterfaceInteraction.run!(serializer: JSON)
|
662
|
+
# => "{\"is_json\":true}"
|
663
|
+
```
|
664
|
+
|
665
|
+
#### Object
|
666
|
+
|
667
|
+
Object filters allow you to require an instance of a particular class or one of
|
668
|
+
its subclasses.
|
669
|
+
|
670
|
+
``` rb
|
671
|
+
class Cow
|
672
|
+
def moo
|
673
|
+
'Moo!'
|
674
|
+
end
|
675
|
+
end
|
676
|
+
|
677
|
+
class ObjectInteraction < ActiveInteraction::Base
|
678
|
+
object :cow
|
679
|
+
|
680
|
+
def execute
|
681
|
+
cow.moo
|
682
|
+
end
|
683
|
+
end
|
684
|
+
|
685
|
+
ObjectInteraction.run!(cow: Object.new)
|
686
|
+
# ActiveInteraction::InvalidInteractionError: Cow is not a valid object
|
687
|
+
ObjectInteraction.run!(cow: Cow.new)
|
688
|
+
# => "Moo!"
|
689
|
+
```
|
690
|
+
|
691
|
+
The class name is automatically determined by the filter name. If your filter
|
692
|
+
name is different than your class name, use the `class` option. It can be
|
693
|
+
either the class, a string, or a symbol.
|
694
|
+
|
695
|
+
``` rb
|
696
|
+
object :dolly1,
|
697
|
+
class: Sheep
|
698
|
+
object :dolly2,
|
699
|
+
class: 'Sheep'
|
700
|
+
object :dolly3,
|
701
|
+
class: :Sheep
|
702
|
+
```
|
703
|
+
|
704
|
+
If you have value objects or you would like to build one object from another,
|
705
|
+
you can use the `converter` option. It is only called if the value provided is
|
706
|
+
not an instance of the class or one of its subclasses. The `converter` option
|
707
|
+
accepts a symbol that specifies a class method on the object class or a proc.
|
708
|
+
Both will be passed the value and any errors thrown inside the converter will
|
709
|
+
cause the value to be considered invalid. Any returned value that is not the
|
710
|
+
correct class will also be treated as invalid. Any `default` that is not an
|
711
|
+
instance of the class or subclass and is not `nil` will also be converted.
|
712
|
+
|
713
|
+
``` rb
|
714
|
+
class ObjectInteraction < ActiveInteraction::Base
|
715
|
+
object :ip_address,
|
716
|
+
class: IPAddr,
|
717
|
+
converter: :new
|
718
|
+
|
719
|
+
def execute
|
720
|
+
ip_address
|
721
|
+
end
|
722
|
+
end
|
723
|
+
|
724
|
+
ObjectInteraction.run!(ip_address: '192.168.1.1')
|
725
|
+
# #<IPAddr: IPv4:192.168.1.1/255.255.255.255>
|
726
|
+
|
727
|
+
ObjectInteraction.run!(ip_address: 1)
|
728
|
+
# ActiveInteraction::InvalidInteractionError: Ip address is not a valid object
|
729
|
+
```
|
730
|
+
|
731
|
+
#### Record
|
732
|
+
|
733
|
+
Record filters allow you to require an instance of a particular class (or one
|
734
|
+
of its subclasses) or a value that can be used to locate an instance of the
|
735
|
+
object. If the value does not match, it will call `find` on the class of the
|
736
|
+
record. This is particularly useful when working with ActiveRecord objects.
|
737
|
+
Like an object filter, the class is derived from the name passed but can be
|
738
|
+
specified with the `class` option. Any `default` that is not an instance of the
|
739
|
+
class or subclass and is not `nil` will also be found. Blank strings passed in
|
740
|
+
will be treated as `nil`.
|
741
|
+
|
742
|
+
``` rb
|
743
|
+
class RecordInteraction < ActiveInteraction::Base
|
744
|
+
record :encoding
|
745
|
+
|
746
|
+
def execute
|
747
|
+
encoding
|
748
|
+
end
|
749
|
+
end
|
750
|
+
|
751
|
+
> RecordInteraction.run!(encoding: Encoding::US_ASCII)
|
752
|
+
=> #<Encoding:US-ASCII>
|
753
|
+
|
754
|
+
> RecordInteraction.run!(encoding: 'ascii')
|
755
|
+
=> #<Encoding:US-ASCII>
|
756
|
+
```
|
757
|
+
|
758
|
+
A different method can be specified by providing a symbol to the `finder` option.
|
759
|
+
|
754
760
|
## Rails
|
755
761
|
|
756
762
|
ActiveInteraction plays nicely with Rails. You can use interactions to handle
|
@@ -1492,13 +1498,13 @@ ActiveInteraction is licensed under [the MIT License][].
|
|
1492
1498
|
|
1493
1499
|
[activeinteraction]: https://github.com/AaronLasseigne/active_interaction
|
1494
1500
|
[API Documentation]: http://rubydoc.info/github/AaronLasseigne/active_interaction
|
1495
|
-
[
|
1501
|
+
[Semantic Versioning]: http://semver.org/spec/v2.0.0.html
|
1496
1502
|
[GitHub releases]: https://github.com/AaronLasseigne/active_interaction/releases
|
1497
1503
|
[aaron lasseigne]: https://github.com/AaronLasseigne
|
1498
1504
|
[taylor fausak]: https://github.com/tfausak
|
1499
1505
|
[our contribution guidelines]: CONTRIBUTING.md
|
1500
1506
|
[complete list of contributors]: https://github.com/AaronLasseigne/active_interaction/graphs/contributors
|
1501
|
-
[the
|
1507
|
+
[the MIT License]: LICENSE.md
|
1502
1508
|
[formtastic]: https://rubygems.org/gems/formtastic
|
1503
1509
|
[simple_form]: https://rubygems.org/gems/simple_form
|
1504
1510
|
[the filters section]: #filters
|
@@ -49,8 +49,10 @@ module ActiveInteraction
|
|
49
49
|
children = []
|
50
50
|
|
51
51
|
unless filters.empty?
|
52
|
-
value.
|
53
|
-
|
52
|
+
value.map! do |item|
|
53
|
+
result = filters[:'0'].process(item, context)
|
54
|
+
children.push(result)
|
55
|
+
result.value
|
54
56
|
end
|
55
57
|
end
|
56
58
|
|
@@ -60,11 +62,15 @@ module ActiveInteraction
|
|
60
62
|
private
|
61
63
|
|
62
64
|
def index_errors?
|
65
|
+
klass = 'ActiveRecord'.safe_constantize
|
66
|
+
|
63
67
|
default =
|
64
|
-
if
|
65
|
-
|
68
|
+
if !klass
|
69
|
+
false
|
70
|
+
elsif klass.respond_to?(:index_nested_attribute_errors)
|
71
|
+
klass.index_nested_attribute_errors # Moved to here in Rails 7.0
|
66
72
|
else
|
67
|
-
::
|
73
|
+
klass::Base.index_nested_attribute_errors
|
68
74
|
end
|
69
75
|
options.fetch(:index_errors, default)
|
70
76
|
end
|
@@ -175,10 +175,14 @@ module ActiveInteraction
|
|
175
175
|
end
|
176
176
|
|
177
177
|
def convert(inputs)
|
178
|
+
return inputs if inputs.is_a?(self.class)
|
178
179
|
return inputs.stringify_keys if inputs.is_a?(Hash)
|
179
|
-
return inputs.to_unsafe_h.stringify_keys if inputs.is_a?(ActionController::Parameters)
|
180
180
|
|
181
|
-
|
181
|
+
parameters = 'ActionController::Parameters'
|
182
|
+
klass = parameters.safe_constantize
|
183
|
+
return inputs.to_unsafe_h.stringify_keys if klass && inputs.is_a?(klass)
|
184
|
+
|
185
|
+
raise ArgumentError, 'inputs must be a hash, ActiveInteraction::Inputs, or ActionController::Parameters'
|
182
186
|
end
|
183
187
|
|
184
188
|
def assign_to_grouped_input!(inputs, key, index, value)
|
data/lib/active_interaction.rb
CHANGED
@@ -294,17 +294,28 @@ describe ActiveInteraction::Base do
|
|
294
294
|
let(:z) { rand }
|
295
295
|
|
296
296
|
context 'with valid composition' do
|
297
|
-
|
298
|
-
inputs
|
299
|
-
|
300
|
-
|
297
|
+
context 'when inputs is a hash' do
|
298
|
+
let(:inputs) { { x: x, z: z } }
|
299
|
+
|
300
|
+
it 'is valid' do
|
301
|
+
expect(outcome).to be_valid
|
302
|
+
end
|
301
303
|
|
302
|
-
|
303
|
-
|
304
|
+
it 'returns the sum' do
|
305
|
+
expect(result).to eql x + z
|
306
|
+
end
|
304
307
|
end
|
305
308
|
|
306
|
-
|
307
|
-
|
309
|
+
context 'when inputs is an ActiveInteraction::Inputs' do
|
310
|
+
let(:inputs) { ActiveInteraction::Inputs.new({ x: x, z: z }, described_class.new) }
|
311
|
+
|
312
|
+
it 'is valid' do
|
313
|
+
expect(outcome).to be_valid
|
314
|
+
end
|
315
|
+
|
316
|
+
it 'returns the sum' do
|
317
|
+
expect(result).to eql x + z
|
318
|
+
end
|
308
319
|
end
|
309
320
|
end
|
310
321
|
|
@@ -123,10 +123,10 @@ describe ActiveInteraction::Errors do
|
|
123
123
|
let(:other) { described_class.new(klass.new) }
|
124
124
|
|
125
125
|
before do
|
126
|
-
if
|
127
|
-
allow(
|
126
|
+
if ActiveRecord.respond_to?(:index_nested_attribute_errors)
|
127
|
+
allow(ActiveRecord).to receive(:index_nested_attribute_errors).and_return(true)
|
128
128
|
else
|
129
|
-
allow(
|
129
|
+
allow(ActiveRecord::Base).to receive(:index_nested_attribute_errors).and_return(true)
|
130
130
|
end
|
131
131
|
|
132
132
|
other.add(:'array[0]')
|
@@ -66,6 +66,16 @@ describe ActiveInteraction::ArrayFilter, :filter do
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
+
context 'with a nested filter where the value transforms' do
|
70
|
+
let(:block) { proc { symbol } }
|
71
|
+
let(:name) { :test }
|
72
|
+
let(:value) { ['test'] }
|
73
|
+
|
74
|
+
it 'returns the transformed value' do
|
75
|
+
expect(result.value).to eql [:test]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
69
79
|
context 'with a nested filter' do
|
70
80
|
let(:block) { proc { array } }
|
71
81
|
|
@@ -81,7 +91,7 @@ describe ActiveInteraction::ArrayFilter, :filter do
|
|
81
91
|
expect(result.children.size).to be 2
|
82
92
|
result.children.each do |child|
|
83
93
|
expect(child).to be_an_instance_of ActiveInteraction::ArrayInput
|
84
|
-
expect(child.value).to
|
94
|
+
expect(child.value).to eql child_value
|
85
95
|
end
|
86
96
|
end
|
87
97
|
|
@@ -136,10 +146,10 @@ describe ActiveInteraction::ArrayFilter, :filter do
|
|
136
146
|
|
137
147
|
context 'when ActiveRecord.index_nested_attribute_errors is true' do
|
138
148
|
before do
|
139
|
-
if
|
140
|
-
allow(
|
149
|
+
if ActiveRecord.respond_to?(:index_nested_attribute_errors)
|
150
|
+
allow(ActiveRecord).to receive(:index_nested_attribute_errors).and_return(true)
|
141
151
|
else
|
142
|
-
allow(
|
152
|
+
allow(ActiveRecord::Base).to receive(:index_nested_attribute_errors).and_return(true)
|
143
153
|
end
|
144
154
|
end
|
145
155
|
|
@@ -44,6 +44,14 @@ describe ActiveInteraction::Inputs do
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
context 'with ActiveInteraction::Inputs inputs' do
|
48
|
+
let(:args) { described_class.new({ key: :value }, base_class.new) }
|
49
|
+
|
50
|
+
it 'does not raise an error' do
|
51
|
+
expect { result }.to_not raise_error
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
47
55
|
context 'with ActionController::Parameters inputs' do
|
48
56
|
let(:args) { ::ActionController::Parameters.new }
|
49
57
|
|
data/spec/spec_helper.rb
CHANGED
@@ -2,6 +2,7 @@ require 'i18n'
|
|
2
2
|
I18n.config.enforce_available_locales = true if I18n.config.respond_to?(:enforce_available_locales)
|
3
3
|
|
4
4
|
require 'active_interaction'
|
5
|
+
require 'active_record'
|
5
6
|
|
6
7
|
Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
|
7
8
|
|
@@ -10,10 +11,10 @@ RSpec.configure do |config|
|
|
10
11
|
config.filter_run_including :focus
|
11
12
|
|
12
13
|
config.before(:suite) do
|
13
|
-
if
|
14
|
-
|
14
|
+
if ActiveRecord.respond_to?(:index_nested_attribute_errors)
|
15
|
+
ActiveRecord.index_nested_attribute_errors = false
|
15
16
|
else
|
16
|
-
|
17
|
+
ActiveRecord::Base.index_nested_attribute_errors = false
|
17
18
|
end
|
18
19
|
end
|
19
20
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_interaction
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aaron Lasseigne
|
@@ -9,10 +9,10 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2022-
|
12
|
+
date: 2022-07-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
15
|
+
name: activemodel
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
18
|
- - ">="
|
@@ -31,6 +31,54 @@ dependencies:
|
|
31
31
|
- - "<"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '8'
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: activesupport
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '5.2'
|
41
|
+
- - "<"
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '8'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '5.2'
|
51
|
+
- - "<"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '8'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: actionpack
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: activerecord
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
34
82
|
- !ruby/object:Gem::Dependency
|
35
83
|
name: kramdown
|
36
84
|
requirement: !ruby/object:Gem::Requirement
|
@@ -241,6 +289,7 @@ files:
|
|
241
289
|
- spec/active_interaction/integration/integer_interaction_spec.rb
|
242
290
|
- spec/active_interaction/integration/interface_interaction_spec.rb
|
243
291
|
- spec/active_interaction/integration/object_interaction_spec.rb
|
292
|
+
- spec/active_interaction/integration/record_integration_spec.rb
|
244
293
|
- spec/active_interaction/integration/string_interaction_spec.rb
|
245
294
|
- spec/active_interaction/integration/symbol_interaction_spec.rb
|
246
295
|
- spec/active_interaction/integration/time_interaction_spec.rb
|
@@ -318,6 +367,7 @@ test_files:
|
|
318
367
|
- spec/active_interaction/integration/integer_interaction_spec.rb
|
319
368
|
- spec/active_interaction/integration/interface_interaction_spec.rb
|
320
369
|
- spec/active_interaction/integration/object_interaction_spec.rb
|
370
|
+
- spec/active_interaction/integration/record_integration_spec.rb
|
321
371
|
- spec/active_interaction/integration/string_interaction_spec.rb
|
322
372
|
- spec/active_interaction/integration/symbol_interaction_spec.rb
|
323
373
|
- spec/active_interaction/integration/time_interaction_spec.rb
|