kind 1.9.0 → 3.0.0

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: 8d4f4be415e28c7f240a097a64626588e7b3c7bc8a70dbfd71bf2b5b426e016a
4
- data.tar.gz: 7401efa30206d27d4f10608b5164d6c334d8da02e4652d8a7f156ee081f18ec5
3
+ metadata.gz: a0a947ffbd4a82b5ca7a74006ae8383c725393e1e8640364dc9e73874aac0eb0
4
+ data.tar.gz: d49f7382d4786e079c42ee7f45609ca8ca292ccb04e4d8ac299cdfa4cbf2e1c8
5
5
  SHA512:
6
- metadata.gz: f672462c9e64afce93af792d179d1c6095003ade48329018f4e64eb0bae7fb825699f2350c643fa2a1f2a3ae2822b3cbb1e8444c3a423101a37beffadbd7a614
7
- data.tar.gz: 890957443724fe3fdd7fec56d8b4a87c0e92f2793d26d298b8347b75e7091612e36a87d29bae70c7a789fe9e0750b366a4fe572104351e55b04903bbfcbffe03
6
+ metadata.gz: 77195516f80bae446150221c3c95a621ecacb70335f4594242b3d7f60e9de7ed5be32624c02183faf1ab5cc4717b0c229c82c52ec70aad6122d1fe51a71dfc92
7
+ data.tar.gz: 1f7ab4c70b8548dda0707d31ba9d500946c5314d8d9462ecdcd5ca0b23f1a197691d0eb6586e4a1628426794f1495d0303d2b1b7c4b32a1bcc73fdc88ea1123b
@@ -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
 
@@ -195,6 +235,49 @@ Kind.of.Hash.class?(Hash) # true
195
235
  Kind.of.Hash.class?(ActiveSupport::HashWithIndifferentAccess) # true
196
236
  ```
197
237
 
238
+ > **Note:** The `Kind.is` could check the inheritance of Classes/Modules.
239
+
240
+ ```ruby
241
+ #
242
+ # Verifying if the attribute value is the class or a subclass.
243
+ #
244
+ class Human; end
245
+ class Person < Human; end
246
+ class User < Human; end
247
+
248
+ Kind.is(Human, User) # true
249
+ Kind.is(Human, Human) # true
250
+ Kind.is(Human, Person) # true
251
+
252
+ Kind.is(Human, Struct) # false
253
+
254
+ #
255
+ # Verifying if the attribute value is the module or if it is a class that includes the module
256
+ #
257
+ module Human; end
258
+ class Person; include Human; end
259
+ class User; include Human; end
260
+
261
+ Kind.is(Human, User) # true
262
+ Kind.is(Human, Human) # true
263
+ Kind.is(Human, Person) # true
264
+
265
+ Kind.is(Human, Struct) # false
266
+
267
+ #
268
+ # Verifying if the attribute value is the module or if it is a module that extends the module
269
+ #
270
+ module Human; end
271
+ module Person; extend Human; end
272
+ module User; extend Human; end
273
+
274
+ Kind.is(Human, User) # true
275
+ Kind.is(Human, Human) # true
276
+ Kind.is(Human, Person) # true
277
+
278
+ Kind.is(Human, Struct) # false
279
+ ```
280
+
198
281
  [⬆️ Back to Top](#table-of-contents-)
199
282
 
200
283
  ### How to create a new type checker?
@@ -219,6 +302,22 @@ Kind.of(User, {}) # Kind::Error ({} expected to be a kind of User)
219
302
  Kind.of(Hash, {}) # {}
220
303
  Kind.of(Hash, user) # Kind::Error (<User ...> expected to be a kind of Hash)
221
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
+
222
321
  # ---------------------------------- #
223
322
  # Creating type checkers dynamically #
224
323
  # ---------------------------------- #
@@ -257,7 +356,7 @@ end
257
356
  Kind.is(User, AdminUser) # true
258
357
  ```
259
358
 
260
- #### Registering new (custom) type checkers
359
+ #### Registering new (custom) type checker
261
360
 
262
361
  Use `Kind::Types.add()`. e.g:
263
362
 
@@ -377,13 +476,165 @@ The list of types (classes and modules) available to use with `Kind.of.*` or `Ki
377
476
  - `Kind.of.Module()`
378
477
  - `Kind.of.Lambda()`
379
478
  - `Kind.of.Boolean()`
380
- - `Kind.of.Callable()`: verifies if the given value `respond_to?(:call)` or if it's a class/module and if its `public_instance_methods.include?(:call)`.
479
+ - `Kind.of.Callable()`: verifies if the given value `respond_to?(:call)`.
381
480
  - `Kind.of.Maybe()` or its alias `Kind.of.Optional()`
382
481
 
383
482
  **Note:** Remember, you can use the `Kind.is.*` method to check if some given value is a class/module with all type checkers above.
384
483
 
385
484
  [⬆️ Back to Top](#table-of-contents-)
386
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
+
387
638
  ## Kind::Undefined
388
639
 
389
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.
@@ -448,7 +699,27 @@ puts optional.value_or(1) # 1
448
699
  puts optional.value_or { 1 } # 1
449
700
  ```
450
701
 
451
- ### 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
452
723
 
453
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.
454
725
 
@@ -462,43 +733,70 @@ result =
462
733
  puts result # 42
463
734
  ```
464
735
 
465
- ### Kind::Maybe#try
736
+ You can also use `Kind::Maybe.wrap()` instead of the `.new` method.
466
737
 
467
- 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
468
750
 
469
751
  ```ruby
470
- object = 'foo'
752
+ Add = -> params do
753
+ a, b = Kind.of.Hash(params, or: Empty::HASH).values_at(:a, :b)
471
754
 
472
- p Kind::Maybe[object].try(:upcase) # "FOO"
755
+ a + b if Kind.of.Numeric.instance?(a, b)
756
+ end
473
757
 
474
- p Kind::Maybe[{}].try(:fetch, :number, 0) # 0
758
+ # --
475
759
 
476
- p Kind::Maybe[{number: 1}].try(:fetch, :number) # 1
760
+ Kind::Maybe[a: 1, b: 2].then(&Add).value_or(0) # 3
477
761
 
478
- p Kind::Maybe[object].try { |value| value.upcase } # "FOO"
762
+ # --
479
763
 
480
- #############
481
- # Nil value #
482
- #############
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
+ ```
483
768
 
484
- object = nil
769
+ ### Kind::None() and Kind::Some()
485
770
 
486
- 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:
487
773
 
488
- 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)
489
777
 
490
- #########################
491
- # Kind::Undefined value #
492
- #########################
778
+ return Kind::None unless Kind.of.Numeric?(a, b)
493
779
 
494
- object = Kind::Undefined
780
+ Kind::Some(a + b)
781
+ end
495
782
 
496
- p Kind::Maybe[object].try(:upcase) # nil
783
+ # --
497
784
 
498
- p Kind::Maybe[object].try { |value| value.upcase } # nil
499
- ```
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>
500
789
 
501
- [⬆️ 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
+ ```
502
800
 
503
801
  ### Kind.of.Maybe()
504
802
 
@@ -555,6 +853,26 @@ result2 =
555
853
  puts result2 # 35
556
854
  ```
557
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
+
558
876
  **Note:** The `Kind.of.Optional` is available to check if some value is a `Kind::Optional`.
559
877
 
560
878
  [⬆️ Back to Top](#table-of-contents-)
@@ -566,10 +884,11 @@ In these scenarios, you could check the given input type as optional and avoid u
566
884
 
567
885
  ```ruby
568
886
  def person_name(params)
569
- Kind::Of::Hash.as_optional(params)
570
- .map { |data| data if data.values_at(:first_name, :last_name).compact.size == 2 }
571
- .map { |data| "#{data[:first_name]} #{data[:last_name]}" }
572
- .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' }
573
892
  end
574
893
 
575
894
  person_name('') # "John Doe"
@@ -590,9 +909,20 @@ def person_name(params)
590
909
  'John Doe'
591
910
  end
592
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
593
923
  ```
594
924
 
595
- > 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).
596
926
 
597
927
  Let's see another example using a collection and how the method `.as_optional` works when it receives no argument.
598
928
 
@@ -666,136 +996,97 @@ end
666
996
 
667
997
  [⬆️ Back to Top](#table-of-contents-)
668
998
 
669
- ## Kind::Validator (ActiveModel::Validations)
999
+ ### Kind::Maybe(<Type>)
670
1000
 
671
- This module enables the capability to validate types via [active model validations](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:
672
1002
 
673
1003
  ```ruby
674
- class Person
675
- include ActiveModel::Validations
676
-
677
- attr_accessor :first_name, :last_name
1004
+ result1 =
1005
+ Kind::Maybe(Numeric)
1006
+ .wrap(5)
1007
+ .then { |value| value * 5 }
1008
+ .value_or { 0 }
678
1009
 
679
- validates :first_name, :last_name, kind: String
680
- end
681
- ```
1010
+ puts result1 # 25
682
1011
 
683
- And to make use of it, you will need to do an explicitly require. e.g:
1012
+ # ---
684
1013
 
685
- ```ruby
686
- # In some Gemfile
687
- 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 }
688
1019
 
689
- # In some .rb file
690
- require 'kind/active_model/validation'
1020
+ puts result2 # 0
691
1021
  ```
692
1022
 
693
- ### Usage
694
-
695
- **[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:
696
1024
 
697
1025
  ```ruby
698
- validates :name, kind: { of: String }
699
- # or
700
- validates :name, kind: { is_a: String }
1026
+ Kind::Maybe(Numeric)[5]
1027
+ Kind::Maybe(Numeric).new(5)
1028
+ Kind::Maybe(Numeric).wrap(5)
701
1029
 
702
- # Use an array to verify if the attribute
703
- # is an instance of one of the classes/modules.
1030
+ # ---
704
1031
 
705
- validates :status, kind: { of: [String, Symbol]}
706
- # or
707
- validates :status, kind: { is_a: [String, Symbol]}
1032
+ Kind::Optional(Numeric)[5]
1033
+ Kind::Optional(Numeric).new(5)
1034
+ Kind::Optional(Numeric).wrap(5)
708
1035
  ```
709
1036
 
710
- **[Object#instance_of?](https://ruby-doc.org/core-2.6.4/Object.html#method-i-instance_of-3F)**
711
-
712
- ```ruby
713
- validates :name, kind: { instance_of: String }
714
-
715
- # or use an array to verify if the attribute
716
- # is an instance of one of the classes/modules.
717
-
718
- validates :name, kind: { instance_of: [String, Symbol] }
719
- ```
1037
+ [⬆️ Back to Top](#table-of-contents-)
720
1038
 
1039
+ ### Kind::Maybe#try
721
1040
 
722
- **[Object#respond_to?](https://ruby-doc.org/core-2.6.4/Object.html#method-i-respond_to-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.
723
1042
 
724
1043
  ```ruby
725
- validates :handler, kind: { respond_to: :call }
726
- ```
1044
+ object = 'foo'
727
1045
 
728
- **Class == Class || Class < Class**
1046
+ Kind::Maybe[object].try(:upcase).value # "FOO"
729
1047
 
730
- ```ruby
731
- # Verifies if the attribute value is the class or a subclass.
1048
+ Kind::Maybe[{}].try(:fetch, :number, 0).value # 0
732
1049
 
733
- validates :handler, kind: { klass: Handler }
1050
+ Kind::Maybe[{number: 1}].try(:fetch, :number).value # 1
734
1051
 
735
- # or use the :is option
1052
+ Kind::Maybe[object].try { |value| value.upcase }.value # "FOO"
736
1053
 
737
- validates :handler, kind: { is: Handler }
738
- ```
1054
+ #############
1055
+ # Nil value #
1056
+ #############
739
1057
 
740
- **Array.new.all? { |item| item.kind_of?(Class) }**
1058
+ object = nil
741
1059
 
742
- ```ruby
743
- validates :account_types, kind: { array_of: String }
1060
+ Kind::Maybe[object].try(:upcase).value # nil
744
1061
 
745
- # or use an array to verify if the attribute
746
- # is an instance of one of the classes
1062
+ Kind::Maybe[object].try { |value| value.upcase }.value # nil
747
1063
 
748
- validates :account_types, kind: { array_of: [String, Symbol] }
749
- ```
1064
+ #########################
1065
+ # Kind::Undefined value #
1066
+ #########################
750
1067
 
751
- **Array.new.all? { |item| expected_values.include?(item) }**
1068
+ object = Kind::Undefined
752
1069
 
753
- ```ruby
754
- # Verifies if the attribute value
755
- # is an array with some or all the expected values.
1070
+ Kind::Maybe[object].try(:upcase).value # nil
756
1071
 
757
- validates :account_types, kind: { array_with: ['foo', 'bar'] }
1072
+ Kind::Maybe[object].try { |value| value.upcase }.value # nil
758
1073
  ```
759
1074
 
760
- #### Defining the default validation strategy
1075
+ > **Note:** You can use the `#try` method with `Kind::Optional` objects.
761
1076
 
762
- By default, you can define the attribute type directly (without a hash). e.g.
1077
+ [⬆️ Back to Top](#table-of-contents-)
763
1078
 
764
- ```ruby
765
- validates :name, kind: String
766
- # or
767
- validates :name, kind: [String, Symbol]
768
- ```
1079
+ ### Kind::Maybe#try!
769
1080
 
770
- 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.
771
1082
 
772
1083
  ```ruby
773
- Kind::Validator.default_strategy = :instance_of
774
-
775
- # Tip: Create an initializer if you are in a Rails application.
776
- ```
777
-
778
- And these are the available options to define the default strategy:
779
- - `is_a`
780
- - `kind_of` *(default)*
781
- - `instance_of`
782
-
783
- #### Using the `allow_nil` and `strict` options
784
-
785
- You can use the `allow_nil` option with any of the kind validations. e.g.
1084
+ Kind::Maybe[{}].try(:upcase) # => #<Kind::Maybe::None:0x0000... @value=nil>
786
1085
 
787
- ```ruby
788
- validates :name, kind: String, allow_nil: true
1086
+ Kind::Maybe[{}].try!(:upcase) # => NoMethodError (undefined method `upcase' for {}:Hash)
789
1087
  ```
790
1088
 
791
- And as any active model validation, kind validations works with the `strict: true`
792
- option and with the `validates!` method. e.g.
793
-
794
- ```ruby
795
- validates :first_name, kind: String, strict: true
796
- # or
797
- validates! :last_name, kind: String
798
- ```
1089
+ > **Note:** You can also use the `#try!` method with `Kind::Optional` objects.
799
1090
 
800
1091
  [⬆️ Back to Top](#table-of-contents-)
801
1092