kind 2.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
|