kind 2.0.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -1
- data/README.md +401 -173
- data/lib/kind.rb +47 -39
- data/lib/kind/active_model/kind_validator.rb +2 -2
- data/lib/kind/checker.rb +3 -63
- data/lib/kind/checker/factory.rb +35 -0
- data/lib/kind/checker/protocol.rb +73 -0
- data/lib/kind/error.rb +1 -1
- data/lib/kind/maybe.rb +90 -17
- data/lib/kind/types.rb +15 -4
- data/lib/kind/undefined.rb +1 -1
- data/lib/kind/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31c64b5797c2bc7b2b1da5a704af8f66ddd34773296a8f5b70fc92ffd8b66246
|
4
|
+
data.tar.gz: ceac47580bc50b6f7b346193ec2c8d9429c7e26726219ee45ea8a5b56051a8e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b94da43b377655485792594548f3a8d2e321ce313cd88ef6ff7b9bca1fba2411a602eaba99b2fa6be0d28e4054242ca0bc19c51df8ea7c08898455a09c0b345a
|
7
|
+
data.tar.gz: c1ac05bf6246d5a9f10f80bdec7adab1c62b87bc40bd0d9e57b9532e5de2c8598bf97de1f12d062d31e494543a87b4b68410c2581adb9ddbbf12088d2e100427
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -18,28 +18,37 @@ One of the goals of this project is to do simple type checking like `"some strin
|
|
18
18
|
- [Required Ruby version](#required-ruby-version)
|
19
19
|
- [Installation](#installation)
|
20
20
|
- [Usage](#usage)
|
21
|
-
- [Verifying the kind of some object](#verifying-the-kind-of-some-object)
|
22
|
-
|
21
|
+
- [Kind.of.\<Type\>() - Verifying the kind of some object](#kindoftype---verifying-the-kind-of-some-object)
|
22
|
+
- [Method aliases to perform a strict validation](#method-aliases-to-perform-a-strict-validation)
|
23
|
+
- [Kind.of.\<Type\>.or_nil()](#kindoftypeor_nil)
|
24
|
+
- [Kind.of.\<Type\>.instance?()](#kindoftypeinstance)
|
25
|
+
- [Kind.is.\<Type\>() - Verifying if some class/module is the expected kind.](#kindistype---verifying-if-some-classmodule-is-the-expected-kind)
|
23
26
|
- [How to create a new type checker?](#how-to-create-a-new-type-checker)
|
24
27
|
- [Creating/Verifiyng type checkers dynamically](#creatingverifiyng-type-checkers-dynamically)
|
25
|
-
- [Registering new (custom) type
|
28
|
+
- [Registering new (custom) type checker](#registering-new-custom-type-checker)
|
26
29
|
- [What happens if a custom type checker has a namespace?](#what-happens-if-a-custom-type-checker-has-a-namespace)
|
27
30
|
- [Type checkers](#type-checkers)
|
28
31
|
- [Classes' type checkers](#classes-type-checkers)
|
29
32
|
- [Modules' type checkers](#modules-type-checkers)
|
30
33
|
- [Specials' type checkers](#specials-type-checkers)
|
34
|
+
- [Kind::Validator (ActiveModel::Validations)](#kindvalidator-activemodelvalidations)
|
35
|
+
- [Usage](#usage-1)
|
36
|
+
- [Defining the default validation strategy](#defining-the-default-validation-strategy)
|
37
|
+
- [Using the `allow_nil` and `strict` options](#using-the-allow_nil-and-strict-options)
|
31
38
|
- [Kind::Undefined](#kindundefined)
|
32
39
|
- [Kind.of.\<Type\>.or_undefined()](#kindoftypeor_undefined)
|
33
40
|
- [Kind::Maybe](#kindmaybe)
|
34
|
-
|
35
|
-
- [Kind::Maybe#
|
41
|
+
- [Replacing blocks by lambdas](#replacing-blocks-by-lambdas)
|
42
|
+
- [Kind::Maybe[], Kind::Maybe.wrap() and Kind::Maybe#then method aliases](#kindmaybe-kindmaybewrap-and-kindmaybethen-method-aliases)
|
43
|
+
- [Replacing blocks by lambdas](#replacing-blocks-by-lambdas-1)
|
44
|
+
- [Kind::None() and Kind::Some()](#kindnone-and-kindsome)
|
36
45
|
- [Kind.of.Maybe()](#kindofmaybe)
|
37
46
|
- [Kind::Optional](#kindoptional)
|
47
|
+
- [Replacing blocks by lambdas](#replacing-blocks-by-lambdas-2)
|
38
48
|
- [Kind.of.\<Type\>.as_optional](#kindoftypeas_optional)
|
39
|
-
- [Kind::
|
40
|
-
- [
|
41
|
-
|
42
|
-
- [Using the `allow_nil` and `strict` options](#using-the-allow_nil-and-strict-options)
|
49
|
+
- [Kind::Maybe(<Type>)](#kindmaybetype)
|
50
|
+
- [Kind::Maybe#try](#kindmaybetry)
|
51
|
+
- [Kind::Maybe#try!](#kindmaybetry-1)
|
43
52
|
- [Kind::Empty](#kindempty)
|
44
53
|
- [Similar Projects](#similar-projects)
|
45
54
|
- [Development](#development)
|
@@ -82,14 +91,14 @@ sum(1, 1) # 2
|
|
82
91
|
sum('1', 1) # Kind::Error ("\"1\" expected to be a kind of Numeric")
|
83
92
|
```
|
84
93
|
|
85
|
-
### Verifying the kind of some object
|
94
|
+
### Kind.of.\<Type\>() - Verifying the kind of some object
|
86
95
|
|
87
96
|
By default, basic verifications are strict. So, when you perform `Kind.of.Hash(value)`, if the given value was a Hash, the value itself will be returned, but if it isn't the right type, an error will be raised.
|
88
97
|
|
89
98
|
```ruby
|
90
|
-
Kind.of.Hash(nil)
|
91
|
-
Kind.of.Hash('')
|
92
|
-
Kind.of.Hash(
|
99
|
+
Kind.of.Hash(nil) # **raise Kind::Error, "nil expected to be a kind of Hash"**
|
100
|
+
Kind.of.Hash('') # raise Kind::Error, "'' expected to be a kind of Hash"
|
101
|
+
Kind.of.Hash(a: 1) # {a: 1}
|
93
102
|
|
94
103
|
# ---
|
95
104
|
|
@@ -99,14 +108,12 @@ Kind.of.Boolean(false) # false
|
|
99
108
|
```
|
100
109
|
|
101
110
|
> **Note:** `Kind.of.<Type>` supports the to_proc protocol.
|
102
|
-
>
|
103
|
-
> when the value has the desired kind and false if it hasn't.
|
111
|
+
> And it will perform a strict validation as expected.
|
104
112
|
|
105
113
|
```ruby
|
106
114
|
collection = [ {number: 1}, 'number 2', {number: 3}, :number_4 ]
|
107
115
|
|
108
|
-
collection
|
109
|
-
.select(&Kind.of.Hash) # [{number: 1}, {number: 3}]
|
116
|
+
collection.map(&Kind.of.Hash) # Kind::Error ("number 2" expected to be a kind of Hash)
|
110
117
|
```
|
111
118
|
|
112
119
|
When the verified value is nil, it is possible to define a default value with the same type to be returned.
|
@@ -123,6 +130,24 @@ Kind.of.Boolean(nil, or: true) # true
|
|
123
130
|
|
124
131
|
> **Note:** As an alternative syntax, you can use the `Kind::Of` instead of the `Kind.of` method. e.g: `Kind::Of::Hash('')`
|
125
132
|
|
133
|
+
#### Method aliases to perform a strict validation
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
Kind.of.Hash[nil] # raise Kind::Error, "nil expected to be a kind of Hash"
|
137
|
+
Kind.of.Hash[''] # raise Kind::Error, "'' expected to be a kind of Hash"
|
138
|
+
Kind.of.Hash[a: 1] # {a: 1}
|
139
|
+
Kind.of.Hash['', or: {}] # {}
|
140
|
+
|
141
|
+
# or
|
142
|
+
|
143
|
+
Kind.of.Hash.instance(nil) # raise Kind::Error, "nil expected to be a kind of Hash"
|
144
|
+
Kind.of.Hash.instance('') # raise Kind::Error, "'' expected to be a kind of Hash"
|
145
|
+
Kind.of.Hash.instance(a: 1) # {a: 1}
|
146
|
+
Kind.of.Hash.instance('', or: {}) # {}
|
147
|
+
```
|
148
|
+
|
149
|
+
### Kind.of.\<Type\>.or_nil()
|
150
|
+
|
126
151
|
But if you don't need a strict type verification, use the `.or_nil` method.
|
127
152
|
|
128
153
|
```ruby
|
@@ -135,17 +160,24 @@ Kind.of.Boolean.or_nil('') # nil
|
|
135
160
|
Kind.of.Boolean.or_nil(true) # true
|
136
161
|
```
|
137
162
|
|
138
|
-
|
163
|
+
### Kind.of.\<Type\>.instance?()
|
164
|
+
|
165
|
+
Use the method `.instance?` to verify if the given object has the expected type.
|
139
166
|
|
140
167
|
```ruby
|
141
|
-
Kind.of.Hash.instance?(
|
142
|
-
#
|
168
|
+
Kind.of.Hash.instance?({}) # true
|
169
|
+
Kind.of.Hash.instance?({}, HashWithIndifferentAccess.new) # true
|
170
|
+
|
171
|
+
Kind.of.Hash.instance?('') # false
|
172
|
+
Kind.of.Hash.instance?({}, '') # false
|
143
173
|
|
144
174
|
# ---
|
145
175
|
|
146
|
-
Kind.of.Boolean.instance?(
|
147
|
-
Kind.of.Boolean.instance?(true)
|
148
|
-
|
176
|
+
Kind.of.Boolean.instance?(true) # true
|
177
|
+
Kind.of.Boolean.instance?(true, false) # true
|
178
|
+
|
179
|
+
Kind.of.Boolean.instance?(nil) # false
|
180
|
+
Kind.of.Boolean.instance?(false, true, nil) # false
|
149
181
|
```
|
150
182
|
|
151
183
|
> **Note:** When `.instance?` is called without an argument,
|
@@ -155,27 +187,35 @@ Kind.of.Boolean.instance?(false) # true
|
|
155
187
|
collection = [ {number: 1}, 'number 2', {number: 3}, :number_4 ]
|
156
188
|
|
157
189
|
collection
|
158
|
-
.select(&Kind.of.Hash.instance?)
|
159
|
-
.reduce(0) { |total, item| total + item.fetch(:number, 0) } # 4
|
190
|
+
.select(&Kind.of.Hash.instance?) # [{:number=>1}, {:number=>3}]
|
160
191
|
```
|
161
192
|
|
162
|
-
|
193
|
+
> **Note:** You can use a different syntax to perform an instance verification.
|
194
|
+
> To do this, use Kind.of.\<Type\>?()
|
163
195
|
|
164
196
|
```ruby
|
165
|
-
Kind.of.Hash
|
166
|
-
Kind.of.Hash
|
167
|
-
Kind.of.Hash[a: 1] # {a: 1}
|
168
|
-
Kind.of.Hash['', or: {}] # {}
|
197
|
+
Kind.of.Hash?({}) # true
|
198
|
+
Kind.of.Hash?({}, HashWithIndifferentAccess.new) # true
|
169
199
|
|
170
|
-
#
|
200
|
+
Kind.of.Hash?('') # false
|
201
|
+
Kind.of.Hash?({}, '') # false
|
171
202
|
|
172
|
-
|
173
|
-
|
174
|
-
Kind.of.
|
175
|
-
Kind.of.
|
203
|
+
# ---
|
204
|
+
|
205
|
+
Kind.of.Boolean?(true) # true
|
206
|
+
Kind.of.Boolean?(false, true) # true
|
207
|
+
|
208
|
+
Kind.of.Boolean?(nil) # false
|
209
|
+
Kind.of.Boolean?(false, true, nil) # false
|
210
|
+
|
211
|
+
# ---
|
212
|
+
|
213
|
+
collection = [ {number: 1}, 'number 2', {number: 3}, :number_4 ]
|
214
|
+
|
215
|
+
collection.select(&Kind.of.Hash?) # [{:number=>1}, {:number=>3}]
|
176
216
|
```
|
177
217
|
|
178
|
-
###
|
218
|
+
### Kind.is.\<Type\>() - Verifying if some class/module is the expected kind.
|
179
219
|
|
180
220
|
You can use `Kind.is` to verify if some class has the expected type as its ancestor.
|
181
221
|
|
@@ -262,6 +302,22 @@ Kind.of(User, {}) # Kind::Error ({} expected to be a kind of User)
|
|
262
302
|
Kind.of(Hash, {}) # {}
|
263
303
|
Kind.of(Hash, user) # Kind::Error (<User ...> expected to be a kind of Hash)
|
264
304
|
|
305
|
+
# ----------------------------------------- #
|
306
|
+
# Verifiyng if the value is a kind instance #
|
307
|
+
# ----------------------------------------- #
|
308
|
+
|
309
|
+
Kind.of?(Numeric, 1) # true
|
310
|
+
Kind.of?(Numeric, 1, 2.0) # true
|
311
|
+
|
312
|
+
Kind.of?(Numeric, '1') # false
|
313
|
+
Kind.of?(Numeric, 1, '2.0') # false
|
314
|
+
|
315
|
+
# Note: Kind.of?(Type) without arguments will return a
|
316
|
+
# lambda that will perform an instance verification
|
317
|
+
#
|
318
|
+
[1, '2', 3.0, '4']
|
319
|
+
.select(&Kind.of?(Numeric)) # [1, 3.0]
|
320
|
+
|
265
321
|
# ---------------------------------- #
|
266
322
|
# Creating type checkers dynamically #
|
267
323
|
# ---------------------------------- #
|
@@ -300,7 +356,7 @@ end
|
|
300
356
|
Kind.is(User, AdminUser) # true
|
301
357
|
```
|
302
358
|
|
303
|
-
#### Registering new (custom) type
|
359
|
+
#### Registering new (custom) type checker
|
304
360
|
|
305
361
|
Use `Kind::Types.add()`. e.g:
|
306
362
|
|
@@ -427,6 +483,158 @@ The list of types (classes and modules) available to use with `Kind.of.*` or `Ki
|
|
427
483
|
|
428
484
|
[⬆️ Back to Top](#table-of-contents-)
|
429
485
|
|
486
|
+
## Kind::Validator (ActiveModel::Validations)
|
487
|
+
|
488
|
+
This module enables the capability to validate types via [`ActiveModel::Validations >= 3.2, < 6.1.0`](https://api.rubyonrails.org/classes/ActiveModel/Validations.html). e.g
|
489
|
+
|
490
|
+
```ruby
|
491
|
+
class Person
|
492
|
+
include ActiveModel::Validations
|
493
|
+
|
494
|
+
attr_accessor :first_name, :last_name
|
495
|
+
|
496
|
+
validates :first_name, :last_name, kind: String
|
497
|
+
end
|
498
|
+
```
|
499
|
+
|
500
|
+
And to make use of it, you will need to do an explicitly require. e.g:
|
501
|
+
|
502
|
+
```ruby
|
503
|
+
# In some Gemfile
|
504
|
+
gem 'kind', require: 'kind/active_model/validation'
|
505
|
+
|
506
|
+
# In some .rb file
|
507
|
+
require 'kind/active_model/validation'
|
508
|
+
```
|
509
|
+
|
510
|
+
### Usage
|
511
|
+
|
512
|
+
**[Object#kind_of?](https://ruby-doc.org/core-2.6.4/Object.html#method-i-kind_of-3F)**
|
513
|
+
|
514
|
+
```ruby
|
515
|
+
validates :name, kind: { of: String }
|
516
|
+
|
517
|
+
# Use an array to verify if the attribute
|
518
|
+
# is an instance of one of the classes/modules.
|
519
|
+
|
520
|
+
validates :status, kind: { of: [String, Symbol]}
|
521
|
+
```
|
522
|
+
|
523
|
+
**[Kind.is](#verifying-the-kind-of-some-classmodule)**
|
524
|
+
|
525
|
+
```ruby
|
526
|
+
#
|
527
|
+
# Verifying if the attribute value is the class or a subclass.
|
528
|
+
#
|
529
|
+
class Human; end
|
530
|
+
class Person < Human; end
|
531
|
+
class User < Human; end
|
532
|
+
|
533
|
+
validates :human_kind, kind: { is: Human }
|
534
|
+
|
535
|
+
#
|
536
|
+
# Verifying if the attribute value is the module or if it is a class that includes the module
|
537
|
+
#
|
538
|
+
module Human; end
|
539
|
+
class Person; include Human; end
|
540
|
+
class User; include Human; end
|
541
|
+
|
542
|
+
validates :human_kind, kind: { is: Human }
|
543
|
+
|
544
|
+
#
|
545
|
+
# Verifying if the attribute value is the module or if it is a module that extends the module
|
546
|
+
#
|
547
|
+
module Human; end
|
548
|
+
module Person; extend Human; end
|
549
|
+
module User; extend Human; end
|
550
|
+
|
551
|
+
validates :human_kind, kind: { is: Human }
|
552
|
+
|
553
|
+
# or use an array to verify if the attribute
|
554
|
+
# is a kind of one those classes/modules.
|
555
|
+
|
556
|
+
validates :human_kind, kind: { is: [Person, User] }
|
557
|
+
```
|
558
|
+
|
559
|
+
**[Object#instance_of?](https://ruby-doc.org/core-2.6.4/Object.html#method-i-instance_of-3F)**
|
560
|
+
|
561
|
+
```ruby
|
562
|
+
validates :name, kind: { instance_of: String }
|
563
|
+
|
564
|
+
# or use an array to verify if the attribute
|
565
|
+
# is an instance of one of the classes/modules.
|
566
|
+
|
567
|
+
validates :name, kind: { instance_of: [String, Symbol] }
|
568
|
+
```
|
569
|
+
|
570
|
+
|
571
|
+
**[Object#respond_to?](https://ruby-doc.org/core-2.6.4/Object.html#method-i-respond_to-3F)**
|
572
|
+
|
573
|
+
```ruby
|
574
|
+
validates :handler, kind: { respond_to: :call }
|
575
|
+
```
|
576
|
+
|
577
|
+
**Array.new.all? { |item| item.kind_of?(Class) }**
|
578
|
+
|
579
|
+
```ruby
|
580
|
+
validates :account_types, kind: { array_of: String }
|
581
|
+
|
582
|
+
# or use an array to verify if the attribute
|
583
|
+
# is an instance of one of the classes
|
584
|
+
|
585
|
+
validates :account_types, kind: { array_of: [String, Symbol] }
|
586
|
+
```
|
587
|
+
|
588
|
+
**Array.new.all? { |item| expected_values.include?(item) }**
|
589
|
+
|
590
|
+
```ruby
|
591
|
+
# Verifies if the attribute value
|
592
|
+
# is an array with some or all the expected values.
|
593
|
+
|
594
|
+
validates :account_types, kind: { array_with: ['foo', 'bar'] }
|
595
|
+
```
|
596
|
+
|
597
|
+
#### Defining the default validation strategy
|
598
|
+
|
599
|
+
By default, you can define the attribute type directly (without a hash). e.g.
|
600
|
+
|
601
|
+
```ruby
|
602
|
+
validates :name, kind: String
|
603
|
+
# or
|
604
|
+
validates :name, kind: [String, Symbol]
|
605
|
+
```
|
606
|
+
|
607
|
+
To changes this behavior you can set another strategy to validates the attributes types:
|
608
|
+
|
609
|
+
```ruby
|
610
|
+
Kind::Validator.default_strategy = :instance_of
|
611
|
+
|
612
|
+
# Tip: Create an initializer if you are in a Rails application.
|
613
|
+
```
|
614
|
+
|
615
|
+
And these are the available options to define the default strategy:
|
616
|
+
- `kind_of` *(default)*
|
617
|
+
- `instance_of`
|
618
|
+
|
619
|
+
#### Using the `allow_nil` and `strict` options
|
620
|
+
|
621
|
+
You can use the `allow_nil` option with any of the kind validations. e.g.
|
622
|
+
|
623
|
+
```ruby
|
624
|
+
validates :name, kind: String, allow_nil: true
|
625
|
+
```
|
626
|
+
|
627
|
+
And as any active model validation, kind validations works with the `strict: true`
|
628
|
+
option and with the `validates!` method. e.g.
|
629
|
+
|
630
|
+
```ruby
|
631
|
+
validates :first_name, kind: String, strict: true
|
632
|
+
# or
|
633
|
+
validates! :last_name, kind: String
|
634
|
+
```
|
635
|
+
|
636
|
+
[⬆️ Back to Top](#table-of-contents-)
|
637
|
+
|
430
638
|
## Kind::Undefined
|
431
639
|
|
432
640
|
The [`Kind::Undefined`](https://github.com/serradura/kind/blob/834f6b8ebdc737de8e5628986585f30c1a5aa41b/lib/kind/undefined.rb) constant is used as the default argument of type checkers. This is necessary [to know if no arguments were passed to the type check methods](https://github.com/serradura/kind/blob/834f6b8ebdc737de8e5628986585f30c1a5aa41b/lib/kind.rb#L45-L48). But, you can use it in your codebase too, especially if you need to distinguish the usage of `nil` as a method argument.
|
@@ -491,7 +699,27 @@ puts optional.value_or(1) # 1
|
|
491
699
|
puts optional.value_or { 1 } # 1
|
492
700
|
```
|
493
701
|
|
494
|
-
|
702
|
+
#### Replacing blocks by lambdas
|
703
|
+
|
704
|
+
```ruby
|
705
|
+
Add = -> params do
|
706
|
+
a, b = Kind.of.Hash(params, or: Empty::HASH).values_at(:a, :b)
|
707
|
+
|
708
|
+
a + b if Kind.of.Numeric?(a, b)
|
709
|
+
end
|
710
|
+
|
711
|
+
# --
|
712
|
+
|
713
|
+
Kind::Maybe.new(a: 1, b: 2).map(&Add).value_or(0) # 3
|
714
|
+
|
715
|
+
# --
|
716
|
+
|
717
|
+
Kind::Maybe.new([]).map(&Add).value_or(0) # 0
|
718
|
+
Kind::Maybe.new({}).map(&Add).value_or(0) # 0
|
719
|
+
Kind::Maybe.new(nil).map(&Add).value_or(0) # 0
|
720
|
+
```
|
721
|
+
|
722
|
+
### Kind::Maybe[], Kind::Maybe.wrap() and Kind::Maybe#then method aliases
|
495
723
|
|
496
724
|
You can use `Kind::Maybe[]` (brackets) instead of the `.new` to transform values in a `Kind::Maybe`. Another alias is `.then` to the `.map` method.
|
497
725
|
|
@@ -505,43 +733,70 @@ result =
|
|
505
733
|
puts result # 42
|
506
734
|
```
|
507
735
|
|
508
|
-
|
736
|
+
You can also use `Kind::Maybe.wrap()` instead of the `.new` method.
|
509
737
|
|
510
|
-
|
738
|
+
```ruby
|
739
|
+
result =
|
740
|
+
Kind::Maybe
|
741
|
+
.wrap(5)
|
742
|
+
.then { |value| value * 5 }
|
743
|
+
.then { |value| value + 17 }
|
744
|
+
.value_or(0)
|
745
|
+
|
746
|
+
puts result # 42
|
747
|
+
```
|
748
|
+
|
749
|
+
#### Replacing blocks by lambdas
|
511
750
|
|
512
751
|
```ruby
|
513
|
-
|
752
|
+
Add = -> params do
|
753
|
+
a, b = Kind.of.Hash(params, or: Empty::HASH).values_at(:a, :b)
|
514
754
|
|
515
|
-
|
755
|
+
a + b if Kind.of.Numeric.instance?(a, b)
|
756
|
+
end
|
516
757
|
|
517
|
-
|
758
|
+
# --
|
518
759
|
|
519
|
-
|
760
|
+
Kind::Maybe[a: 1, b: 2].then(&Add).value_or(0) # 3
|
520
761
|
|
521
|
-
|
762
|
+
# --
|
522
763
|
|
523
|
-
|
524
|
-
#
|
525
|
-
|
764
|
+
Kind::Maybe[1].then(&Add).value_or(0) # 0
|
765
|
+
Kind::Maybe['2'].then(&Add).value_or(0) # 0
|
766
|
+
Kind::Maybe[nil].then(&Add).value_or(0) # 0
|
767
|
+
```
|
526
768
|
|
527
|
-
|
769
|
+
### Kind::None() and Kind::Some()
|
528
770
|
|
529
|
-
|
771
|
+
If you need to ensure the return of `Kind::Maybe` results from your methods/lambdas,
|
772
|
+
you could use the methods `Kind::None` and `Kind::Some` to do this. e.g:
|
530
773
|
|
531
|
-
|
774
|
+
```ruby
|
775
|
+
Add = -> params do
|
776
|
+
a, b = Kind.of.Hash(params, or: Empty::HASH).values_at(:a, :b)
|
532
777
|
|
533
|
-
|
534
|
-
# Kind::Undefined value #
|
535
|
-
#########################
|
778
|
+
return Kind::None unless Kind.of.Numeric?(a, b)
|
536
779
|
|
537
|
-
|
780
|
+
Kind::Some(a + b)
|
781
|
+
end
|
538
782
|
|
539
|
-
|
783
|
+
# --
|
540
784
|
|
541
|
-
|
542
|
-
|
785
|
+
Add.call(1) # #<Kind::Maybe::None:0x0000... @value=nil>
|
786
|
+
Add.call({}) # #<Kind::Maybe::None:0x0000... @value=nil>
|
787
|
+
Add.call(a: 1) # #<Kind::Maybe::None:0x0000... @value=nil>
|
788
|
+
Add.call(b: 2) # #<Kind::Maybe::None:0x0000... @value=nil>
|
543
789
|
|
544
|
-
|
790
|
+
Add.call(a:1, b: 2) # #<Kind::Maybe::Some:0x0000... @value=3>
|
791
|
+
|
792
|
+
# --
|
793
|
+
|
794
|
+
Kind::Maybe[a: 1, b: 2].then(&Add).value_or(0) # 3
|
795
|
+
|
796
|
+
Kind::Maybe[1].then(&Add).value_or(0) # 0
|
797
|
+
Kind::Maybe['2'].then(&Add).value_or(0) # 0
|
798
|
+
Kind::Maybe[nil].then(&Add).value_or(0) # 0
|
799
|
+
```
|
545
800
|
|
546
801
|
### Kind.of.Maybe()
|
547
802
|
|
@@ -598,6 +853,26 @@ result2 =
|
|
598
853
|
puts result2 # 35
|
599
854
|
```
|
600
855
|
|
856
|
+
#### Replacing blocks by lambdas
|
857
|
+
|
858
|
+
```ruby
|
859
|
+
Add = -> params do
|
860
|
+
a, b = Kind.of.Hash(params, or: Empty::HASH).values_at(:a, :b)
|
861
|
+
|
862
|
+
a + b if Kind.of.Numeric.instance?(a, b)
|
863
|
+
end
|
864
|
+
|
865
|
+
# --
|
866
|
+
|
867
|
+
Kind::Optional[a: 1, b: 2].then(&Add).value_or(0) # 3
|
868
|
+
|
869
|
+
# --
|
870
|
+
|
871
|
+
Kind::Optional[1].then(&Add).value_or(0) # 0
|
872
|
+
Kind::Optional['2'].then(&Add).value_or(0) # 0
|
873
|
+
Kind::Optional[nil].then(&Add).value_or(0) # 0
|
874
|
+
```
|
875
|
+
|
601
876
|
**Note:** The `Kind.of.Optional` is available to check if some value is a `Kind::Optional`.
|
602
877
|
|
603
878
|
[⬆️ Back to Top](#table-of-contents-)
|
@@ -609,10 +884,11 @@ In these scenarios, you could check the given input type as optional and avoid u
|
|
609
884
|
|
610
885
|
```ruby
|
611
886
|
def person_name(params)
|
612
|
-
Kind::Of::Hash
|
613
|
-
|
614
|
-
|
615
|
-
|
887
|
+
Kind::Of::Hash
|
888
|
+
.as_optional(params)
|
889
|
+
.map { |data| data if data.values_at(:first_name, :last_name).compact.size == 2 }
|
890
|
+
.map { |data| "#{data[:first_name]} #{data[:last_name]}" }
|
891
|
+
.value_or { 'John Doe' }
|
616
892
|
end
|
617
893
|
|
618
894
|
person_name('') # "John Doe"
|
@@ -633,9 +909,20 @@ def person_name(params)
|
|
633
909
|
'John Doe'
|
634
910
|
end
|
635
911
|
end
|
912
|
+
|
913
|
+
#
|
914
|
+
# You can also use Kind::Optional(<Type>) to achieve the same behavior
|
915
|
+
#
|
916
|
+
def person_name(params)
|
917
|
+
Kind::Optional(Hash)
|
918
|
+
.wrap(params)
|
919
|
+
.map { |data| data if data.values_at(:first_name, :last_name).compact.size == 2 }
|
920
|
+
.map { |data| "#{data[:first_name]} #{data[:last_name]}" }
|
921
|
+
.value_or { 'John Doe' }
|
922
|
+
end
|
636
923
|
```
|
637
924
|
|
638
|
-
> Note: You could use the `.as_optional` method (or it alias
|
925
|
+
> Note: You could use the `.as_optional` method (or it alias `.as_maybe`) with any [type checker](https://github.com/serradura/kind/blob/b177fed9cc2b3347d63963a2a2fd99f989c51a9a/README.md#type-checkers).
|
639
926
|
|
640
927
|
Let's see another example using a collection and how the method `.as_optional` works when it receives no argument.
|
641
928
|
|
@@ -709,156 +996,97 @@ end
|
|
709
996
|
|
710
997
|
[⬆️ Back to Top](#table-of-contents-)
|
711
998
|
|
712
|
-
|
999
|
+
### Kind::Maybe(<Type>)
|
713
1000
|
|
714
|
-
|
1001
|
+
There is an alternative to `Kind.of.\<Type\>.as_optional`, you can use `Kind::Optional(<Type>)` to create a maybe monad which will return None if the given input hasn't the expected type. e.g:
|
715
1002
|
|
716
1003
|
```ruby
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
1004
|
+
result1 =
|
1005
|
+
Kind::Maybe(Numeric)
|
1006
|
+
.wrap(5)
|
1007
|
+
.then { |value| value * 5 }
|
1008
|
+
.value_or { 0 }
|
721
1009
|
|
722
|
-
|
723
|
-
end
|
724
|
-
```
|
1010
|
+
puts result1 # 25
|
725
1011
|
|
726
|
-
|
1012
|
+
# ---
|
727
1013
|
|
728
|
-
|
729
|
-
|
730
|
-
|
1014
|
+
result2 =
|
1015
|
+
Kind::Optional(Numeric)
|
1016
|
+
.wrap('5')
|
1017
|
+
.then { |value| value * 5 }
|
1018
|
+
.value_or { 0 }
|
731
1019
|
|
732
|
-
|
733
|
-
require 'kind/active_model/validation'
|
1020
|
+
puts result2 # 0
|
734
1021
|
```
|
735
1022
|
|
736
|
-
|
737
|
-
|
738
|
-
**[Object#kind_of?](https://ruby-doc.org/core-2.6.4/Object.html#method-i-kind_of-3F)**
|
1023
|
+
This typed maybe has the same methods of `Kind::Maybe` class. e.g:
|
739
1024
|
|
740
1025
|
```ruby
|
741
|
-
|
1026
|
+
Kind::Maybe(Numeric)[5]
|
1027
|
+
Kind::Maybe(Numeric).new(5)
|
1028
|
+
Kind::Maybe(Numeric).wrap(5)
|
742
1029
|
|
743
|
-
#
|
744
|
-
# is an instance of one of the classes/modules.
|
1030
|
+
# ---
|
745
1031
|
|
746
|
-
|
1032
|
+
Kind::Optional(Numeric)[5]
|
1033
|
+
Kind::Optional(Numeric).new(5)
|
1034
|
+
Kind::Optional(Numeric).wrap(5)
|
747
1035
|
```
|
748
1036
|
|
749
|
-
|
750
|
-
|
751
|
-
```ruby
|
752
|
-
#
|
753
|
-
# Verifying if the attribute value is the class or a subclass.
|
754
|
-
#
|
755
|
-
class Human; end
|
756
|
-
class Person < Human; end
|
757
|
-
class User < Human; end
|
758
|
-
|
759
|
-
validates :human_kind, kind: { is: Human }
|
760
|
-
|
761
|
-
#
|
762
|
-
# Verifying if the attribute value is the module or if it is a class that includes the module
|
763
|
-
#
|
764
|
-
module Human; end
|
765
|
-
class Person; include Human; end
|
766
|
-
class User; include Human; end
|
767
|
-
|
768
|
-
validates :human_kind, kind: { is: Human }
|
769
|
-
|
770
|
-
#
|
771
|
-
# Verifying if the attribute value is the module or if it is a module that extends the module
|
772
|
-
#
|
773
|
-
module Human; end
|
774
|
-
module Person; extend Human; end
|
775
|
-
module User; extend Human; end
|
776
|
-
|
777
|
-
validates :human_kind, kind: { is: Human }
|
778
|
-
|
779
|
-
# or use an array to verify if the attribute
|
780
|
-
# is a kind of one those classes/modules.
|
1037
|
+
[⬆️ Back to Top](#table-of-contents-)
|
781
1038
|
|
782
|
-
|
783
|
-
```
|
1039
|
+
### Kind::Maybe#try
|
784
1040
|
|
785
|
-
|
1041
|
+
If you don't want to use `#map/#then` to access the value, you could use the `#try` method to access it. So, if the value wasn't `nil` or `Kind::Undefined`, the some monad will be returned.
|
786
1042
|
|
787
1043
|
```ruby
|
788
|
-
|
1044
|
+
object = 'foo'
|
789
1045
|
|
790
|
-
#
|
791
|
-
# is an instance of one of the classes/modules.
|
1046
|
+
Kind::Maybe[object].try(:upcase).value # "FOO"
|
792
1047
|
|
793
|
-
|
794
|
-
```
|
1048
|
+
Kind::Maybe[{}].try(:fetch, :number, 0).value # 0
|
795
1049
|
|
1050
|
+
Kind::Maybe[{number: 1}].try(:fetch, :number).value # 1
|
796
1051
|
|
797
|
-
|
1052
|
+
Kind::Maybe[object].try { |value| value.upcase }.value # "FOO"
|
798
1053
|
|
799
|
-
|
800
|
-
|
801
|
-
|
1054
|
+
#############
|
1055
|
+
# Nil value #
|
1056
|
+
#############
|
802
1057
|
|
803
|
-
|
1058
|
+
object = nil
|
804
1059
|
|
805
|
-
|
806
|
-
validates :account_types, kind: { array_of: String }
|
1060
|
+
Kind::Maybe[object].try(:upcase).value # nil
|
807
1061
|
|
808
|
-
|
809
|
-
# is an instance of one of the classes
|
1062
|
+
Kind::Maybe[object].try { |value| value.upcase }.value # nil
|
810
1063
|
|
811
|
-
|
812
|
-
|
1064
|
+
#########################
|
1065
|
+
# Kind::Undefined value #
|
1066
|
+
#########################
|
813
1067
|
|
814
|
-
|
1068
|
+
object = Kind::Undefined
|
815
1069
|
|
816
|
-
|
817
|
-
# Verifies if the attribute value
|
818
|
-
# is an array with some or all the expected values.
|
1070
|
+
Kind::Maybe[object].try(:upcase).value # nil
|
819
1071
|
|
820
|
-
|
1072
|
+
Kind::Maybe[object].try { |value| value.upcase }.value # nil
|
821
1073
|
```
|
822
1074
|
|
823
|
-
|
1075
|
+
> **Note:** You can use the `#try` method with `Kind::Optional` objects.
|
824
1076
|
|
825
|
-
|
1077
|
+
[⬆️ Back to Top](#table-of-contents-)
|
826
1078
|
|
827
|
-
|
828
|
-
validates :name, kind: String
|
829
|
-
# or
|
830
|
-
validates :name, kind: [String, Symbol]
|
831
|
-
```
|
1079
|
+
### Kind::Maybe#try!
|
832
1080
|
|
833
|
-
|
1081
|
+
Has the same behavior of its `#try`, but it will raise an error if the value doesn't respond to the expected method.
|
834
1082
|
|
835
1083
|
```ruby
|
836
|
-
Kind::
|
837
|
-
|
838
|
-
# Tip: Create an initializer if you are in a Rails application.
|
839
|
-
```
|
840
|
-
|
841
|
-
And these are the available options to define the default strategy:
|
842
|
-
- `is_a`
|
843
|
-
- `kind_of` *(default)*
|
844
|
-
- `instance_of`
|
845
|
-
|
846
|
-
#### Using the `allow_nil` and `strict` options
|
1084
|
+
Kind::Maybe[{}].try(:upcase) # => #<Kind::Maybe::None:0x0000... @value=nil>
|
847
1085
|
|
848
|
-
|
849
|
-
|
850
|
-
```ruby
|
851
|
-
validates :name, kind: String, allow_nil: true
|
1086
|
+
Kind::Maybe[{}].try!(:upcase) # => NoMethodError (undefined method `upcase' for {}:Hash)
|
852
1087
|
```
|
853
1088
|
|
854
|
-
|
855
|
-
option and with the `validates!` method. e.g.
|
856
|
-
|
857
|
-
```ruby
|
858
|
-
validates :first_name, kind: String, strict: true
|
859
|
-
# or
|
860
|
-
validates! :last_name, kind: String
|
861
|
-
```
|
1089
|
+
> **Note:** You can also use the `#try!` method with `Kind::Optional` objects.
|
862
1090
|
|
863
1091
|
[⬆️ Back to Top](#table-of-contents-)
|
864
1092
|
|