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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ebd68f3e676f6b030fa85da36b57a47a7f866e52373aed2b0e41315c63b0fdd0
4
- data.tar.gz: 5800122e2ed54025450eabe06bee5f9df3474c45b6ffa396c3b80dc4ad8ca566
3
+ metadata.gz: 31c64b5797c2bc7b2b1da5a704af8f66ddd34773296a8f5b70fc92ffd8b66246
4
+ data.tar.gz: ceac47580bc50b6f7b346193ec2c8d9429c7e26726219ee45ea8a5b56051a8e0
5
5
  SHA512:
6
- metadata.gz: d3769fbbe39073a41961af28fde90412f30f1caab3b2129b4a91a39afb1e482310e209b1a5eea4249f09c667760f862c06cc98dc56ab7bb40854561ef75ea435
7
- data.tar.gz: 8356c7a49afe6370ecced9485acd2a663d1f17729cbbccd89dec2c1f839ad0f754778502e447c89b8a129697a891bed07af30c20df0b52a9e4d338cb4df593ed
6
+ metadata.gz: b94da43b377655485792594548f3a8d2e321ce313cd88ef6ff7b9bca1fba2411a602eaba99b2fa6be0d28e4054242ca0bc19c51df8ea7c08898455a09c0b345a
7
+ data.tar.gz: c1ac05bf6246d5a9f10f80bdec7adab1c62b87bc40bd0d9e57b9532e5de2c8598bf97de1f12d062d31e494543a87b4b68410c2581adb9ddbbf12088d2e100427
@@ -3,7 +3,10 @@ language: ruby
3
3
 
4
4
  sudo: false
5
5
 
6
- cache: bundler
6
+ cache:
7
+ bundler: true
8
+ directories:
9
+ - /home/travis/.rvm/
7
10
 
8
11
  rvm:
9
12
  - 2.2.0
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
- - [Verifying the kind of some class/module](#verifying-the-kind-of-some-classmodule)
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 checkers](#registering-new-custom-type-checkers)
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
- - [Kind::Maybe[] and Kind::Maybe#then](#kindmaybe-and-kindmaybethen)
35
- - [Kind::Maybe#try](#kindmaybetry)
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::Validator (ActiveModel::Validations)](#kindvalidator-activemodelvalidations)
40
- - [Usage](#usage-1)
41
- - [Defining the default validation strategy](#defining-the-default-validation-strategy)
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) # **raise Kind::Error, "nil expected to be a kind of Hash"**
91
- Kind.of.Hash('') # raise Kind::Error, "'' expected to be a kind of Hash"
92
- Kind.of.Hash({a: 1}) # {a: 1}
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
- > But it won't perform a strict validation, instead, it will return true
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
- And just for convenience, you can use the method `.instance?` to verify if the given object has the expected type.
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
- # false
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?('') # false
147
- Kind.of.Boolean.instance?(true) # true
148
- Kind.of.Boolean.instance?(false) # true
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
- Also, there are aliases to perform the strict type verification. e.g:
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[nil] # raise Kind::Error, "nil expected to be a kind of Hash"
166
- Kind.of.Hash[''] # raise Kind::Error, "'' expected to be a 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
- # or
200
+ Kind.of.Hash?('') # false
201
+ Kind.of.Hash?({}, '') # false
171
202
 
172
- Kind.of.Hash.instance(nil) # raise Kind::Error, "nil expected to be a kind of Hash"
173
- Kind.of.Hash.instance('') # raise Kind::Error, "'' expected to be a kind of Hash"
174
- Kind.of.Hash.instance(a: 1) # {a: 1}
175
- Kind.of.Hash.instance('', or: {}) # {}
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
- ### Verifying the kind of some class/module
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 checkers
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
- ### Kind::Maybe[] and Kind::Maybe#then
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
- ### Kind::Maybe#try
736
+ You can also use `Kind::Maybe.wrap()` instead of the `.new` method.
509
737
 
510
- If you don't want to use a map to access the value, you could use the `#try` method to access it. So, if the value wasn't `nil` or `Kind::Undefined`, it will be returned.
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
- object = 'foo'
752
+ Add = -> params do
753
+ a, b = Kind.of.Hash(params, or: Empty::HASH).values_at(:a, :b)
514
754
 
515
- p Kind::Maybe[object].try(:upcase) # "FOO"
755
+ a + b if Kind.of.Numeric.instance?(a, b)
756
+ end
516
757
 
517
- p Kind::Maybe[{}].try(:fetch, :number, 0) # 0
758
+ # --
518
759
 
519
- p Kind::Maybe[{number: 1}].try(:fetch, :number) # 1
760
+ Kind::Maybe[a: 1, b: 2].then(&Add).value_or(0) # 3
520
761
 
521
- p Kind::Maybe[object].try { |value| value.upcase } # "FOO"
762
+ # --
522
763
 
523
- #############
524
- # Nil value #
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
- object = nil
769
+ ### Kind::None() and Kind::Some()
528
770
 
529
- p Kind::Maybe[object].try(:upcase) # nil
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
- p Kind::Maybe[object].try { |value| value.upcase } # nil
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
- object = Kind::Undefined
780
+ Kind::Some(a + b)
781
+ end
538
782
 
539
- p Kind::Maybe[object].try(:upcase) # nil
783
+ # --
540
784
 
541
- p Kind::Maybe[object].try { |value| value.upcase } # nil
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
- [⬆️ Back to Top](#table-of-contents-)
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.as_optional(params)
613
- .map { |data| data if data.values_at(:first_name, :last_name).compact.size == 2 }
614
- .map { |data| "#{data[:first_name]} #{data[:last_name]}" }
615
- .value_or { 'John Doe' }
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 `as_maybe`) with any [type checker](https://github.com/serradura/kind/blob/b177fed9cc2b3347d63963a2a2fd99f989c51a9a/README.md#type-checkers).
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
- ## Kind::Validator (ActiveModel::Validations)
999
+ ### Kind::Maybe(<Type>)
713
1000
 
714
- 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
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
- class Person
718
- include ActiveModel::Validations
719
-
720
- attr_accessor :first_name, :last_name
1004
+ result1 =
1005
+ Kind::Maybe(Numeric)
1006
+ .wrap(5)
1007
+ .then { |value| value * 5 }
1008
+ .value_or { 0 }
721
1009
 
722
- validates :first_name, :last_name, kind: String
723
- end
724
- ```
1010
+ puts result1 # 25
725
1011
 
726
- And to make use of it, you will need to do an explicitly require. e.g:
1012
+ # ---
727
1013
 
728
- ```ruby
729
- # In some Gemfile
730
- gem 'kind', require: 'kind/active_model/validation'
1014
+ result2 =
1015
+ Kind::Optional(Numeric)
1016
+ .wrap('5')
1017
+ .then { |value| value * 5 }
1018
+ .value_or { 0 }
731
1019
 
732
- # In some .rb file
733
- require 'kind/active_model/validation'
1020
+ puts result2 # 0
734
1021
  ```
735
1022
 
736
- ### Usage
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
- validates :name, kind: { of: String }
1026
+ Kind::Maybe(Numeric)[5]
1027
+ Kind::Maybe(Numeric).new(5)
1028
+ Kind::Maybe(Numeric).wrap(5)
742
1029
 
743
- # Use an array to verify if the attribute
744
- # is an instance of one of the classes/modules.
1030
+ # ---
745
1031
 
746
- validates :status, kind: { of: [String, Symbol]}
1032
+ Kind::Optional(Numeric)[5]
1033
+ Kind::Optional(Numeric).new(5)
1034
+ Kind::Optional(Numeric).wrap(5)
747
1035
  ```
748
1036
 
749
- **[Kind.is](#verifying-the-kind-of-some-classmodule)**
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
- validates :human_kind, kind: { is: [Person, User] }
783
- ```
1039
+ ### Kind::Maybe#try
784
1040
 
785
- **[Object#instance_of?](https://ruby-doc.org/core-2.6.4/Object.html#method-i-instance_of-3F)**
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
- validates :name, kind: { instance_of: String }
1044
+ object = 'foo'
789
1045
 
790
- # or use an array to verify if the attribute
791
- # is an instance of one of the classes/modules.
1046
+ Kind::Maybe[object].try(:upcase).value # "FOO"
792
1047
 
793
- validates :name, kind: { instance_of: [String, Symbol] }
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
- **[Object#respond_to?](https://ruby-doc.org/core-2.6.4/Object.html#method-i-respond_to-3F)**
1052
+ Kind::Maybe[object].try { |value| value.upcase }.value # "FOO"
798
1053
 
799
- ```ruby
800
- validates :handler, kind: { respond_to: :call }
801
- ```
1054
+ #############
1055
+ # Nil value #
1056
+ #############
802
1057
 
803
- **Array.new.all? { |item| item.kind_of?(Class) }**
1058
+ object = nil
804
1059
 
805
- ```ruby
806
- validates :account_types, kind: { array_of: String }
1060
+ Kind::Maybe[object].try(:upcase).value # nil
807
1061
 
808
- # or use an array to verify if the attribute
809
- # is an instance of one of the classes
1062
+ Kind::Maybe[object].try { |value| value.upcase }.value # nil
810
1063
 
811
- validates :account_types, kind: { array_of: [String, Symbol] }
812
- ```
1064
+ #########################
1065
+ # Kind::Undefined value #
1066
+ #########################
813
1067
 
814
- **Array.new.all? { |item| expected_values.include?(item) }**
1068
+ object = Kind::Undefined
815
1069
 
816
- ```ruby
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
- validates :account_types, kind: { array_with: ['foo', 'bar'] }
1072
+ Kind::Maybe[object].try { |value| value.upcase }.value # nil
821
1073
  ```
822
1074
 
823
- #### Defining the default validation strategy
1075
+ > **Note:** You can use the `#try` method with `Kind::Optional` objects.
824
1076
 
825
- By default, you can define the attribute type directly (without a hash). e.g.
1077
+ [⬆️ Back to Top](#table-of-contents-)
826
1078
 
827
- ```ruby
828
- validates :name, kind: String
829
- # or
830
- validates :name, kind: [String, Symbol]
831
- ```
1079
+ ### Kind::Maybe#try!
832
1080
 
833
- To changes this behavior you can set another strategy to validates the attributes types:
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::Validator.default_strategy = :instance_of
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
- You can use the `allow_nil` option with any of the kind validations. e.g.
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
- And as any active model validation, kind validations works with the `strict: true`
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