kind 2.1.0 → 3.1.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: a83d36b4c1fe131567251f418b775a8366ecf2a7c0d31682f801866cb9d83a27
4
- data.tar.gz: b9b613500675aa1ea9e10c4b5ff83a451f289aec39896aaed025c196f0212ece
3
+ metadata.gz: 7b62da87ad8443fb4d3f1d96535bc69259addf767721db70288401d677733967
4
+ data.tar.gz: 995f0a3a34ed67a3244e181380d627e076d4576a2b6e0c7de2b34fa415c5f868
5
5
  SHA512:
6
- metadata.gz: 252e2e4f3bd9bbd09ff182579d450de170688ada607dfaa30cd70748c1ac4473381c856ba1be37f74ccd1eb05d077763cbf853ea1f356c58226408108e69f4e8
7
- data.tar.gz: f35a606761e6de74f81427a4431c711252303f78e4b0b5d11dd5e1878180112c1733fa4fef85949f3414196e387b54c672e768384f92aff9ac1c434662529834
6
+ metadata.gz: 3398bbf2b67fe8b81023d9c781b14ac115f5749f45641a74fc1ff1c8d05f4fd1c4e2a4bc817d6c71e7f8dc96b245ec9f9c80a776ae11d22ba69dbc878dfaa847
7
+ data.tar.gz: 75c6c1e387548447edf601d4b73b18ff3e2700debf3f6715b2f772f582829d39a72e7bb9d8af61fe8bcd32e9f72aa8398f1703f2c706668a0ab5e59fc0612b05
@@ -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
@@ -31,22 +31,25 @@ One of the goals of this project is to do simple type checking like `"some strin
31
31
  - [Classes' type checkers](#classes-type-checkers)
32
32
  - [Modules' type checkers](#modules-type-checkers)
33
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)
34
38
  - [Kind::Undefined](#kindundefined)
35
39
  - [Kind.of.\<Type\>.or_undefined()](#kindoftypeor_undefined)
36
40
  - [Kind::Maybe](#kindmaybe)
37
41
  - [Replacing blocks by lambdas](#replacing-blocks-by-lambdas)
38
- - [Kind::Maybe[] and Kind::Maybe#then method aliases](#kindmaybe-and-kindmaybethen-method-aliases)
42
+ - [Kind::Maybe[], Kind::Maybe.wrap() and Kind::Maybe#then method aliases](#kindmaybe-kindmaybewrap-and-kindmaybethen-method-aliases)
39
43
  - [Replacing blocks by lambdas](#replacing-blocks-by-lambdas-1)
40
44
  - [Kind::None() and Kind::Some()](#kindnone-and-kindsome)
41
45
  - [Kind.of.Maybe()](#kindofmaybe)
42
46
  - [Kind::Optional](#kindoptional)
43
47
  - [Replacing blocks by lambdas](#replacing-blocks-by-lambdas-2)
44
48
  - [Kind.of.\<Type\>.as_optional](#kindoftypeas_optional)
49
+ - [Kind::Maybe(<Type>)](#kindmaybetype)
45
50
  - [Kind::Maybe#try](#kindmaybetry)
46
- - [Kind::Validator (ActiveModel::Validations)](#kindvalidator-activemodelvalidations)
47
- - [Usage](#usage-1)
48
- - [Defining the default validation strategy](#defining-the-default-validation-strategy)
49
- - [Using the `allow_nil` and `strict` options](#using-the-allow_nil-and-strict-options)
51
+ - [Kind::Maybe#try!](#kindmaybetry-1)
52
+ - [Kind::Maybe#dig](#kindmaybedig)
50
53
  - [Kind::Empty](#kindempty)
51
54
  - [Similar Projects](#similar-projects)
52
55
  - [Development](#development)
@@ -163,13 +166,19 @@ Kind.of.Boolean.or_nil(true) # true
163
166
  Use the method `.instance?` to verify if the given object has the expected type.
164
167
 
165
168
  ```ruby
166
- Kind.of.Hash.instance?('') # false
169
+ Kind.of.Hash.instance?({}) # true
170
+ Kind.of.Hash.instance?({}, HashWithIndifferentAccess.new) # true
171
+
172
+ Kind.of.Hash.instance?('') # false
173
+ Kind.of.Hash.instance?({}, '') # false
167
174
 
168
175
  # ---
169
176
 
170
- Kind.of.Boolean.instance?('') # false
171
- Kind.of.Boolean.instance?(true) # true
172
- Kind.of.Boolean.instance?(false) # true
177
+ Kind.of.Boolean.instance?(true) # true
178
+ Kind.of.Boolean.instance?(true, false) # true
179
+
180
+ Kind.of.Boolean.instance?(nil) # false
181
+ Kind.of.Boolean.instance?(false, true, nil) # false
173
182
  ```
174
183
 
175
184
  > **Note:** When `.instance?` is called without an argument,
@@ -186,6 +195,22 @@ collection
186
195
  > To do this, use Kind.of.\<Type\>?()
187
196
 
188
197
  ```ruby
198
+ Kind.of.Hash?({}) # true
199
+ Kind.of.Hash?({}, HashWithIndifferentAccess.new) # true
200
+
201
+ Kind.of.Hash?('') # false
202
+ Kind.of.Hash?({}, '') # false
203
+
204
+ # ---
205
+
206
+ Kind.of.Boolean?(true) # true
207
+ Kind.of.Boolean?(false, true) # true
208
+
209
+ Kind.of.Boolean?(nil) # false
210
+ Kind.of.Boolean?(false, true, nil) # false
211
+
212
+ # ---
213
+
189
214
  collection = [ {number: 1}, 'number 2', {number: 3}, :number_4 ]
190
215
 
191
216
  collection.select(&Kind.of.Hash?) # [{:number=>1}, {:number=>3}]
@@ -434,6 +459,7 @@ The list of types (classes and modules) available to use with `Kind.of.*` or `Ki
434
459
  - `Kind.of.Range`
435
460
  - `Kind.of.Hash`
436
461
  - `Kind.of.Struct`
462
+ - `Kind.of.OpenStruct`
437
463
  - `Kind.of.Enumerator`
438
464
  - `Kind.of.Set`
439
465
  - `Kind.of.Method`
@@ -459,6 +485,158 @@ The list of types (classes and modules) available to use with `Kind.of.*` or `Ki
459
485
 
460
486
  [⬆️ Back to Top](#table-of-contents-)
461
487
 
488
+ ## Kind::Validator (ActiveModel::Validations)
489
+
490
+ 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
491
+
492
+ ```ruby
493
+ class Person
494
+ include ActiveModel::Validations
495
+
496
+ attr_accessor :first_name, :last_name
497
+
498
+ validates :first_name, :last_name, kind: String
499
+ end
500
+ ```
501
+
502
+ And to make use of it, you will need to do an explicitly require. e.g:
503
+
504
+ ```ruby
505
+ # In some Gemfile
506
+ gem 'kind', require: 'kind/active_model/validation'
507
+
508
+ # In some .rb file
509
+ require 'kind/active_model/validation'
510
+ ```
511
+
512
+ ### Usage
513
+
514
+ **[Object#kind_of?](https://ruby-doc.org/core-2.6.4/Object.html#method-i-kind_of-3F)**
515
+
516
+ ```ruby
517
+ validates :name, kind: { of: String }
518
+
519
+ # Use an array to verify if the attribute
520
+ # is an instance of one of the classes/modules.
521
+
522
+ validates :status, kind: { of: [String, Symbol]}
523
+ ```
524
+
525
+ **[Kind.is](#verifying-the-kind-of-some-classmodule)**
526
+
527
+ ```ruby
528
+ #
529
+ # Verifying if the attribute value is the class or a subclass.
530
+ #
531
+ class Human; end
532
+ class Person < Human; end
533
+ class User < Human; end
534
+
535
+ validates :human_kind, kind: { is: Human }
536
+
537
+ #
538
+ # Verifying if the attribute value is the module or if it is a class that includes the module
539
+ #
540
+ module Human; end
541
+ class Person; include Human; end
542
+ class User; include Human; end
543
+
544
+ validates :human_kind, kind: { is: Human }
545
+
546
+ #
547
+ # Verifying if the attribute value is the module or if it is a module that extends the module
548
+ #
549
+ module Human; end
550
+ module Person; extend Human; end
551
+ module User; extend Human; end
552
+
553
+ validates :human_kind, kind: { is: Human }
554
+
555
+ # or use an array to verify if the attribute
556
+ # is a kind of one those classes/modules.
557
+
558
+ validates :human_kind, kind: { is: [Person, User] }
559
+ ```
560
+
561
+ **[Object#instance_of?](https://ruby-doc.org/core-2.6.4/Object.html#method-i-instance_of-3F)**
562
+
563
+ ```ruby
564
+ validates :name, kind: { instance_of: String }
565
+
566
+ # or use an array to verify if the attribute
567
+ # is an instance of one of the classes/modules.
568
+
569
+ validates :name, kind: { instance_of: [String, Symbol] }
570
+ ```
571
+
572
+
573
+ **[Object#respond_to?](https://ruby-doc.org/core-2.6.4/Object.html#method-i-respond_to-3F)**
574
+
575
+ ```ruby
576
+ validates :handler, kind: { respond_to: :call }
577
+ ```
578
+
579
+ **Array.new.all? { |item| item.kind_of?(Class) }**
580
+
581
+ ```ruby
582
+ validates :account_types, kind: { array_of: String }
583
+
584
+ # or use an array to verify if the attribute
585
+ # is an instance of one of the classes
586
+
587
+ validates :account_types, kind: { array_of: [String, Symbol] }
588
+ ```
589
+
590
+ **Array.new.all? { |item| expected_values.include?(item) }**
591
+
592
+ ```ruby
593
+ # Verifies if the attribute value
594
+ # is an array with some or all the expected values.
595
+
596
+ validates :account_types, kind: { array_with: ['foo', 'bar'] }
597
+ ```
598
+
599
+ #### Defining the default validation strategy
600
+
601
+ By default, you can define the attribute type directly (without a hash). e.g.
602
+
603
+ ```ruby
604
+ validates :name, kind: String
605
+ # or
606
+ validates :name, kind: [String, Symbol]
607
+ ```
608
+
609
+ To changes this behavior you can set another strategy to validates the attributes types:
610
+
611
+ ```ruby
612
+ Kind::Validator.default_strategy = :instance_of
613
+
614
+ # Tip: Create an initializer if you are in a Rails application.
615
+ ```
616
+
617
+ And these are the available options to define the default strategy:
618
+ - `kind_of` *(default)*
619
+ - `instance_of`
620
+
621
+ #### Using the `allow_nil` and `strict` options
622
+
623
+ You can use the `allow_nil` option with any of the kind validations. e.g.
624
+
625
+ ```ruby
626
+ validates :name, kind: String, allow_nil: true
627
+ ```
628
+
629
+ And as any active model validation, kind validations works with the `strict: true`
630
+ option and with the `validates!` method. e.g.
631
+
632
+ ```ruby
633
+ validates :first_name, kind: String, strict: true
634
+ # or
635
+ validates! :last_name, kind: String
636
+ ```
637
+
638
+ [⬆️ Back to Top](#table-of-contents-)
639
+
462
640
  ## Kind::Undefined
463
641
 
464
642
  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.
@@ -543,7 +721,7 @@ end
543
721
  Kind::Maybe.new(nil).map(&Add).value_or(0) # 0
544
722
  ```
545
723
 
546
- ### Kind::Maybe[] and Kind::Maybe#then method aliases
724
+ ### Kind::Maybe[], Kind::Maybe.wrap() and Kind::Maybe#then method aliases
547
725
 
548
726
  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.
549
727
 
@@ -557,6 +735,19 @@ result =
557
735
  puts result # 42
558
736
  ```
559
737
 
738
+ You can also use `Kind::Maybe.wrap()` instead of the `.new` method.
739
+
740
+ ```ruby
741
+ result =
742
+ Kind::Maybe
743
+ .wrap(5)
744
+ .then { |value| value * 5 }
745
+ .then { |value| value + 17 }
746
+ .value_or(0)
747
+
748
+ puts result # 42
749
+ ```
750
+
560
751
  #### Replacing blocks by lambdas
561
752
 
562
753
  ```ruby
@@ -586,7 +777,7 @@ you could use the methods `Kind::None` and `Kind::Some` to do this. e.g:
586
777
  Add = -> params do
587
778
  a, b = Kind.of.Hash(params, or: Empty::HASH).values_at(:a, :b)
588
779
 
589
- return Kind::None unless Kind.of.Numeric.instance?(a, b)
780
+ return Kind::None unless Kind.of.Numeric?(a, b)
590
781
 
591
782
  Kind::Some(a + b)
592
783
  end
@@ -695,10 +886,11 @@ In these scenarios, you could check the given input type as optional and avoid u
695
886
 
696
887
  ```ruby
697
888
  def person_name(params)
698
- Kind::Of::Hash.as_optional(params)
699
- .map { |data| data if data.values_at(:first_name, :last_name).compact.size == 2 }
700
- .map { |data| "#{data[:first_name]} #{data[:last_name]}" }
701
- .value_or { 'John Doe' }
889
+ Kind::Of::Hash
890
+ .as_optional(params)
891
+ .map { |data| data if data.values_at(:first_name, :last_name).compact.size == 2 }
892
+ .map { |data| "#{data[:first_name]} #{data[:last_name]}" }
893
+ .value_or { 'John Doe' }
702
894
  end
703
895
 
704
896
  person_name('') # "John Doe"
@@ -719,9 +911,20 @@ def person_name(params)
719
911
  'John Doe'
720
912
  end
721
913
  end
914
+
915
+ #
916
+ # You can also use Kind::Optional(<Type>) to achieve the same behavior
917
+ #
918
+ def person_name(params)
919
+ Kind::Optional(Hash)
920
+ .wrap(params)
921
+ .map { |data| data if data.values_at(:first_name, :last_name).compact.size == 2 }
922
+ .map { |data| "#{data[:first_name]} #{data[:last_name]}" }
923
+ .value_or { 'John Doe' }
924
+ end
722
925
  ```
723
926
 
724
- > 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).
927
+ > 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).
725
928
 
726
929
  Let's see another example using a collection and how the method `.as_optional` works when it receives no argument.
727
930
 
@@ -795,193 +998,162 @@ end
795
998
 
796
999
  [⬆️ Back to Top](#table-of-contents-)
797
1000
 
798
- ### Kind::Maybe#try
1001
+ ### Kind::Maybe(<Type>)
799
1002
 
800
- 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.
1003
+ 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:
801
1004
 
802
1005
  ```ruby
803
- object = 'foo'
804
-
805
- p Kind::Maybe[object].try(:upcase) # "FOO"
806
-
807
- p Kind::Maybe[{}].try(:fetch, :number, 0) # 0
808
-
809
- p Kind::Maybe[{number: 1}].try(:fetch, :number) # 1
810
-
811
- p Kind::Maybe[object].try { |value| value.upcase } # "FOO"
1006
+ result1 =
1007
+ Kind::Maybe(Numeric)
1008
+ .wrap(5)
1009
+ .then { |value| value * 5 }
1010
+ .value_or { 0 }
812
1011
 
813
- #############
814
- # Nil value #
815
- #############
1012
+ puts result1 # 25
816
1013
 
817
- object = nil
1014
+ # ---
818
1015
 
819
- p Kind::Maybe[object].try(:upcase) # nil
1016
+ result2 =
1017
+ Kind::Optional(Numeric)
1018
+ .wrap('5')
1019
+ .then { |value| value * 5 }
1020
+ .value_or { 0 }
820
1021
 
821
- p Kind::Maybe[object].try { |value| value.upcase } # nil
1022
+ puts result2 # 0
1023
+ ```
822
1024
 
823
- #########################
824
- # Kind::Undefined value #
825
- #########################
1025
+ This typed maybe has the same methods of `Kind::Maybe` class. e.g:
826
1026
 
827
- object = Kind::Undefined
1027
+ ```ruby
1028
+ Kind::Maybe(Numeric)[5]
1029
+ Kind::Maybe(Numeric).new(5)
1030
+ Kind::Maybe(Numeric).wrap(5)
828
1031
 
829
- p Kind::Maybe[object].try(:upcase) # nil
1032
+ # ---
830
1033
 
831
- p Kind::Maybe[object].try { |value| value.upcase } # nil
1034
+ Kind::Optional(Numeric)[5]
1035
+ Kind::Optional(Numeric).new(5)
1036
+ Kind::Optional(Numeric).wrap(5)
832
1037
  ```
833
1038
 
834
1039
  [⬆️ Back to Top](#table-of-contents-)
835
1040
 
836
- ## Kind::Validator (ActiveModel::Validations)
1041
+ ### Kind::Maybe#try
837
1042
 
838
- 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
1043
+ 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.
839
1044
 
840
1045
  ```ruby
841
- class Person
842
- include ActiveModel::Validations
843
-
844
- attr_accessor :first_name, :last_name
845
-
846
- validates :first_name, :last_name, kind: String
847
- end
848
- ```
1046
+ object = 'foo'
849
1047
 
850
- And to make use of it, you will need to do an explicitly require. e.g:
1048
+ Kind::Maybe[object].try(:upcase).value # "FOO"
851
1049
 
852
- ```ruby
853
- # In some Gemfile
854
- gem 'kind', require: 'kind/active_model/validation'
1050
+ Kind::Maybe[{}].try(:fetch, :number, 0).value # 0
855
1051
 
856
- # In some .rb file
857
- require 'kind/active_model/validation'
858
- ```
1052
+ Kind::Maybe[{number: 1}].try(:fetch, :number).value # 1
859
1053
 
860
- ### Usage
1054
+ Kind::Maybe[object].try { |value| value.upcase }.value # "FOO"
861
1055
 
862
- **[Object#kind_of?](https://ruby-doc.org/core-2.6.4/Object.html#method-i-kind_of-3F)**
863
-
864
- ```ruby
865
- validates :name, kind: { of: String }
1056
+ #############
1057
+ # Nil value #
1058
+ #############
866
1059
 
867
- # Use an array to verify if the attribute
868
- # is an instance of one of the classes/modules.
1060
+ object = nil
869
1061
 
870
- validates :status, kind: { of: [String, Symbol]}
871
- ```
1062
+ Kind::Maybe[object].try(:upcase).value # nil
872
1063
 
873
- **[Kind.is](#verifying-the-kind-of-some-classmodule)**
1064
+ Kind::Maybe[object].try { |value| value.upcase }.value # nil
874
1065
 
875
- ```ruby
876
- #
877
- # Verifying if the attribute value is the class or a subclass.
878
- #
879
- class Human; end
880
- class Person < Human; end
881
- class User < Human; end
882
-
883
- validates :human_kind, kind: { is: Human }
1066
+ #########################
1067
+ # Kind::Undefined value #
1068
+ #########################
884
1069
 
885
- #
886
- # Verifying if the attribute value is the module or if it is a class that includes the module
887
- #
888
- module Human; end
889
- class Person; include Human; end
890
- class User; include Human; end
1070
+ object = Kind::Undefined
891
1071
 
892
- validates :human_kind, kind: { is: Human }
1072
+ Kind::Maybe[object].try(:upcase).value # nil
893
1073
 
894
- #
895
- # Verifying if the attribute value is the module or if it is a module that extends the module
896
- #
897
- module Human; end
898
- module Person; extend Human; end
899
- module User; extend Human; end
1074
+ Kind::Maybe[object].try { |value| value.upcase }.value # nil
1075
+ ```
900
1076
 
901
- validates :human_kind, kind: { is: Human }
1077
+ > **Note:** You can use the `#try` method with `Kind::Optional` objects.
902
1078
 
903
- # or use an array to verify if the attribute
904
- # is a kind of one those classes/modules.
1079
+ [⬆️ Back to Top](#table-of-contents-)
905
1080
 
906
- validates :human_kind, kind: { is: [Person, User] }
907
- ```
1081
+ ### Kind::Maybe#try!
908
1082
 
909
- **[Object#instance_of?](https://ruby-doc.org/core-2.6.4/Object.html#method-i-instance_of-3F)**
1083
+ Has the same behavior of its `#try`, but it will raise an error if the value doesn't respond to the expected method.
910
1084
 
911
1085
  ```ruby
912
- validates :name, kind: { instance_of: String }
913
-
914
- # or use an array to verify if the attribute
915
- # is an instance of one of the classes/modules.
1086
+ Kind::Maybe[{}].try(:upcase) # => #<Kind::Maybe::None:0x0000... @value=nil>
916
1087
 
917
- validates :name, kind: { instance_of: [String, Symbol] }
1088
+ Kind::Maybe[{}].try!(:upcase) # => NoMethodError (undefined method `upcase' for {}:Hash)
918
1089
  ```
919
1090
 
1091
+ > **Note:** You can also use the `#try!` method with `Kind::Optional` objects.
920
1092
 
921
- **[Object#respond_to?](https://ruby-doc.org/core-2.6.4/Object.html#method-i-respond_to-3F)**
1093
+ [⬆️ Back to Top](#table-of-contents-)
922
1094
 
923
- ```ruby
924
- validates :handler, kind: { respond_to: :call }
925
- ```
1095
+ ### Kind::Maybe#dig
926
1096
 
927
- **Array.new.all? { |item| item.kind_of?(Class) }**
1097
+ Has the same behavior of Ruby dig methods ([Hash](https://ruby-doc.org/core-2.3.0/Hash.html#method-i-dig), [Array](https://ruby-doc.org/core-2.3.0/Array.html#method-i-dig), [Struct](https://ruby-doc.org/core-2.3.0/Struct.html#method-i-dig), [OpenStruct](https://ruby-doc.org/stdlib-2.3.0/libdoc/ostruct/rdoc/OpenStruct.html#method-i-dig)), but it will not raise an error if some value can't be digged.
928
1098
 
929
1099
  ```ruby
930
- validates :account_types, kind: { array_of: String }
931
-
932
- # or use an array to verify if the attribute
933
- # is an instance of one of the classes
1100
+ [nil, 1, '', /x/].each do |value|
1101
+ p Kind::Maybe[value].dig(:foo).value # nil
1102
+ end
934
1103
 
935
- validates :account_types, kind: { array_of: [String, Symbol] }
936
- ```
1104
+ # --
937
1105
 
938
- **Array.new.all? { |item| expected_values.include?(item) }**
1106
+ a = [1, 2, 3]
939
1107
 
940
- ```ruby
941
- # Verifies if the attribute value
942
- # is an array with some or all the expected values.
1108
+ Kind::Maybe[a].dig(0).value # 1
943
1109
 
944
- validates :account_types, kind: { array_with: ['foo', 'bar'] }
945
- ```
1110
+ Kind::Maybe[a].dig(3).value # nil
946
1111
 
947
- #### Defining the default validation strategy
1112
+ # --
948
1113
 
949
- By default, you can define the attribute type directly (without a hash). e.g.
1114
+ h = { foo: {bar: {baz: 1}}}
950
1115
 
951
- ```ruby
952
- validates :name, kind: String
953
- # or
954
- validates :name, kind: [String, Symbol]
955
- ```
1116
+ Kind::Maybe[h].dig(:foo).value # {bar: {baz: 1}}
1117
+ Kind::Maybe[h].dig(:foo, :bar).value # {baz: 1}
1118
+ Kind::Maybe[h].dig(:foo, :bar, :baz).value # 1
956
1119
 
957
- To changes this behavior you can set another strategy to validates the attributes types:
1120
+ Kind::Maybe[h].dig(:foo, :bar, 'baz').value # nil
958
1121
 
959
- ```ruby
960
- Kind::Validator.default_strategy = :instance_of
1122
+ # --
961
1123
 
962
- # Tip: Create an initializer if you are in a Rails application.
963
- ```
1124
+ i = { foo: [{'bar' => [1, 2]}, {baz: [3, 4]}] }
964
1125
 
965
- And these are the available options to define the default strategy:
966
- - `kind_of` *(default)*
967
- - `instance_of`
1126
+ Kind::Maybe[i].dig(:foo, 0, 'bar', 0).value # 1
1127
+ Kind::Maybe[i].dig(:foo, 0, 'bar', 1).value # 2
1128
+ Kind::Maybe[i].dig(:foo, 0, 'bar', -1).value # 2
968
1129
 
969
- #### Using the `allow_nil` and `strict` options
1130
+ Kind::Maybe[i].dig(:foo, 0, 'bar', 2).value # nil
970
1131
 
971
- You can use the `allow_nil` option with any of the kind validations. e.g.
1132
+ # --
972
1133
 
973
- ```ruby
974
- validates :name, kind: String, allow_nil: true
1134
+ s = Struct.new(:a, :b).new(101, 102)
1135
+ o = OpenStruct.new(c: 103, d: 104)
1136
+ b = { struct: s, ostruct: o, data: [s, o]}
1137
+
1138
+ Kind::Maybe[s].dig(:a).value # 101
1139
+ Kind::Maybe[b].dig(:struct, :b).value # 102
1140
+ Kind::Maybe[b].dig(:data, 0, :b).value # 102
1141
+ Kind::Maybe[b].dig(:data, 0, 'b').value # 102
1142
+
1143
+ Kind::Maybe[o].dig(:c).value # 103
1144
+ Kind::Maybe[b].dig(:ostruct, :d).value # 104
1145
+ Kind::Maybe[b].dig(:data, 1, :d).value # 104
1146
+ Kind::Maybe[b].dig(:data, 1, 'd').value # 104
1147
+
1148
+ Kind::Maybe[s].dig(:f).value # nil
1149
+ Kind::Maybe[o].dig(:f).value # nil
1150
+ Kind::Maybe[b].dig(:struct, :f).value # nil
1151
+ Kind::Maybe[b].dig(:ostruct, :f).value # nil
1152
+ Kind::Maybe[b].dig(:data, 0, :f).value # nil
1153
+ Kind::Maybe[b].dig(:data, 1, :f).value # nil
975
1154
  ```
976
1155
 
977
- And as any active model validation, kind validations works with the `strict: true`
978
- option and with the `validates!` method. e.g.
979
-
980
- ```ruby
981
- validates :first_name, kind: String, strict: true
982
- # or
983
- validates! :last_name, kind: String
984
- ```
1156
+ > **Note:** You can also use the `#dig` method with `Kind::Optional` objects.
985
1157
 
986
1158
  [⬆️ Back to Top](#table-of-contents-)
987
1159
 
@@ -2,14 +2,16 @@
2
2
 
3
3
  require 'kind/version'
4
4
 
5
+ require 'ostruct'
6
+
5
7
  require 'kind/empty'
6
8
  require 'kind/undefined'
9
+ require 'kind/checker'
7
10
  require 'kind/maybe'
8
11
 
9
12
  require 'kind/error'
10
13
  require 'kind/of'
11
14
  require 'kind/is'
12
- require 'kind/checker'
13
15
  require 'kind/types'
14
16
 
15
17
  module Kind
@@ -18,37 +20,19 @@ module Kind
18
20
  private_constant :WRONG_NUMBER_OF_ARGUMENTS
19
21
 
20
22
  def self.is(expected = Undefined, object = Undefined)
21
- return Is if expected == Undefined && object == Undefined
23
+ return Is if Undefined == expected && Undefined == object
22
24
 
23
- return Kind::Is.(expected, object) if object != Undefined
25
+ return Kind::Is.(expected, object) if Undefined != object
24
26
 
25
27
  raise ArgumentError, WRONG_NUMBER_OF_ARGUMENTS
26
28
  end
27
29
 
28
- MODULE_OR_CLASS = 'Module/Class'.freeze
29
-
30
- private_constant :MODULE_OR_CLASS
31
-
32
- private_class_method def self.__checkers__
33
- @__checkers__ ||= {}
34
- end
35
-
36
- __checkers__
37
-
38
30
  def self.of(kind = Undefined, object = Undefined)
39
- return Of if kind == Undefined && object == Undefined
40
-
41
- return Kind::Of.(kind, object) if object != Undefined
31
+ return Of if Undefined == kind && Undefined == object
42
32
 
43
- __checkers__[kind] ||= begin
44
- kind_name = kind.name
33
+ return Kind::Of.(kind, object) if Undefined != object
45
34
 
46
- if Kind::Of.const_defined?(kind_name, false)
47
- Kind::Of.const_get(kind_name)
48
- else
49
- Checker.new(Kind::Of.(Module, kind, MODULE_OR_CLASS))
50
- end
51
- end
35
+ Kind::Checker::Factory.create(kind)
52
36
  end
53
37
 
54
38
  def self.of?(kind, *args)
@@ -65,7 +49,7 @@ module Kind
65
49
  end
66
50
 
67
51
  def self.Module(value)
68
- value == ::Module || (value.is_a?(::Module) && !self.Class(value))
52
+ ::Module == value || (value.is_a?(::Module) && !self.Class(value))
69
53
  end
70
54
 
71
55
  def self.Boolean(value)
@@ -81,13 +65,13 @@ module Kind
81
65
  # -- Class
82
66
 
83
67
  def self.Class(object = Undefined)
84
- return Class if object == Undefined
68
+ return Class if Undefined == object
85
69
 
86
70
  self.call(::Class, object)
87
71
  end
88
72
 
89
73
  const_set(:Class, ::Module.new do
90
- extend Checkable
74
+ extend Checker::Protocol
91
75
 
92
76
  def self.__kind; ::Class; end
93
77
 
@@ -103,16 +87,16 @@ module Kind
103
87
  # -- Module
104
88
 
105
89
  def self.Module(object = Undefined)
106
- return Module if object == Undefined
90
+ return Module if Undefined == object
107
91
 
108
92
  self.call(::Module, object)
109
93
  end
110
94
 
111
95
  const_set(:Module, ::Module.new do
112
- extend Checkable
96
+ extend Checker::Protocol
113
97
 
114
98
  def self.__kind_undefined(value)
115
- __kind_error(Kind::Undefined) if value == Kind::Undefined
99
+ __kind_error(Kind::Undefined) if Kind::Undefined == value
116
100
 
117
101
  yield
118
102
  end
@@ -137,7 +121,7 @@ module Kind
137
121
  if ::Kind::Maybe::Value.none?(default)
138
122
  __kind_undefined(value) { __kind_of(value) }
139
123
  else
140
- return value if value != Kind::Undefined && instance?(value)
124
+ return value if Kind::Undefined != value && instance?(value)
141
125
 
142
126
  __kind_undefined(default) { __kind_of(default) }
143
127
  end
@@ -155,7 +139,7 @@ module Kind
155
139
  def self.Boolean(object = Undefined, options = Empty::HASH)
156
140
  default = options[:or]
157
141
 
158
- return Kind::Of::Boolean if object == Undefined && default.nil?
142
+ return Kind::Of::Boolean if Undefined == object && default.nil?
159
143
 
160
144
  bool = object.nil? ? default : object
161
145
 
@@ -165,7 +149,7 @@ module Kind
165
149
  end
166
150
 
167
151
  const_set(:Boolean, ::Module.new do
168
- extend Checkable
152
+ extend Checker::Protocol
169
153
 
170
154
  def self.__kind; [TrueClass, FalseClass].freeze; end
171
155
 
@@ -177,14 +161,14 @@ module Kind
177
161
  if ::Kind::Maybe::Value.none?(default)
178
162
  __kind_undefined(value) { Kind::Of::Boolean(value) }
179
163
  else
180
- return value if value != Kind::Undefined && instance?(value)
164
+ return value if Kind::Undefined != value && instance?(value)
181
165
 
182
166
  __kind_undefined(default) { Kind::Of::Boolean(default) }
183
167
  end
184
168
  end
185
169
 
186
170
  def self.__kind_undefined(value)
187
- if value == Kind::Undefined
171
+ if Kind::Undefined == value
188
172
  raise Kind::Error.new('Boolean'.freeze, Kind::Undefined)
189
173
  else
190
174
  yield
@@ -210,7 +194,7 @@ module Kind
210
194
  def self.Lambda(object = Undefined, options = Empty::HASH)
211
195
  default = options[:or]
212
196
 
213
- return Kind::Of::Lambda if object == Undefined && default.nil?
197
+ return Kind::Of::Lambda if Undefined == object && default.nil?
214
198
 
215
199
  func = object || default
216
200
 
@@ -220,7 +204,7 @@ module Kind
220
204
  end
221
205
 
222
206
  const_set(:Lambda, ::Module.new do
223
- extend Checkable
207
+ extend Checker::Protocol
224
208
 
225
209
  def self.__kind; ::Proc; end
226
210
 
@@ -230,14 +214,14 @@ module Kind
230
214
  if ::Kind::Maybe::Value.none?(default)
231
215
  __kind_undefined(value) { Kind::Of::Lambda(value) }
232
216
  else
233
- return value if value != Kind::Undefined && instance?(value)
217
+ return value if Kind::Undefined != value && instance?(value)
234
218
 
235
219
  __kind_undefined(default) { Kind::Of::Lambda(default) }
236
220
  end
237
221
  end
238
222
 
239
223
  def self.__kind_undefined(value)
240
- if value == Kind::Undefined
224
+ if Kind::Undefined == value
241
225
  raise Kind::Error.new('Lambda'.freeze, Kind::Undefined)
242
226
  else
243
227
  yield
@@ -258,7 +242,7 @@ module Kind
258
242
  def self.Callable(object = Undefined, options = Empty::HASH)
259
243
  default = options[:or]
260
244
 
261
- return Kind::Of::Callable if object == Undefined && default.nil?
245
+ return Kind::Of::Callable if Undefined == object && default.nil?
262
246
 
263
247
  callable = object || default
264
248
 
@@ -268,7 +252,7 @@ module Kind
268
252
  end
269
253
 
270
254
  const_set(:Callable, ::Module.new do
271
- extend Checkable
255
+ extend Checker::Protocol
272
256
 
273
257
  def self.__kind; Object; end
274
258
 
@@ -282,14 +266,14 @@ module Kind
282
266
  if ::Kind::Maybe::Value.none?(default)
283
267
  __kind_undefined(value) { Kind::Of::Callable(value) }
284
268
  else
285
- return value if value != Kind::Undefined && instance?(value)
269
+ return value if Kind::Undefined != value && instance?(value)
286
270
 
287
271
  __kind_undefined(default) { Kind::Of::Callable(default) }
288
272
  end
289
273
  end
290
274
 
291
275
  def self.__kind_undefined(value)
292
- if value == Kind::Undefined
276
+ if Kind::Undefined == value
293
277
  raise Kind::Error.new('Callable'.freeze, Kind::Undefined)
294
278
  else
295
279
  yield
@@ -312,7 +296,7 @@ module Kind
312
296
  # -- Classes
313
297
  [
314
298
  String, Symbol, Numeric, Integer, Float, Regexp, Time,
315
- Array, Range, Hash, Struct, Enumerator, Set,
299
+ Array, Range, Hash, Struct, Enumerator, Set, OpenStruct,
316
300
  Method, Proc,
317
301
  IO, File
318
302
  ].each { |klass| Types.add(klass) }
@@ -53,12 +53,12 @@ class KindValidator < ActiveModel::EachValidator
53
53
  def kind_is_not(expected, value)
54
54
  case expected
55
55
  when Class
56
- return if Kind.of.Class(value) == expected || value < expected
56
+ return if expected == Kind.of.Class(value) || value < expected
57
57
 
58
58
  "must be the class or a subclass of `#{expected.name}`"
59
59
  when Module
60
60
  return if value.kind_of?(Class) && value <= expected
61
- return if Kind.of.Module(value) == expected || value.kind_of?(expected)
61
+ return if expected == Kind.of.Module(value) || value.kind_of?(expected)
62
62
 
63
63
  "must include the `#{expected.name}` module"
64
64
  else
@@ -1,78 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'kind/checker/factory'
4
+ require 'kind/checker/protocol'
3
5
  module Kind
4
- module Checkable
5
- def class?(value)
6
- Kind::Is.__call__(__kind, value)
7
- end
8
-
9
- def instance(value, options = Empty::HASH)
10
- default = options[:or]
11
-
12
- return Kind::Of.(__kind, value) if ::Kind::Maybe::Value.none?(default)
13
-
14
- value != Kind::Undefined && instance?(value) ? value : Kind::Of.(__kind, default)
15
- end
16
-
17
- def [](value, options = options = Empty::HASH)
18
- instance(value, options)
19
- end
20
-
21
- def to_proc
22
- @to_proc ||=
23
- -> checker { -> value { checker.instance(value) } }.call(self)
24
- end
25
-
26
- def __is_instance__(value)
27
- value.kind_of?(__kind)
28
- end
29
-
30
- def is_instance_to_proc
31
- @is_instance_to_proc ||=
32
- -> checker { -> value { checker.__is_instance__(value) } }.call(self)
33
- end
34
-
35
- def instance?(*args)
36
- return is_instance_to_proc if args.empty?
37
-
38
- return args.all? { |object| __is_instance__(object) } if args.size > 1
39
-
40
- arg = args[0]
41
- arg == Kind::Undefined ? is_instance_to_proc : __is_instance__(arg)
42
- end
43
-
44
- def or_nil(value)
45
- return value if instance?(value)
46
- end
47
-
48
- def or_undefined(value)
49
- or_nil(value) || Kind::Undefined
50
- end
51
-
52
- def __as_maybe__(value)
53
- Kind::Maybe.new(or_nil(value))
54
- end
55
-
56
- def as_maybe_to_proc
57
- @as_maybe_to_proc ||=
58
- -> checker { -> value { checker.__as_maybe__(value) } }.call(self)
59
- end
60
-
61
- def as_maybe(value = Kind::Undefined)
62
- return __as_maybe__(value) if value != Kind::Undefined
63
-
64
- as_maybe_to_proc
65
- end
66
-
67
- def as_optional(value = Kind::Undefined)
68
- as_maybe(value)
69
- end
70
- end
71
-
72
- private_constant :Checkable
73
-
74
6
  class Checker
75
- include Checkable
7
+ include Protocol
76
8
 
77
9
  attr_reader :__kind
78
10
 
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ module Kind
6
+ class Checker
7
+ class Factory
8
+ include Singleton
9
+
10
+ def self.create(kind)
11
+ instance.create(kind)
12
+ end
13
+
14
+ def initialize
15
+ @__checkers__ = {}
16
+ end
17
+
18
+ MODULE_OR_CLASS = 'Module/Class'.freeze
19
+
20
+ private_constant :MODULE_OR_CLASS
21
+
22
+ def create(kind)
23
+ @__checkers__[kind] ||= begin
24
+ kind_name = kind.name
25
+
26
+ if Kind::Of.const_defined?(kind_name, false)
27
+ Kind::Of.const_get(kind_name)
28
+ else
29
+ Kind::Checker.new(Kind::Of.(Module, kind, MODULE_OR_CLASS))
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ class Checker
5
+ module Protocol
6
+ def class?(value)
7
+ Kind::Is.__call__(__kind, value)
8
+ end
9
+
10
+ def instance(value, options = Empty::HASH)
11
+ default = options[:or]
12
+
13
+ return Kind::Of.(__kind, value) if ::Kind::Maybe::Value.none?(default)
14
+
15
+ Kind::Undefined != value && instance?(value) ? value : Kind::Of.(__kind, default)
16
+ end
17
+
18
+ def [](value, options = options = Empty::HASH)
19
+ instance(value, options)
20
+ end
21
+
22
+ def to_proc
23
+ @to_proc ||=
24
+ -> checker { -> value { checker.instance(value) } }.call(self)
25
+ end
26
+
27
+ def __is_instance__(value)
28
+ value.kind_of?(__kind)
29
+ end
30
+
31
+ def is_instance_to_proc
32
+ @is_instance_to_proc ||=
33
+ -> checker { -> value { checker.__is_instance__(value) } }.call(self)
34
+ end
35
+
36
+ def instance?(*args)
37
+ return is_instance_to_proc if args.empty?
38
+
39
+ return args.all? { |object| __is_instance__(object) } if args.size > 1
40
+
41
+ arg = args[0]
42
+ Kind::Undefined == arg ? is_instance_to_proc : __is_instance__(arg)
43
+ end
44
+
45
+ def or_nil(value)
46
+ return value if instance?(value)
47
+ end
48
+
49
+ def or_undefined(value)
50
+ or_nil(value) || Kind::Undefined
51
+ end
52
+
53
+ def __as_maybe__(value)
54
+ Kind::Maybe.new(or_nil(value))
55
+ end
56
+
57
+ def as_maybe_to_proc
58
+ @as_maybe_to_proc ||=
59
+ -> checker { -> value { checker.__as_maybe__(value) } }.call(self)
60
+ end
61
+
62
+ def as_maybe(value = Kind::Undefined)
63
+ return __as_maybe__(value) if Kind::Undefined != value
64
+
65
+ as_maybe_to_proc
66
+ end
67
+
68
+ def as_optional(value = Kind::Undefined)
69
+ as_maybe(value)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Dig
5
+ extend self
6
+
7
+ def call(data, keys)
8
+ return unless keys.is_a?(Array)
9
+
10
+ keys.reduce(data) do |memo, key|
11
+ value = get(memo, key)
12
+
13
+ break if value.nil?
14
+
15
+ value
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def get(data, key)
22
+ return data[key] if Hash === data
23
+
24
+ case data
25
+ when Array
26
+ data[key] if key.respond_to?(:to_int)
27
+ when OpenStruct
28
+ data[key] if key.respond_to?(:to_sym)
29
+ when Struct
30
+ if key.respond_to?(:to_int) || key.respond_to?(:to_sym)
31
+ data[key] rescue nil
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -7,7 +7,7 @@ module Kind
7
7
  private_constant :UNDEFINED_OBJECT
8
8
 
9
9
  def initialize(arg, object = UNDEFINED_OBJECT)
10
- if object == UNDEFINED_OBJECT
10
+ if UNDEFINED_OBJECT == object
11
11
  # Will be used when the exception was raised with a message. e.g:
12
12
  # raise Kind::Error, "some message"
13
13
  super(arg)
@@ -1,10 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'kind/dig'
4
+
3
5
  module Kind
4
6
  module Maybe
7
+ class Typed
8
+ def initialize(kind)
9
+ @kind_checker = Kind::Checker::Factory.create(kind)
10
+ end
11
+
12
+ def wrap(value)
13
+ @kind_checker.as_maybe(value)
14
+ end
15
+
16
+ alias_method :new, :wrap
17
+ alias_method :[], :wrap
18
+ end
19
+
5
20
  module Value
6
21
  def self.none?(value)
7
- value == nil || value == Undefined
22
+ value.nil? || Undefined == value
8
23
  end
9
24
 
10
25
  def self.some?(value)
@@ -42,9 +57,9 @@ module Kind
42
57
  INVALID_DEFAULT_ARG = 'the default value must be defined as an argument or block'.freeze
43
58
 
44
59
  def value_or(default = Undefined, &block)
45
- raise ArgumentError, INVALID_DEFAULT_ARG if default == Undefined && !block
60
+ raise ArgumentError, INVALID_DEFAULT_ARG if Undefined == default && !block
46
61
 
47
- default != Undefined ? default : block.call
62
+ Undefined != default ? default : block.call
48
63
  end
49
64
 
50
65
  def none?; true; end
@@ -55,10 +70,16 @@ module Kind
55
70
 
56
71
  alias_method :then, :map
57
72
 
58
- def try(method_name = Undefined, &block)
59
- Kind.of.Symbol(method_name) if method_name != Undefined
73
+ def try!(method_name = Undefined, *args, &block)
74
+ Kind.of.Symbol(method_name) if Undefined != method_name
60
75
 
61
- nil
76
+ NONE_WITH_NIL_VALUE
77
+ end
78
+
79
+ alias_method :try, :try!
80
+
81
+ def dig(*keys)
82
+ NONE_WITH_NIL_VALUE
62
83
  end
63
84
 
64
85
  private_constant :INVALID_DEFAULT_ARG
@@ -79,22 +100,47 @@ module Kind
79
100
  def map(&fn)
80
101
  result = fn.call(@value)
81
102
 
82
- return result if Maybe::None === result
83
- return NONE_WITH_NIL_VALUE if result == nil
84
- return NONE_WITH_UNDEFINED_VALUE if result == Undefined
85
-
86
- Some.new(result)
103
+ resolve(result)
87
104
  end
88
105
 
89
106
  alias_method :then, :map
90
107
 
91
- def try(method_name = Undefined, *args, &block)
92
- fn = method_name == Undefined ? block : Kind.of.Symbol(method_name).to_proc
108
+ def try!(method_name = Undefined, *args, &block)
109
+ Kind::Of::Symbol(method_name) if Undefined != method_name
110
+
111
+ __try__(method_name, args, block)
112
+ end
93
113
 
94
- result = args.empty? ? fn.call(value) : fn.call(*args.unshift(value))
114
+ def try(method_name = Undefined, *args, &block)
115
+ if (Undefined != method_name && value.respond_to?(Kind::Of::Symbol(method_name))) ||
116
+ (Undefined == method_name && block)
117
+ __try__(method_name, args, block)
118
+ else
119
+ NONE_WITH_NIL_VALUE
120
+ end
121
+ end
95
122
 
96
- return result if Maybe::Value.some?(result)
123
+ def dig(*keys)
124
+ resolve(Kind::Dig.call(value, keys))
97
125
  end
126
+
127
+ private
128
+
129
+ def __try__(method_name = Undefined, args, block)
130
+ fn = Undefined == method_name ? block : method_name.to_proc
131
+
132
+ result = args.empty? ? fn.call(value) : fn.call(*args.unshift(value))
133
+
134
+ resolve(result)
135
+ end
136
+
137
+ def resolve(result)
138
+ return result if Maybe::None === result
139
+ return NONE_WITH_NIL_VALUE if result.nil?
140
+ return NONE_WITH_UNDEFINED_VALUE if Undefined == result
141
+
142
+ Some.new(result)
143
+ end
98
144
  end
99
145
 
100
146
  def self.new(value)
@@ -102,7 +148,11 @@ module Kind
102
148
  result_type.new(value)
103
149
  end
104
150
 
105
- def self.[](value);
151
+ def self.[](value)
152
+ new(value)
153
+ end
154
+
155
+ def self.wrap(value)
106
156
  new(value)
107
157
  end
108
158
 
@@ -132,4 +182,12 @@ module Kind
132
182
  def self.Some(value)
133
183
  Maybe.some(value)
134
184
  end
185
+
186
+ def self.Maybe(kind)
187
+ Maybe::Typed.new(kind)
188
+ end
189
+
190
+ def self.Optional(kind)
191
+ Maybe::Typed.new(kind)
192
+ end
135
193
  end
@@ -10,7 +10,7 @@ module Kind
10
10
  def self.%{method_name}(object = Undefined, options = Empty::HASH)
11
11
  default = options[:or]
12
12
 
13
- return Kind::Of::%{kind_name} if object == Undefined && default.nil?
13
+ return Kind::Of::%{kind_name} if Undefined == object && default.nil?
14
14
 
15
15
  is_instance = Kind::Of::%{kind_name}.__is_instance__(object)
16
16
 
@@ -28,7 +28,7 @@ module Kind
28
28
 
29
29
  KIND_IS = <<-RUBY
30
30
  def self.%{method_name}(value = Undefined)
31
- return Kind::Is::%{kind_name} if value == Undefined
31
+ return Kind::Is::%{kind_name} if Undefined == value
32
32
 
33
33
  Kind::Is.__call__(::%{kind_name_to_check}, value)
34
34
  end
@@ -98,7 +98,7 @@ module Kind
98
98
  kind_name = params[:kind_name]
99
99
  params[:kind_name_to_check] ||= kind_name
100
100
 
101
- kind_checker = ::Module.new { extend Checkable }
101
+ kind_checker = ::Module.new { extend Checker::Protocol }
102
102
  kind_checker.module_eval("def self.__kind; #{params[:kind_name_to_check]}; end")
103
103
 
104
104
  kind_of_mod.instance_eval(KIND_OF % params)
@@ -19,7 +19,7 @@ module Kind
19
19
  end
20
20
 
21
21
  def undefined.default(value, default)
22
- return self if value != self
22
+ return self if self != value
23
23
 
24
24
  default.respond_to?(:call) ? default.call : default
25
25
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kind
4
- VERSION = '2.1.0'
4
+ VERSION = '3.1.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kind
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-12 00:00:00.000000000 Z
11
+ date: 2020-07-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A simple type system (at runtime) for Ruby - free of dependencies.
14
14
  email:
@@ -32,6 +32,9 @@ files:
32
32
  - lib/kind/active_model/kind_validator.rb
33
33
  - lib/kind/active_model/validation.rb
34
34
  - lib/kind/checker.rb
35
+ - lib/kind/checker/factory.rb
36
+ - lib/kind/checker/protocol.rb
37
+ - lib/kind/dig.rb
35
38
  - lib/kind/empty.rb
36
39
  - lib/kind/error.rb
37
40
  - lib/kind/is.rb