kind 1.9.0 → 3.0.0

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