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 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