kind 3.1.0 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/.tool-versions +1 -0
  3. data/.travis.sh +67 -12
  4. data/.travis.yml +7 -5
  5. data/CHANGELOG.md +1647 -0
  6. data/Gemfile +22 -7
  7. data/README.md +920 -486
  8. data/kind.gemspec +1 -1
  9. data/lib/kind.rb +2 -314
  10. data/lib/kind/__lib__/attributes.rb +66 -0
  11. data/lib/kind/__lib__/kind.rb +71 -0
  12. data/lib/kind/__lib__/undefined.rb +14 -0
  13. data/lib/kind/action.rb +92 -0
  14. data/lib/kind/active_model/validation.rb +3 -4
  15. data/lib/kind/basic.rb +73 -0
  16. data/lib/kind/basic/error.rb +29 -0
  17. data/lib/kind/{undefined.rb → basic/undefined.rb} +8 -1
  18. data/lib/kind/dig.rb +31 -11
  19. data/lib/kind/either.rb +30 -0
  20. data/lib/kind/either/left.rb +29 -0
  21. data/lib/kind/either/methods.rb +17 -0
  22. data/lib/kind/either/monad.rb +65 -0
  23. data/lib/kind/either/monad/wrapper.rb +19 -0
  24. data/lib/kind/either/right.rb +38 -0
  25. data/lib/kind/empty.rb +4 -10
  26. data/lib/kind/empty/constant.rb +7 -0
  27. data/lib/kind/enum.rb +63 -0
  28. data/lib/kind/enum/item.rb +40 -0
  29. data/lib/kind/enum/methods.rb +72 -0
  30. data/lib/kind/function.rb +47 -0
  31. data/lib/kind/functional.rb +89 -0
  32. data/lib/kind/functional/action.rb +89 -0
  33. data/lib/kind/immutable_attributes.rb +34 -0
  34. data/lib/kind/immutable_attributes/initializer.rb +70 -0
  35. data/lib/kind/immutable_attributes/reader.rb +38 -0
  36. data/lib/kind/maybe.rb +35 -159
  37. data/lib/kind/maybe/methods.rb +21 -0
  38. data/lib/kind/maybe/monad.rb +82 -0
  39. data/lib/kind/maybe/monad/wrapper.rb +19 -0
  40. data/lib/kind/maybe/none.rb +50 -0
  41. data/lib/kind/maybe/some.rb +132 -0
  42. data/lib/kind/maybe/typed.rb +35 -0
  43. data/lib/kind/maybe/wrapper.rb +37 -0
  44. data/lib/kind/monad.rb +22 -0
  45. data/lib/kind/monads.rb +5 -0
  46. data/lib/kind/objects.rb +17 -0
  47. data/lib/kind/objects/basic_object.rb +45 -0
  48. data/lib/kind/objects/modules.rb +32 -0
  49. data/lib/kind/objects/modules/core/array.rb +19 -0
  50. data/lib/kind/objects/modules/core/class.rb +13 -0
  51. data/lib/kind/objects/modules/core/comparable.rb +13 -0
  52. data/lib/kind/objects/modules/core/enumerable.rb +13 -0
  53. data/lib/kind/objects/modules/core/enumerator.rb +13 -0
  54. data/lib/kind/objects/modules/core/file.rb +13 -0
  55. data/lib/kind/objects/modules/core/float.rb +13 -0
  56. data/lib/kind/objects/modules/core/hash.rb +19 -0
  57. data/lib/kind/objects/modules/core/integer.rb +13 -0
  58. data/lib/kind/objects/modules/core/io.rb +13 -0
  59. data/lib/kind/objects/modules/core/method.rb +13 -0
  60. data/lib/kind/objects/modules/core/module.rb +17 -0
  61. data/lib/kind/objects/modules/core/numeric.rb +13 -0
  62. data/lib/kind/objects/modules/core/proc.rb +13 -0
  63. data/lib/kind/objects/modules/core/queue.rb +14 -0
  64. data/lib/kind/objects/modules/core/range.rb +13 -0
  65. data/lib/kind/objects/modules/core/regexp.rb +13 -0
  66. data/lib/kind/objects/modules/core/string.rb +19 -0
  67. data/lib/kind/objects/modules/core/struct.rb +13 -0
  68. data/lib/kind/objects/modules/core/symbol.rb +13 -0
  69. data/lib/kind/objects/modules/core/time.rb +13 -0
  70. data/lib/kind/objects/modules/custom/boolean.rb +19 -0
  71. data/lib/kind/objects/modules/custom/callable.rb +19 -0
  72. data/lib/kind/objects/modules/custom/lambda.rb +19 -0
  73. data/lib/kind/objects/modules/stdlib/open_struct.rb +15 -0
  74. data/lib/kind/objects/modules/stdlib/set.rb +19 -0
  75. data/lib/kind/objects/nil.rb +17 -0
  76. data/lib/kind/objects/not_nil.rb +13 -0
  77. data/lib/kind/objects/object.rb +56 -0
  78. data/lib/kind/objects/respond_to.rb +30 -0
  79. data/lib/kind/objects/union_type.rb +44 -0
  80. data/lib/kind/presence.rb +35 -0
  81. data/lib/kind/result.rb +31 -0
  82. data/lib/kind/result/abstract.rb +53 -0
  83. data/lib/kind/result/failure.rb +31 -0
  84. data/lib/kind/result/methods.rb +17 -0
  85. data/lib/kind/result/monad.rb +69 -0
  86. data/lib/kind/result/monad/wrapper.rb +19 -0
  87. data/lib/kind/result/success.rb +40 -0
  88. data/lib/kind/try.rb +46 -0
  89. data/lib/kind/validator.rb +112 -1
  90. data/lib/kind/version.rb +1 -1
  91. data/test.sh +4 -4
  92. metadata +81 -13
  93. data/lib/kind/active_model/kind_validator.rb +0 -96
  94. data/lib/kind/checker.rb +0 -15
  95. data/lib/kind/checker/factory.rb +0 -35
  96. data/lib/kind/checker/protocol.rb +0 -73
  97. data/lib/kind/error.rb +0 -19
  98. data/lib/kind/is.rb +0 -19
  99. data/lib/kind/of.rb +0 -11
  100. data/lib/kind/types.rb +0 -115
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b62da87ad8443fb4d3f1d96535bc69259addf767721db70288401d677733967
4
- data.tar.gz: 995f0a3a34ed67a3244e181380d627e076d4576a2b6e0c7de2b34fa415c5f868
3
+ metadata.gz: e522116072626ac76a87df9351e292f31a0f96cefa1bbc07d9e83c50198a25a8
4
+ data.tar.gz: 529cd2b05c6767c2553ce9fa1e157cef564e7fc0092874839c6714e0df797240
5
5
  SHA512:
6
- metadata.gz: 3398bbf2b67fe8b81023d9c781b14ac115f5749f45641a74fc1ff1c8d05f4fd1c4e2a4bc817d6c71e7f8dc96b245ec9f9c80a776ae11d22ba69dbc878dfaa847
7
- data.tar.gz: 75c6c1e387548447edf601d4b73b18ff3e2700debf3f6715b2f772f582829d39a72e7bb9d8af61fe8bcd32e9f72aa8398f1703f2c706668a0ab5e59fc0612b05
6
+ metadata.gz: 206cacdc59d7288b4db022dac5c45fe95649845bb0093fbee14eb86444b145651407847b380b831c8267190f1d515b7c775810549c6ee73df61059f1d484129a
7
+ data.tar.gz: ff97ce3f297254fd3932b853588c6d1f47a931c16065b5961e06fc4fe219ec6672db0fe16e1d0ffef4b34d25c6e9c35cc28c655b3cc539b519e916f60162ba04
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.0.0
data/.travis.sh CHANGED
@@ -1,20 +1,75 @@
1
1
  #!/bin/bash
2
2
 
3
- bundle exec rake test
3
+ RUBY_V=$(ruby -v)
4
4
 
5
- ruby_v=$(ruby -v)
5
+ function run_basic_tests {
6
+ if [ ! -z "$1" ]; then
7
+ bundle_cmd="bundle _$1_"
8
+ else
9
+ bundle_cmd="bundle"
10
+ fi
6
11
 
7
- ACTIVEMODEL_VERSION='3.2' bundle update && bundle exec rake test
8
- ACTIVEMODEL_VERSION='4.0' bundle update && bundle exec rake test
9
- ACTIVEMODEL_VERSION='4.1' bundle update && bundle exec rake test
10
- ACTIVEMODEL_VERSION='4.2' bundle update && bundle exec rake test
11
- ACTIVEMODEL_VERSION='5.0' bundle update && bundle exec rake test
12
- ACTIVEMODEL_VERSION='5.1' bundle update && bundle exec rake test
12
+ eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/{basic/*_test,basic_test}.rb'"
13
+ eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/enum_test.rb'"
14
+ eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/presence_test.rb'"
15
+ eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/dig_test.rb'"
16
+ eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/try_test.rb'"
17
+ eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/maybe_test.rb'"
18
+ eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/immutable_attributes_test.rb'"
19
+ eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/function_test.rb'"
20
+ eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/action_test.rb'"
21
+ eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/{functional/*_test,functional_test}.rb'"
22
+ eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/either/*_test.rb'"
23
+ eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/result/*_test.rb'"
24
+ }
13
25
 
14
- if [[ ! $ruby_v =~ '2.2.0' ]]; then
15
- ACTIVEMODEL_VERSION='5.2' bundle update && bundle exec rake test
26
+ function run_with_bundler {
27
+ rm Gemfile.lock
28
+
29
+ if [ ! -z "$1" ]; then
30
+ bundle_cmd="bundle _$1_"
31
+ else
32
+ bundle_cmd="bundle"
33
+ fi
34
+
35
+ eval "$2 $bundle_cmd update"
36
+ eval "$2 $bundle_cmd exec rake test"
37
+ }
38
+
39
+ function run_with_am_version_and_bundler {
40
+ run_with_bundler "$2" "ACTIVEMODEL_VERSION=$1"
41
+ }
42
+
43
+ RUBY_2_12="ruby 2.[12]."
44
+ RUBY_2_2345="ruby 2.[2345]."
45
+ RUBY_2_12345="ruby 2.[12345]."
46
+ RUBY_2_567="ruby 2.[567]."
47
+ RUBY_3_0="ruby 3.0."
48
+
49
+ if [[ $RUBY_V =~ $RUBY_2_12 ]]; then
50
+ run_with_am_version_and_bundler "3.2" "$BUNDLER_V1"
16
51
  fi
17
52
 
18
- if [[ $ruby_v =~ '2.5.' ]] || [[ $ruby_v =~ '2.6.' ]] || [[ $ruby_v =~ '2.7.' ]]; then
19
- ACTIVEMODEL_VERSION='6.0' bundle update && bundle exec rake test
53
+ if [[ $RUBY_V =~ $RUBY_2_2345 ]]; then
54
+ run_with_am_version_and_bundler "4.0" "$BUNDLER_V1"
55
+ run_with_am_version_and_bundler "4.1" "$BUNDLER_V1"
56
+ run_with_am_version_and_bundler "4.2" "$BUNDLER_V1"
57
+ run_with_am_version_and_bundler "5.0" "$BUNDLER_V1"
58
+ run_with_am_version_and_bundler "5.1" "$BUNDLER_V1"
59
+ run_with_am_version_and_bundler "5.2" "$BUNDLER_V1"
60
+ fi
61
+
62
+ if [[ $RUBY_V =~ $RUBY_2_12345 ]]; then
63
+ run_basic_tests "$BUNDLER_V1"
64
+ run_with_bundler "$BUNDLER_V1"
65
+ fi
66
+
67
+ if [[ $RUBY_V =~ $RUBY_2_567 ]] || [[ $RUBY_V =~ $RUBY_3_0 ]]; then
68
+ gem install bundler -v ">= 2" --no-doc
69
+
70
+ run_with_am_version_and_bundler "6.0"
71
+ run_with_am_version_and_bundler "6.1"
72
+
73
+ run_basic_tests
74
+ run_with_bundler
20
75
  fi
data/.travis.yml CHANGED
@@ -1,24 +1,26 @@
1
1
  ---
2
2
  language: ruby
3
3
 
4
- sudo: false
5
-
6
4
  cache:
7
5
  bundler: true
8
6
  directories:
9
7
  - /home/travis/.rvm/
10
8
 
11
9
  rvm:
12
- - 2.2.0
10
+ - 2.1.10
11
+ - 2.2.2
13
12
  - 2.3.0
14
13
  - 2.4.0
15
14
  - 2.5.0
16
15
  - 2.6.0
17
16
  - 2.7.0
17
+ - 3.0.0
18
+
19
+ env:
20
+ - BUNDLER_V1="1.17.3"
18
21
 
19
22
  before_install:
20
- - gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true
21
- - gem install bundler -v '< 2'
23
+ - gem install bundler -v "$BUNDLER_V1"
22
24
 
23
25
  install: bundle install --jobs=3 --retry=3
24
26
 
data/CHANGELOG.md ADDED
@@ -0,0 +1,1647 @@
1
+ # Changelog <!-- omit in toc -->
2
+
3
+ This project follows [semver 2.0.0](http://semver.org/spec/v2.0.0.html) and the recommendations of [keepachangelog.com](http://keepachangelog.com/).
4
+
5
+ - [Unreleased](#unreleased)
6
+ - [5.2.0 (2021-03-17)](#520-2021-03-17)
7
+ - [Added](#added)
8
+ - [Deprecated](#deprecated)
9
+ - [Changes](#changes)
10
+ - [5.1.0 (2021-02-23)](#510-2021-02-23)
11
+ - [Added](#added-1)
12
+ - [Deprecated](#deprecated-1)
13
+ - [5.0.0 (2021-02-22)](#500-2021-02-22)
14
+ - [Breaking Changes](#breaking-changes)
15
+ - [Removed](#removed)
16
+ - [4.1.0 (2021-02-22)](#410-2021-02-22)
17
+ - [Added](#added-2)
18
+ - [4.0.0 (2021-02-22)](#400-2021-02-22)
19
+ - [Added](#added-3)
20
+ - [Deprecated](#deprecated-2)
21
+ - [Fixed](#fixed)
22
+ - [3.1.0 (2020-07-08)](#310-2020-07-08)
23
+ - [Added](#added-4)
24
+ - [3.0.0 (2020-06-25)](#300-2020-06-25)
25
+ - [Breaking Changes](#breaking-changes-1)
26
+ - [Added](#added-5)
27
+ - [2.3.0 (2020-06-24)](#230-2020-06-24)
28
+ - [Added](#added-6)
29
+ - [2.2.0 (2020-06-23)](#220-2020-06-23)
30
+ - [Added](#added-7)
31
+ - [2.1.0 (2020-05-12)](#210-2020-05-12)
32
+ - [Added](#added-8)
33
+ - [Breaking Changes](#breaking-changes-2)
34
+ - [2.0.0 (2020-05-07)](#200-2020-05-07)
35
+ - [Added](#added-9)
36
+ - [Breaking Changes](#breaking-changes-3)
37
+ - [Removed](#removed-1)
38
+ - [1.9.0 (2020-05-06)](#190-2020-05-06)
39
+ - [Added](#added-10)
40
+ - [1.8.0 (2020-05-03)](#180-2020-05-03)
41
+ - [Added](#added-11)
42
+ - [1.7.0 (2020-05-03)](#170-2020-05-03)
43
+ - [Fixed](#fixed-1)
44
+ - [1.6.0 (2020-04-17)](#160-2020-04-17)
45
+ - [Added](#added-12)
46
+ - [Changes](#changes-1)
47
+ - [1.5.0 (2020-04-12)](#150-2020-04-12)
48
+ - [Added](#added-13)
49
+ - [1.4.0 (2020-04-12)](#140-2020-04-12)
50
+ - [Added](#added-14)
51
+ - [1.3.0 (2020-04-12)](#130-2020-04-12)
52
+ - [Added](#added-15)
53
+ - [1.2.0 (2020-04-12)](#120-2020-04-12)
54
+ - [Added](#added-16)
55
+ - [1.1.0 (2020-04-09)](#110-2020-04-09)
56
+ - [Added](#added-17)
57
+ - [Fixed](#fixed-2)
58
+ - [1.0.0 (2020-03-16)](#100-2020-03-16)
59
+ - [Added](#added-18)
60
+ - [0.6.0 (2020-01-06)](#060-2020-01-06)
61
+ - [Added](#added-19)
62
+ - [0.5.0 (2020-01-04)](#050-2020-01-04)
63
+ - [Added](#added-20)
64
+ - [0.4.0 (2020-01-03)](#040-2020-01-03)
65
+ - [Added](#added-21)
66
+ - [0.3.0 (2020-01-03)](#030-2020-01-03)
67
+ - [Added](#added-22)
68
+ - [Breaking Changes](#breaking-changes-4)
69
+ - [0.2.0 (2020-01-02)](#020-2020-01-02)
70
+ - [Added](#added-23)
71
+ - [0.1.0 (2019-12-26)](#010-2019-12-26)
72
+ - [Added](#added-24)
73
+
74
+ ## Unreleased
75
+
76
+ <!--
77
+ ### Added
78
+ ### Breaking Changes
79
+ ### Deprecated
80
+ ### Removed
81
+ ### Fixed
82
+ -->
83
+
84
+ 5.2.0 (2021-03-17)
85
+ ------------------
86
+
87
+ ### Added
88
+
89
+ * [#46](https://github.com/serradura/kind/pull/46) - Add `Kind.respond_to?`. This method has two different behaviors, when it receives just one argument it will behave like the native Ruby's
90
+ `respond_to?` method, that is, it will check if the `Kind` method implements some method. But if it receives two arguments, the first one will be the object, and the others (one or more) will be a list of method names that will be used to check if the given object implements them. e.g.
91
+ ```ruby
92
+ Kind.respond_to?(:is?) # true
93
+
94
+ Kind.respond_to?(:foo?) # true
95
+
96
+ # --
97
+
98
+ Kind.respond_to?({}, :fetch, :merge) # true
99
+
100
+ Kind.respond_to?([], :fetch, :merge) # false
101
+ ```
102
+
103
+ * [#46](https://github.com/serradura/kind/pull/46) - Add `Kind::UnionType`. This class allows the creation of an object that knows how to compare a value through different kinds of types. e.g.
104
+ ```ruby
105
+ # The method [] can build a union type object that will have the method #|
106
+ # which allows the composition with other object kinds.
107
+ array_or_hash = Kind::UnionType[Array] | Hash
108
+
109
+ # The method === can verify if a given value is one of the kinds that compounds the union type.
110
+ array_or_hash === {} # true
111
+ array_or_hash === [] # true
112
+ array_or_hash === 1 # false
113
+ array_or_hash === nil # false
114
+
115
+ # The method #[] will return the given value if it has one of the expected kinds,
116
+ # but if not, it will raise a Kind::Error.
117
+ array_or_hash[{}] # {}
118
+
119
+ array_or_hash[1] # Kind::Error (1 expected to be a kind of (Array | Hash))
120
+
121
+ # At last, the method #name is an alias to the method #inspect.
122
+ array_or_hash.name # "(Array | Hash)"
123
+ array_or_hash.inspect # "(Array | Hash)"
124
+ ```
125
+
126
+ * [#46](https://github.com/serradura/kind/pull/46) - Add `Kind::Nil`. This module was added to be used to create union types. e.g.
127
+ ```ruby
128
+ hash_or_nil = Kind::UnionType[Hash] | Kind::Nil
129
+
130
+ hash_or_nil === {} # true
131
+ hash_or_nil === [] # false
132
+ hash_or_nil === 1 # false
133
+ hash_or_nil === nil # true
134
+ ```
135
+
136
+ * [#46](https://github.com/serradura/kind/pull/46), [#47](https://github.com/serradura/kind/pull/47) - Add `Kind::NotNil`. This module was added to perform a strict verification where the given value will be returned if it is not nil, and if not, a `Kind::Error` will be raised. e.g.
137
+ ```ruby
138
+ Kind::NotNil[1] # 1
139
+
140
+ Kind::NotNil[nil] # Kind::Error (expected to not be nil)
141
+
142
+ Kind::NotNil[nil, label: 'Foo#bar'] # Kind::Error (Foo#bar: expected to not be nil)
143
+ ```
144
+
145
+ * [#46](https://github.com/serradura/kind/pull/46) - Add `Kind::RespondTo` to create objects that know how to verify if a given object implements one or more expected methods.
146
+ ```ruby
147
+ HashLike = Kind::RespondTo[:fetch, :merge!]
148
+ Fetchable = Kind::RespondTo[:fetch]
149
+
150
+ # Verifying if an object implements the expected interface.
151
+ HashLike === ENV # true
152
+ HashLike === {} # true
153
+ HashLike === [] # false
154
+
155
+ Fetchable === ENV # true
156
+ Fetchable === [] # true
157
+ Fetchable === {} # true
158
+
159
+ # Performing an strict verification
160
+ HashLike[ENV] # true
161
+ HashLike[{}] # true
162
+ HashLike[Array.new] # false Kind::Error ([] expected to be a kind of Kind::RespondTo[:fetch, :merge!])
163
+
164
+ # Creating union types using interfaces
165
+ HashLikeOrArray = HashLike | Array # Kind::RespondTo[:fetch, :merge!] | Array
166
+
167
+ HashLikeOrArray === {} # true
168
+ HashLikeOrArray === [] # true
169
+ HashLikeOrArray === ENV # true
170
+ ```
171
+
172
+ * [#46](https://github.com/serradura/kind/pull/46) - Unfreeze the output of `Kind::Boolean.kind`.
173
+
174
+ * [#46](https://github.com/serradura/kind/pull/46) - Freeze `Kind::UNDEFINED` and define its inspect method.
175
+
176
+ * [#46](https://github.com/serradura/kind/pull/46) - Add `Kind::TypeChecker#|` to allow the creation of union types.
177
+ ```ruby
178
+ StatusLabel = Kind::String | Kind::Symbol
179
+
180
+ StatusLabel === :ok # true
181
+ StatusLabel === 'ok' # true
182
+ StatusLabel === true # false
183
+ ```
184
+
185
+ * [#46](https://github.com/serradura/kind/pull/46) - Allow `Kind.of()` and `Kind::TypeChecker#[]` receive a label.
186
+ ```ruby
187
+ # Kind.of(<Type>, value, label:)
188
+ class Person
189
+ attr_reader :name
190
+
191
+ def initialize(name:)
192
+ @name = Kind.of(String, name, label: 'Person#name')
193
+ end
194
+ end
195
+
196
+ Person.new(name: 'Rodrigo') # #<Person:0x0000... @name="Rodrigo">
197
+ Person.new(name: :ok) # Kind::Error (Person#name: :ok expected to be a kind of String)
198
+
199
+ # Kind<Type>[value, label:]
200
+ class User
201
+ attr_reader :name
202
+
203
+ def initialize(name:)
204
+ @name = Kind::String[name, label: 'User#name']
205
+ end
206
+ end
207
+
208
+ User.new(name: 'Rodrigo') # #<User:0x0000... @name="Rodrigo">
209
+ User.new(name: :ok) # Kind::Error (User#name: :ok expected to be a kind of String)
210
+ ```
211
+
212
+ * [#46](https://github.com/serradura/kind/pull/46) - Create `kind/basic` to expose a small set of features to do type handling/verification. This is util if you want to make use only of these features instead of all of the others that are loaded via `require "kind"`. So, use `require "kind/basic"` if you only want these features:
213
+ * `Kind.is?`
214
+ * `Kind.of`
215
+ * `Kind.of?`
216
+ * `Kind.of_class?`
217
+ * `Kind.of_module?`
218
+ * `Kind.of_module_or_class`
219
+ * `Kind.respond_to`
220
+ * `Kind.respond_to?`
221
+ * `Kind.value`
222
+ * `Kind::Error`
223
+ * `Kind::Undefined`
224
+
225
+ * [#46](https://github.com/serradura/kind/pull/46) - Improve `Kind::Maybe`.
226
+ * Improve the `#inspect` output.
227
+ * Make `Kind::Maybe.{new,[],wrap}` return `None` if they receive an exception instance.
228
+ * Add `#accept` as an alias of `#check` method.
229
+ * Add `#reject` as the reverse of the methods `#check` and `#accept`.
230
+ * Allow the methods `#map`, `#map!`, `#then`, `#then!`, `#check`, `#accept`, `#reject` to receive one symbol as an argument, it will be used to perform the correspondent method in the `Maybe` value, so if the object responds to the expected method a `Some` will be returned.
231
+ * Add `Kind::Maybe#on`, this method allows you to use a block where will be possible to define a `Some` or `None` handling. The output of the matched result (some or none) will be the block's output.
232
+ ```ruby
233
+ # Kind::Maybe#accept (an alias of Kind::Maybe#check)
234
+ Kind::Maybe[1].accept(&:odd?) # #<Kind::Some value=1>
235
+ Kind::Maybe[1].accept(&:even?) # #<Kind::None value=nil>
236
+
237
+ # Kind::Maybe#reject (the reverse of Kind::Maybe#check)
238
+ Kind::Maybe[1].reject(&:odd?) # #<Kind::None value=nil>
239
+ Kind::Maybe[1].reject(&:even?) # #<Kind::Some value=1>
240
+
241
+ # Passing one symbol as an argument of the methods: `#map`, `#then`, `#check`, `#accept`, `#reject`
242
+ Kind::Maybe['1'].map(:to_i) # #<Kind::Some value=1>
243
+ Kind::Maybe[' '].then(:strip) # #<Kind::Some value="">
244
+
245
+ Kind::Maybe['1'].map!(:to_i).accept(:odd?) # #<Kind::Some value=1>
246
+ Kind::Maybe[' '].then!(:strip).reject(:empty?) # #<Kind::None value=nil>
247
+
248
+ # `Kind::Maybe#on` making use of a block to handle Some or None results.
249
+ number = Kind::Maybe['2'].then(:to_i).reject(:even?).on do |result|
250
+ result.none { 0 }
251
+ result.some { 1 }
252
+ end
253
+
254
+ p number # 0
255
+ ```
256
+
257
+ * [#46](https://github.com/serradura/kind/pull/46) - Add `Kind::Either` (either monad). This is basically the same as the `Maybe` monad, but with `Some` called `Right` and `None` called `Left`. But this time, `Left` is also allowed to have an underlying value. This module isn't loaded by default, so you will need to require it.
258
+ ```ruby
259
+ require 'kind/either'
260
+
261
+ # Use the methods Kind::Left() or Kind::Right() to create either monads
262
+ Kind::Left(0) # #<Kind::Left value=0>
263
+ Kind::Right(1) # #<Kind::Right value=1>
264
+
265
+ # The Kind::Either.new() or Kind::Either[] also creates a Kind::Right monad
266
+ Kind::Either.new(2) # #<Kind::Right value=2>
267
+ Kind::Either[3] # #<Kind::Right value=3>
268
+
269
+ # An Either has methods that allow you to know what kind it is.
270
+ monad = Kind::Right(1)
271
+ monad.right? # true
272
+ monad.left? # false
273
+
274
+ # This monad allows you to chain a sequence of operations that will continue while the output
275
+ # of each step is a Right monad. So, if some step return a Left, all of the next steps will be avoided.
276
+ # Let's see an example of how you can use the method #map to define a sequence of operations.
277
+
278
+ def do_some_calculation(arg)
279
+ Kind::Right(arg)
280
+ .map { |value| Kind::Numeric?(value) ? Kind::Right(value + 2) : Kind::Left('value must be numeric') }
281
+ .map { |value| value.odd? ? Kind::Right(value) : Kind::Left('value can\'t be even') }
282
+ .map { |value| Kind::Right(value * 3) }
283
+ end
284
+
285
+ do_some_calculation('1') # #<Kind::Left value="value must be numeric">
286
+ do_some_calculation(2) # #<Kind::Left value="value can't be even">
287
+ do_some_calculation(1) # #<Kind::Right value=9>
288
+
289
+ # You can use procs/lambdas as an alternative of blocks
290
+ Add = ->(a, b) do
291
+ return Kind::Right(a + b) if Kind::Numeric?(a, b)
292
+
293
+ Kind::Left('the arguments must be numerics')
294
+ end
295
+
296
+ Double = ->(number) do
297
+ return Kind::Right(number * 2) if Kind::Numeric?(number)
298
+
299
+ Kind::Left('the argument must be numeric')
300
+ end
301
+
302
+ AsString = ->(value) { Kind::Right(value.to_s) }
303
+
304
+ Add.(1, 2).map(&Double).map(&Double) # #<Kind::Right value=12>
305
+ Add.(1, 2).map(&AsString).map(&Double) # #<Kind::Left value="the argument must be numeric">
306
+
307
+ # The method #then is an alias for #map
308
+ Add.(2, 2).then(&Double).then(&AsString) # #<Kind::Right value="8">
309
+
310
+ # An exception will occur when your block or lambda doesn't return a Kind::Either
311
+ Add.(2, 2).map { |number| number * 2 } # Kind::Monad::Error (8 expected to be a kind of Kind::Right | Kind::Left)
312
+
313
+ # The methods #map, #then auto handle StandardError exceptions,
314
+ # so Left will be returned when an exception occur.
315
+ Add.(0, 0).map { |number| Kind::Right(10 / number) } # #<Kind::Left value=#<ZeroDivisionError: divided by 0>>
316
+
317
+ # You can use #map! or #then! if you don't want this auto exception handling.
318
+ Add.(0, 0).map! { |number| Kind::Right(10 / number) } # ZeroDivisionError (divided by 0)
319
+
320
+ Add.(0, 0).then! { |number| Kind::Right(10 / number) } # ZeroDivisionError (divided by 0)
321
+ ```
322
+
323
+ * [#46](https://github.com/serradura/kind/pull/46) - Add `Kind::Result` (an Either monad variation)
324
+
325
+ * [#46](https://github.com/serradura/kind/pull/46) - Add `Kind::Function`.
326
+
327
+ * [#46](https://github.com/serradura/kind/pull/46) - Add `Kind::Functional`.
328
+
329
+ * [#46](https://github.com/serradura/kind/pull/46) - Add `Kind::Try.presence` and improve the input/output handling of `Kind::Try.call`.
330
+
331
+ * [#46](https://github.com/serradura/kind/pull/46) - Add `Kind::Dig.presence` and improve the input/output handling of `Kind::Dig.call`.
332
+
333
+ * [#47](https://github.com/serradura/kind/pull/47) - Add `Kind.is!`
334
+
335
+ * [#47](https://github.com/serradura/kind/pull/47) - Create aliases to the methods `Kind.of` (`Kind.of!`) and `Kind.respond_to` (`Kind.respond_to!`)
336
+
337
+ * [#47](https://github.com/serradura/kind/pull/47) - Add `Kind[]` as the `Kind::Of()` substitute.
338
+
339
+ * [#49](https://github.com/serradura/kind/pull/49) - Add `Kind::Either::Methods` and `Kind::Result::Methods`
340
+
341
+ * [#49](https://github.com/serradura/kind/pull/49) - Add `Kind::Undefined.empty?`
342
+
343
+ * [#50](https://github.com/serradura/kind/pull/50) - Add `Kind::<Type>.empty_or` as an alias of `Kind::<Type>.value_or_empty`
344
+
345
+ * [#46](https://github.com/serradura/kind/pull/46), [#51](https://github.com/serradura/kind/pull/51) - Add `Kind::Functional::Action`.
346
+
347
+ * [#51](https://github.com/serradura/kind/pull/51) - Add `Kind::Action`.
348
+
349
+ * [#51](https://github.com/serradura/kind/pull/51) - Add `Kind::Maybe::ImmutableAttributes`.
350
+
351
+ * [#51](https://github.com/serradura/kind/pull/51) - Add `Kind::Maybe::Methods`.
352
+
353
+ * [#51](https://github.com/serradura/kind/pull/51) - Add `Kind::Enum`.
354
+
355
+ * [#51](https://github.com/serradura/kind/pull/51) - Modularize all the kind components. So now you can require only the modules/features that do you want, `kind/action` is the minimal requirement for all of them.
356
+ * `kind/action`
357
+ * `kind/dig`
358
+ * `kind/either`
359
+ * `kind/empty`
360
+ * `kind/enum`
361
+ * `kind/function`
362
+ * `kind/functional`
363
+ * `kind/functional/action`
364
+ * `kind/immutable_attributes`
365
+ * `kind/maybe`
366
+ * `kind/objects`
367
+ * `kind/presence`
368
+ * `kind/result`
369
+ * `kind/try`
370
+ * `kind/validator`
371
+
372
+ * [#52](https://github.com/serradura/kind/pull/52) - Improve `Kind::Validator`, now will be possible to make use of `lambdas` or objects that responds to `.===` and `.name` to perform a kind validation. e.g.
373
+ ```ruby
374
+ class User
375
+ include ActiveModel::Validations
376
+
377
+ attr_reader :name, :bool
378
+
379
+ FilledString = ->(value) { value.kind_of?(String) && !value.empty? }
380
+
381
+ Bool = Object.new
382
+ def Bool.===(value)
383
+ value == true || value == false
384
+ end
385
+ def Bool.name; 'Bool'; end
386
+
387
+ validates :name, kind: FilledString
388
+ validates :bool, kind: Bool
389
+
390
+ def initialize(name:, bool:)
391
+ @name, @bool = name, bool
392
+ end
393
+ end
394
+
395
+ user = User.new(name: '', bool: 1)
396
+
397
+ user.valid? # true
398
+ user.errors[:name] # ['invalid kind']
399
+ user.errors[:bool] # ['must be a kind of Bool']
400
+
401
+ User.new(name: 'Serradura', bool: true).valid? # true
402
+ ```
403
+
404
+ * [#52](https://github.com/serradura/kind/pull/52) - Add `Kind::Enum.===`.
405
+
406
+ ### Deprecated
407
+
408
+ * [#47](https://github.com/serradura/kind/pull/47) - Deprecate `Kind.is` and `Kind::Of()`.
409
+
410
+ * [#48](https://github.com/serradura/kind/pull/48) - Deprecate `Kind::Maybe()` and `Kind::Optional()`
411
+
412
+ ### Changes
413
+
414
+ * [#48](https://github.com/serradura/kind/pull/48) - Rename `Kind::TypeChecker` to `Kind::Object` and `Kind::TypeChecker::Object` to `Kind::Object::Instance`.
415
+
416
+ [⬆️ &nbsp;Back to Top](#changelog-)
417
+
418
+ 5.1.0 (2021-02-23)
419
+ ------------------
420
+
421
+ ### Added
422
+
423
+ * [#45](https://github.com/serradura/kind/pull/45) - Add support to Ruby `>= 2.1.0`.
424
+
425
+ ### Deprecated
426
+
427
+ * [#45](https://github.com/serradura/kind/pull/45) - `kind/active_model/validation` is deprecated; use `kind/validator` instead.
428
+
429
+ 5.0.0 (2021-02-22)
430
+ ------------------
431
+
432
+ ### Breaking Changes
433
+
434
+ * [#44](https://github.com/serradura/kind/pull/44) - Now, do you need to require `"kind/empty/constant"` to define the constant `Empty` as a `Kind::Empty` alias.
435
+
436
+ ### Removed
437
+
438
+ * [#44](https://github.com/serradura/kind/pull/44) - Remove `Kind::Is.call`
439
+
440
+ * [#44](https://github.com/serradura/kind/pull/44) - Remove `Kind::Of.call`
441
+
442
+ * [#44](https://github.com/serradura/kind/pull/44) - Remove `Kind::Types.add`.
443
+
444
+ * [#44](https://github.com/serradura/kind/pull/44) - Remove `Kind::Of::<Type>` and `Kind::Is::<Type>` modules.
445
+
446
+ * [#44](https://github.com/serradura/kind/pull/44) - Remove `Kind::Checker`, `Kind::Checker::Protocol`, `Kind::Checker::Factory`.
447
+
448
+ * [#44](https://github.com/serradura/kind/pull/44) - Remove the invocation of `Kind.is` without arguments.
449
+
450
+ * [#44](https://github.com/serradura/kind/pull/44) - Remove the invocation of `Kind.of` without arguments.
451
+
452
+ * [#44](https://github.com/serradura/kind/pull/44) - Remove the invocation of `Kind.of` with a single argument (the kind).
453
+
454
+ 4.1.0 (2021-02-22)
455
+ ------------------
456
+
457
+ ### Added
458
+
459
+ * [#43](https://github.com/serradura/kind/pull/43) - Make `Kind::Maybe::Typed` verify the kind of the value via `===`, because of this, now is possible to use type checkers in typed Maybes.
460
+ ```ruby
461
+ Kind::Maybe(Kind::Boolean).wrap(nil).value_or(false) # false
462
+
463
+ Kind::Maybe(Kind::Boolean).wrap(true).value_or(false) # true
464
+ ```
465
+
466
+ * [#43](https://github.com/serradura/kind/pull/43) - Add `Kind::<Type>.maybe` and `Kind::<Type>.optional`. This method returns a typed Maybe with the expected kind when it is invoked without arguments. But, if it receives arguments, it will behave like the `Kind::Maybe.wrap` method. e.g.
467
+ ```ruby
468
+ Kind::Integer.maybe #<Kind::Maybe::Typed:0x0000... @kind=Kind::Integer>
469
+
470
+ Kind::Integer.maybe(0).some? # true
471
+ Kind::Integer.maybe { 1 }.some? # true
472
+ Kind::Integer.maybe(2) { |n| n * 2 }.some? # true
473
+
474
+ Kind::Integer.maybe { 2 / 0 }.none? # true
475
+ Kind::Integer.maybe(2) { |n| n / 0 }.none? # true
476
+ Kind::Integer.maybe('2') { |n| n * n }.none? # true
477
+ ```
478
+
479
+ * [#43](https://github.com/serradura/kind/pull/43) - Make the `:respond_to` kind validation verify by one or multiple methods. e.g.
480
+ ```ruby
481
+ validates :params, kind: { respond_to: [:[], :values_at] }
482
+ ```
483
+
484
+ * [#43](https://github.com/serradura/kind/pull/43) - Make the `:of` kind validation verify the expected value kind using `===`, because of this, now is possible to use type checkers as expected kinds. e.g.
485
+ ```ruby
486
+ validates :alive, kind: Kind::Boolean
487
+ ```
488
+
489
+ 4.0.0 (2021-02-22)
490
+ ------------------
491
+
492
+ ### Added
493
+
494
+ * [#40](https://github.com/serradura/kind/pull/40) - Add `Kind.of_class?` to verify if a given value is a `Class`.
495
+
496
+ * [#40](https://github.com/serradura/kind/pull/40) - Add `Kind.of_module?` to verify if a given value is a `Module`.
497
+
498
+ * [#40](https://github.com/serradura/kind/pull/40) - Add `Kind.of_module_or_class()` that returns the given value if it is a module or a class. If not, a `Kind::Error` will be raised.
499
+ ```ruby
500
+ Kind.of_module_or_class(String) # String
501
+ Kind.of_module_or_class(1) # Kind::Error (1 expected to be a kind of Module/Class)
502
+ ```
503
+
504
+ * [#40](https://github.com/serradura/kind/pull/40) - Add `Kind.respond_to(object, *method_names)`, this method returns the given object if it responds to all of the method names. But if the object does not respond to some of the expected methods, an error will be raised.
505
+ ```ruby
506
+ Kind.respond_to('', :upcase) # ""
507
+ Kind.respond_to('', :upcase, :strip) # ""
508
+
509
+ Kind.respond_to(1, :upcase) # expected 1 to respond to :upcase
510
+ Kind.respond_to(2, :to_s, :upcase) # expected 2 to respond to :upcase
511
+ ```
512
+
513
+ * [#40](https://github.com/serradura/kind/pull/40) - Add `Kind::Try.call()`. This method invokes a public method with or without arguments like public_send does, except that if the receiver does not respond to it the call returns `nil` rather than raising an exception.
514
+ ```ruby
515
+ Kind::Try.(' foo ', :strip) # "foo"
516
+ Kind::Try.({a: 1}, :[], :a) # 1
517
+ Kind::Try.({a: 1}, :[], :b) # nil
518
+ Kind::Try.({a: 1}, :fetch, :b, 2) # 2
519
+
520
+ Kind::Try.(:symbol, :strip) # nil
521
+ Kind::Try.(:symbol, :fetch, :b, 2) # nil
522
+
523
+ # It raises an exception if the method name isn't a string or a symbol
524
+ Kind::Try.({a: 1}, 1, :a) # TypeError (1 is not a symbol nor a string)
525
+ ```
526
+
527
+ * [#40](https://github.com/serradura/kind/pull/40), [#41](https://github.com/serradura/kind/pull/40) - Add `Kind::DEPRECATION` module to be used to warn about all of the deprecations. You can use the `DISABLE_KIND_DEPRECATION` environment variable to disable the warning messages.
528
+
529
+ * [#40](https://github.com/serradura/kind/pull/40), [#41](https://github.com/serradura/kind/pull/40) - Add type checkers modules that have several utility methods related to type checking/handling.
530
+ ```ruby
531
+ # All of the methods used with the Kind::String can be used with any other type checker module.
532
+
533
+ # Kind::<Type>.name
534
+ # Kind::<Type>.kind
535
+ # The type checker can return its kind and its name
536
+ Kind::String.name # "String"
537
+ Kind::String.kind # ::String
538
+
539
+ # Kind::<Type>.===
540
+ # Can check if a given value is an instance of its kind.
541
+ Kind::String === 'foo' # true
542
+ Kind::String === :foo # false
543
+
544
+ # Kind::<Type>.value?(value)
545
+ # Can check if a given value is an instance of its kind.
546
+ Kind::String.value?('foo') # true
547
+ Kind::String.value?(:foo) # false
548
+
549
+ # If it doesn't receive an argument, a lambda will be returned and it will know how to do the type verification.
550
+ [1, 2, 'foo', 3, 'Bar'].select?(&Kind::String.value?) # ["foo", "bar"]
551
+
552
+ # Kind::<Type>.or_nil(value)
553
+ # Can return nil if the given value isn't an instance of its kind
554
+ Kind::String.or_nil('foo') # "foo"
555
+ Kind::String.or_nil(:foo) # nil
556
+
557
+ # Kind::<Type>.or_undefined(value)
558
+ # Can return Kind::Undefined if the given value isn't an instance of its kind
559
+ Kind::String.or_undefined('foo') # "foo"
560
+ Kind::String.or_undefined(:foo) # Kind::Undefined
561
+
562
+ # Kind::<Type>.or(fallback, value)
563
+ # Can return a fallback if the given value isn't an instance of its kind
564
+ Kind::String.or(nil, 'foo') # "foo"
565
+ Kind::String.or(nil, :foo) # nil
566
+
567
+ # If it doesn't receive a second argument (the value), it will return a callable that knows how to expose an instance of the expected type or a fallback if the given value is wrong.
568
+ [1, 2, 'foo', 3, 'Bar'].map(&Kind::String.or('')) # ["", "", "foo", "", "Bar"]
569
+ [1, 2, 'foo', 3, 'Bar'].map(&Kind::String.or(nil)) # [nil, nil, "foo", nil, "Bar"]
570
+
571
+ # An error will be raised if the fallback didn't have the expected kind or if not nil / Kind::Undefined.
572
+ [1, 2, 'foo', 3, 'Bar'].map(&Kind::String.or(:foo)) # Kind::Error (:foo expected to be a kind of String)
573
+
574
+ # Kind::<Type>[value]
575
+ # Will raise Kind::Error if the given value isn't an instance of the expected kind
576
+ Kind::String['foo'] # "foo"
577
+ Kind::String[:foo ] # Kind::Error (:foo expected to be a kind of String)
578
+ ```
579
+ * List of all type checkers:
580
+ * **Core:**
581
+ * `Kind::Array`
582
+ * `Kind::Class`
583
+ * `Kind::Comparable`
584
+ * `Kind::Enumerable`
585
+ * `Kind::Enumerator`
586
+ * `Kind::File`
587
+ * `Kind::Float`
588
+ * `Kind::Hash`
589
+ * `Kind::Integer`
590
+ * `Kind::IO`
591
+ * `Kind::Method`
592
+ * `Kind::Module`
593
+ * `Kind::Numeric`
594
+ * `Kind::Proc`
595
+ * `Kind::Queue`
596
+ * `Kind::Range`
597
+ * `Kind::Regexp`
598
+ * `Kind::String`
599
+ * `Kind::Struct`
600
+ * `Kind::Symbol`
601
+ * `Kind::Time`
602
+ * **Custom:**
603
+ * `Kind::Boolean`
604
+ * `Kind::Callable`
605
+ * `Kind::Lambda`
606
+ * **Stdlib:**
607
+ * `Kind::OpenStruct`
608
+ * `Kind::Set`
609
+
610
+ * [#40](https://github.com/serradura/kind/pull/40) - Add `Kind::Of()`. This method allows the creation of type checkers in runtime. To do this, the kind must respond to the method `.===`, and if doesn't have the `.name` method (which needs to return a string), a hash must be provided with a filled `:name` property.
611
+ ```ruby
612
+ # Example using a class (an object which responds to .=== and has the .name method):
613
+ # This object will have all of the default methods that a standard type checker (e.g: Kind::String) has.
614
+ kind_of_string = Kind::Of(String)
615
+
616
+ kind_of_string[''] # ""
617
+ kind_of_string[{}] # Kind::Error ({} expected to be a kind of String)
618
+
619
+ # Example using a lambda (an object which responds to .===) and a hash with the kind name.
620
+
621
+ PositiveInteger = Kind::Of(-> value { value.kind_of?(Integer) && value > 0 }, name: 'PositiveInteger')
622
+
623
+ # PositiveInteger.name
624
+ # PositiveInteger.kind
625
+ # The type checker can return its kind and its name
626
+ PositiveInteger.name # "PositiveInteger"
627
+ PositiveInteger.kind # #<Proc:0x0000.... >
628
+
629
+ # PositiveInteger.===
630
+ # Can check if a given value is an instance of its kind.
631
+ PositiveInteger === 1 # true
632
+ PositiveInteger === 0 # false
633
+
634
+ # PositiveInteger.value?(value)
635
+ # Can check if a given value is an instance of its kind.
636
+ PositiveInteger.value?(1) # true
637
+ PositiveInteger.value?(-1) # false
638
+
639
+ # If it doesn't receive an argument, a lambda will be returned and it will know how to do the type verification.
640
+ [1, 2, 0, 3, -1].select?(&PositiveInteger.value?) # [1, 2, 3]
641
+
642
+ # PositiveInteger.or_nil(value)
643
+ # Can return nil if the given value isn't an instance of its kind
644
+ PositiveInteger.or_nil(1) # 1
645
+ PositiveInteger.or_nil(0) # nil
646
+
647
+ # PositiveInteger.or_undefined(value)
648
+ # Can return Kind::Undefined if the given value isn't an instance of its kind
649
+ PositiveInteger.or_undefined(2) # 2
650
+ PositiveInteger.or_undefined(-1) # Kind::Undefined
651
+
652
+ # PositiveInteger.or(fallback, value)
653
+ # Can return a fallback if the given value isn't an instance of its kind
654
+ PositiveInteger.or(nil, 1) # 1
655
+ PositiveInteger.or(nil, 0) # nil
656
+
657
+ # If it doesn't receive a second argument (the value), it will return a callable that knows how to expose an instance of the expected type or a fallback if the given value is wrong.
658
+ [1, 2, 0, 3, -1].map(&PositiveInteger.or(1)) # [1, 2, 1, 3, 1]
659
+ [1, 2, 0, 3, -1].map(&PositiveInteger.or(nil)) # [1, 2, nil, 3, nil]
660
+
661
+ # An error will be raised if the fallback didn't have the expected kind or if not nil / Kind::Undefined.
662
+ [1, 2, 0, 3, -1].map(&PositiveInteger.or(:foo)) # Kind::Error (:foo expected to be a kind of PositiveInteger)
663
+
664
+ # PositiveInteger[value]
665
+ # Will raise Kind::Error if the given value isn't an instance of the expected kind
666
+ PositiveInteger[1] # 1
667
+ PositiveInteger[:foo] # Kind::Error (:foo expected to be a kind of PositiveInteger)
668
+ ```
669
+ * [#40](https://github.com/serradura/kind/pull/40), [#41](https://github.com/serradura/kind/pull/40) - Add type checkers methods.
670
+ ```ruby
671
+ # All of the methods used with the Kind::String? can be used with any other type checker method.
672
+
673
+ # Kind::<Type>?(*values)
674
+ # Can check if a given value (one or many) is an instance of its kind.
675
+ Kind::String?('foo') # true
676
+ Kind::String?('foo', 'bar') # true
677
+ Kind::String?('foo', :bar) # false
678
+
679
+ # If it doesn't receive an argument, a lambda will be returned and it will know how to do the type verification.
680
+ [1, 2, 'foo', 3, 'Bar'].select?(&Kind::String?) # ["foo", "bar"]
681
+ ```
682
+ * List of all type checkers:
683
+ * **Core:**
684
+ * `Kind::Array?`
685
+ * `Kind::Class?`
686
+ * `Kind::Comparable?`
687
+ * `Kind::Enumerable?`
688
+ * `Kind::Enumerator?`
689
+ * `Kind::File?`
690
+ * `Kind::Float?`
691
+ * `Kind::Hash?`
692
+ * `Kind::Integer?`
693
+ * `Kind::IO?`
694
+ * `Kind::Method?`
695
+ * `Kind::Module?`
696
+ * `Kind::Numeric?`
697
+ * `Kind::Proc?`
698
+ * `Kind::Queue?`
699
+ * `Kind::Range?`
700
+ * `Kind::Regexp?`
701
+ * `Kind::String?`
702
+ * `Kind::Struct?`
703
+ * `Kind::Symbol?`
704
+ * `Kind::Time?`
705
+ * **Custom:**
706
+ * `Kind::Boolean?`
707
+ * `Kind::Callable?`
708
+ * `Kind::Lambda?`
709
+ * **Stdlib:**
710
+ * `Kind::OpenStruct?`
711
+ * `Kind::Set?`
712
+
713
+ * [#41](https://github.com/serradura/kind/pull/41) - Make `Kind::Dig.call` extract values from regular objects.
714
+ ```ruby
715
+ class Person
716
+ attr_reader :name
717
+
718
+ def initialize(name)
719
+ @name = name
720
+ end
721
+ end
722
+
723
+ person = Person.new('Rodrigo')
724
+
725
+ Kind::Dig.(person, [:name]) # "Rodrigo"
726
+
727
+ Kind::Dig.({people: [person]}, [:people, 0, :name]) # "Rodrigo"
728
+ ```
729
+
730
+ * [#41](https://github.com/serradura/kind/pull/41) - Add `Kind::Presence.call`. Returns the given value if it's present otherwise it will return `nil`.
731
+ ```ruby
732
+ Kind::Presence.(true) # true
733
+ Kind::Presence.('foo') # "foo"
734
+ Kind::Presence.([1, 2]) # [1, 2]
735
+ Kind::Presence.({a: 3}) # {a: 3}
736
+ Kind::Presence.(Set.new([4])) # #<Set: {4}>
737
+
738
+ Kind::Presence.('') # nil
739
+ Kind::Presence.(' ') # nil
740
+ Kind::Presence.("\t\n\r") # nil
741
+ Kind::Presence.("\u00a0") # nil
742
+
743
+ Kind::Presence.([]) # nil
744
+ Kind::Presence.({}) # nil
745
+ Kind::Presence.(Set.new) # nil
746
+
747
+ Kind::Presence.(nil) # nil
748
+ Kind::Presence.(false) # nil
749
+
750
+ # nil will be returned if the given object responds to the method blank? and this method result is true.
751
+ MyObject = Struct.new(:is_blank) do
752
+ def blank?
753
+ is_blank
754
+ end
755
+ end
756
+
757
+ my_object = MyObject.new
758
+
759
+ my_object.is_blank = true
760
+
761
+ Kind::Presence.(my_object) # nil
762
+
763
+ my_object.is_blank = false
764
+
765
+ Kind::Presence.(my_object) # #<struct MyObject is_blank=false>
766
+ ```
767
+
768
+ * [#41](https://github.com/serradura/kind/pull/41) - Add `Kind::Maybe#presence`, this method will return None if the wrapped value wasn't present.
769
+ ```ruby
770
+ result1 = Kind::Maybe(Hash).wrap(foo: '').dig(:foo).presence
771
+ result1.none? # true
772
+ result1.value # nil
773
+
774
+ result2 = Kind::Maybe(Hash).wrap(foo: 'bar').dig(:foo).presence
775
+ result2.none? # false
776
+ result2.value # "bar"
777
+ ```
778
+
779
+ * [#41](https://github.com/serradura/kind/pull/41) - Make `Kind::Maybe#wrap` receive a block and intercept StandardError exceptions. And a None will be returned if some exception happening.
780
+ ```ruby
781
+ Kind::Maybe.wrap { 2 / 0 } # #<Kind::Maybe::None:0x0000... @value=#<ZeroDivisionError: divided by 0>>
782
+
783
+ Kind::Maybe(Numeric).wrap(2) { |number| number / 0 } # #<Kind::Maybe::None:0x0000... @value=#<ZeroDivisionError: divided by 0>>
784
+ ```
785
+
786
+ * [#41](https://github.com/serradura/kind/pull/41) - Make `Kind::Maybe#map` intercept StandardError exceptions.
787
+ * Now the `#map` and `#then` methods will intercept any StandardError and return None with the exception or their values.
788
+ * Add `#map!` and `#then!` that allows the exception leak, so, the user must handle the exception by himself or use this method when he wants to see the error be raised.
789
+ * If an exception (StandardError) is returned by the methods `#then`, `#map` it will be resolved as None.
790
+ ```ruby
791
+ # Handling StandardError exceptions
792
+ result1 = Kind::Maybe[2].map { |number| number / 0 }
793
+ result1.none? # true
794
+ result1.value # #<ZeroDivisionError: divided by 0>
795
+
796
+ result2 = Kind::Maybe[3].then { |number| number / 0 }
797
+ result2.none? # true
798
+ result2.value # #<ZeroDivisionError: divided by 0>
799
+
800
+ # Leaking StandardError exceptions
801
+ Kind::Maybe[2].map! { |number| number / 0 } # ZeroDivisionError (divided by 0)
802
+
803
+ Kind::Maybe[2].then! { |number| number / 0 } # ZeroDivisionError (divided by 0)
804
+ ```
805
+
806
+ * [#41](https://github.com/serradura/kind/pull/41) - Add `Kind::TypeCheckers#value`. This method ensures that you will have a value of the expected kind. But, in the case of the given value be invalid, this method will require a default value (with the expected kind) to be returned.
807
+ ```ruby
808
+ # Using built-in type checkers
809
+ Kind::String.value(1, default: '') # ""
810
+ Kind::String.value('1', default: '') # "1"
811
+
812
+ Kind::String.value('1', default: 1) # Kind::Error (1 expected to be a kind of String)
813
+
814
+ # Using custom type checkers
815
+ PositiveInteger = Kind::Of(-> value { value.kind_of?(Integer) && value > 0 }, name: 'PositiveInteger')
816
+
817
+ PositiveInteger.value(0, default: 1) # 1
818
+ PositiveInteger.value(2, default: 1) # 2
819
+
820
+ PositiveInteger.value(-1, default: 0) # Kind::Error (0 expected to be a kind of PositiveInteger)
821
+ ```
822
+
823
+ * [#41](https://github.com/serradura/kind/pull/41) - Add the method `value_or_empty` for some type checkers. This method is available for some type checkers (`Kind::Array`, `Kind::Hash`, `Kind::String`, `Kind::Set`), and it will return an empty frozen value if the given value hasn't the expected kind.
824
+ ```ruby
825
+ Kind::Array.value_or_empty({}) # []
826
+ Kind::Array.value_or_empty({}).frozen? # true
827
+ ```
828
+
829
+ * [#42](https://github.com/serradura/kind/pull/42) - Add the method `Kind.value`. This method ensures that you will have a value of the expected kind. But, in the case of the given value be invalid, this method will require a default value (with the expected kind) to be returned.
830
+ ```ruby
831
+ Kind.value(String, '1', default: '') # "1"
832
+
833
+ Kind.value(String, 1, default: '') # ""
834
+
835
+ Kind.value(String, 1, default: 2) # Kind::Error (2 expected to be a kind of String)
836
+ ```
837
+
838
+ * [#42](https://github.com/serradura/kind/pull/42) - Add `Kind::Presence.to_proc`. This method allow you to make use of the `Kind::Presence` in methods that receive a block as an argument. e.g:
839
+ ```ruby
840
+ ['', [], {}, '1', [2]].map(&Kind::Presence) # [nil, nil, nil, "1", [2]]
841
+ ```
842
+
843
+ * [#42](https://github.com/serradura/kind/pull/42) - `Kind::Maybe(<Type>).{new,[],wrap}` Now, these methods know how to get the value of another Maybe monad.
844
+ ```ruby
845
+ some_number = Kind::Some(2)
846
+
847
+ Kind::Maybe(Numeric)[some_number] # #<Kind::Maybe::Some:0x0000... @value=2>
848
+
849
+ Kind::Maybe(Numeric).new(some_number) # #<Kind::Maybe::Some:0x0000... @value=2>
850
+
851
+ Kind::Maybe(Numeric).wrap(some_number) # #<Kind::Maybe::Some:0x0000... @value=2>
852
+
853
+ Kind::Maybe(Numeric).wrap { some_number } # #<Kind::Maybe::Some:0x0000... @value=2>
854
+
855
+ Kind::Maybe(Numeric).wrap(some_number) { |number| number / 2 } # #<Kind::Maybe::Some:0x0000... @value=1>
856
+ ```
857
+
858
+ * [#42](https://github.com/serradura/kind/pull/42) - `Kind::Maybe(<Type>).wrap(arg) { |arg_value| }` if the block receives an argument, the typed Maybe monad will verify if the argument is from the expected kind.
859
+ ```ruby
860
+ Kind::Maybe(Numeric).wrap('2') { |number| number / 0 } # #<Kind::Maybe::None:0x0000... @value=nil>
861
+
862
+ Kind::Maybe(Numeric).wrap(2) { |number| number / 0 } # #<Kind::Maybe::None:0x0000... @value=#<ZeroDivisionError: divided by 0>>
863
+ ```
864
+
865
+ * [#42](https://github.com/serradura/kind/pull/42) - Add `Kind::Maybe#check`. This method returns the current Some after verifies if the block output was truthy.
866
+ ```ruby
867
+ person_name = ->(params) do
868
+ Kind::Maybe(Hash)
869
+ .wrap(params)
870
+ .then { |hash| hash.values_at(:first_name, :last_name) }
871
+ .then { |names| names.map(&Kind::Presence).tap(&:compact!) }
872
+ .check { |names| names.size == 2 }
873
+ .then { |(first_name, last_name)| "#{first_name} #{last_name}" }
874
+ .value_or { 'John Doe' }
875
+ end
876
+
877
+ person_name.('') # "John Doe"
878
+ person_name.(nil) # "John Doe"
879
+ person_name.(last_name: 'Serradura') # "John Doe"
880
+ person_name.(first_name: 'Rodrigo') # "John Doe"
881
+
882
+ person_name.(first_name: 'Rodrigo', last_name: 'Serradura') # "Rodrigo Serradura"
883
+ ```
884
+
885
+ * [#42](https://github.com/serradura/kind/pull/42) - Add `Kind::Dig[]`. This method knows how to create a lambda that will know how to perform the dig strategy.
886
+ ```ruby
887
+ results = [
888
+ { person: {} },
889
+ { person: { name: 'Foo Bar'} },
890
+ { person: { name: 'Rodrigo Serradura'} },
891
+ ].map(&Kind::Dig[:person, :name])
892
+
893
+ p results # [nil, "Foo Bar", "Rodrigo Serradura"],
894
+ ```
895
+
896
+ * [#42](https://github.com/serradura/kind/pull/42) - Add `Kind::Try[]`. This method knows how to create a lambda that will know how to perform the try strategy.
897
+ ```ruby
898
+ results =
899
+ [
900
+ {},
901
+ {name: 'Foo Bar'},
902
+ {name: 'Rodrigo Serradura'},
903
+ ].map(&Kind::Try[:fetch, :name, 'John Doe'])
904
+
905
+ p results # ["John Doe", "Foo Bar", "Rodrigo Serradura"]
906
+ ```
907
+
908
+ ### Deprecated
909
+
910
+ * [#40](https://github.com/serradura/kind/pull/40) - Deprecate `Kind::Is.call`
911
+
912
+ * [#40](https://github.com/serradura/kind/pull/40) - Deprecate `Kind::Of.call`
913
+
914
+ * [#40](https://github.com/serradura/kind/pull/40) - Deprecate `Kind::Types.add`.
915
+
916
+ * [#40](https://github.com/serradura/kind/pull/40) - Deprecate `Kind::Of::<Type>` and `Kind::Is::<Type>` modules.
917
+
918
+ * [#40](https://github.com/serradura/kind/pull/40) - Deprecate `Kind::Checker`, `Kind::Checker::Protocol`, `Kind::Checker::Factory`.
919
+
920
+ * [#40](https://github.com/serradura/kind/pull/40) - Deprecate the invocation of `Kind.is` without arguments.
921
+
922
+ * [#40](https://github.com/serradura/kind/pull/40) - Deprecate the invocation of `Kind.of` without arguments.
923
+
924
+ * [#40](https://github.com/serradura/kind/pull/40) - Deprecate the invocation of `Kind.of` with a single argument (the kind).
925
+
926
+ ### Fixed
927
+
928
+ * [#40](https://github.com/serradura/kind/pull/40) - Make `Kind::Maybe.try!()` raises an error if it was called without a block or arguments.
929
+
930
+ [⬆️ &nbsp;Back to Top](#changelog-)
931
+
932
+ 3.1.0 (2020-07-08)
933
+ ------------------
934
+
935
+ ### Added
936
+
937
+ * [#33](https://github.com/serradura/kind/pull/33) - Add new type checker.
938
+ - `Kind::Of::OpenStruct`, `Kind::Is::OpenStruct`
939
+
940
+ * [#33](https://github.com/serradura/kind/pull/33) - Add `Kind::Maybe::Result#dig`. It extracts the nested value in a sequence of objects, if any step returns `nil` the operation will stop and `None` will be returned, otherwise a `Some` will be returned with the final value.
941
+ ```ruby
942
+ class User
943
+ def self.find_by(id:)
944
+ return :user_1 if id == 1
945
+ end
946
+ end
947
+
948
+ Kind::Optional(Hash).wrap(user: { id: 2 }).dig(:user).value # {:id=>2}
949
+
950
+ # --
951
+
952
+ user_id = Kind::Optional(Hash).wrap(user: { id: 1 }).dig(:user, :id)
953
+
954
+ user_id.then { |id| User.find_by(id: id) }.value # :user_id
955
+ ```
956
+
957
+ * [#33](https://github.com/serradura/kind/pull/33) - Add the utility module `Kind::Dig`. It 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 step can't be digged.
958
+ ```ruby
959
+ s = Struct.new(:a, :b).new(101, 102)
960
+ o = OpenStruct.new(c: 103, d: 104)
961
+ d = { struct: s, ostruct: o, data: [s, o]}
962
+
963
+ Kind::Dig.(s, [:a]) # 101
964
+ Kind::Dig.(o, [:c]) # 103
965
+
966
+ Kind::Dig.(d, [:struct, :b]) # 102
967
+ Kind::Dig.(d, [:data, 0, :b]) # 102
968
+ Kind::Dig.(d, [:data, 0, 'b']) # 102
969
+
970
+ Kind::Dig.(d, [:ostruct, :d]) # 104
971
+ Kind::Dig.(d, [:data, 1, :d]) # 104
972
+ Kind::Dig.(d, [:data, 1, 'd']) # 104
973
+
974
+ Kind::Dig.(d, [:struct, :f]) # nil
975
+ Kind::Dig.(d, [:ostruct, :f]) # nil
976
+ Kind::Dig.(d, [:data, 0, :f]) # nil
977
+ Kind::Dig.(d, [:data, 1, :f]) # nil
978
+ ```
979
+
980
+ [⬆️ &nbsp;Back to Top](#changelog-)
981
+
982
+ 3.0.0 (2020-06-25)
983
+ ------------------
984
+
985
+ ### Breaking Changes
986
+
987
+ * [#31](https://github.com/serradura/kind/pull/31) - Change the `Kind::Maybe::Result#try()` behavior.
988
+ - If you don't want to use the methods `#map`/`#then` to access some value inside of the monad, you could use the `#try` method to do this. It invokes a public method with or without arguments like public_send does, except that if the receiver does not respond to it the call returns `nil` rather than raising an exception. So, if the value wasn't `nil` or `Kind::Undefined` a `Some` will be returned.
989
+ ```ruby
990
+ # Examples using Kind::Maybe
991
+
992
+ Kind::Maybe['foo'].try(:upcase).value # "FOO"
993
+
994
+ Kind::Maybe[{}].try(:fetch, :number, 0).value # 0
995
+
996
+ Kind::Maybe[{number: 1}].try(:fetch, :number).value # 1
997
+
998
+ Kind::Maybe[' foo '].try { |value| value.strip }.value # "foo"
999
+
1000
+ # Examples using Kind::Optional
1001
+
1002
+ Kind::Optional[1].try(:strip).value_or('') # ""
1003
+
1004
+ Kind::Optional[' Rodrigo '].try(:strip).value_or("") # 'Rodrigo'
1005
+ ```
1006
+
1007
+ ### Added
1008
+
1009
+ * [#31](https://github.com/serradura/kind/pull/31) - Add `Kind::Maybe::Result#try!()` that have almost of the same behavior of `Kind::Maybe::Result#try()`, the difference is because it will raise an error when the monad value doesn't respond to the expected method.
1010
+ ```ruby
1011
+ Kind::Maybe[{}].try(:upcase) # => #<Kind::Maybe::None:0x0000... @value=nil>
1012
+
1013
+ Kind::Maybe[{}].try!(:upcase) # => NoMethodError (undefined method `upcase' for {}:Hash)
1014
+ ```
1015
+
1016
+ [⬆️ &nbsp;Back to Top](#changelog-)
1017
+
1018
+ 2.3.0 (2020-06-24)
1019
+ ------------------
1020
+
1021
+ ### Added
1022
+
1023
+ * [#30](https://github.com/serradura/kind/pull/30) - Add `Kind::Maybe.wrap()` as an alias for `Kind::Maybe.new()`.
1024
+
1025
+ * [#30](https://github.com/serradura/kind/pull/30) - Add `Kind::Maybe::Typed` and the methods `Kind::Maybe()`, `Kind::Optional()` to create typed monads. It will return `Some` if the given value has the expected kind or `None` if it hasn't.
1026
+ ```ruby
1027
+ Double = ->(arg) do
1028
+ Kind::Optional(Numeric)
1029
+ .wrap(arg)
1030
+ .then { |number| number * 2 }
1031
+ end
1032
+ ```
1033
+
1034
+ [⬆️ &nbsp;Back to Top](#changelog-)
1035
+
1036
+ 2.2.0 (2020-06-23)
1037
+ ------------------
1038
+
1039
+ ### Added
1040
+
1041
+ * [#29](https://github.com/serradura/kind/pull/29) - Improve the comparison of values with `Kind::Undefined`.
1042
+ - I've been using `Kind.of(ActiveRecord::Relation, some_ar_relation)` in one of my apps and I found an unexpected behavior, the relations were always been executed. The reason is `ActiveRecord::AssociationRelation#==` behavior, which always performs a query (`to_a`). So, to avoid this and other unexpected behaviors, I decided to invert the comparison with `Kind::Undefined` to ensure a regular equality checking.
1043
+
1044
+ [⬆️ &nbsp;Back to Top](#changelog-)
1045
+
1046
+ 2.1.0 (2020-05-12)
1047
+ ------------------
1048
+
1049
+ ### Added
1050
+
1051
+ * [#28](https://github.com/serradura/kind/pull/28) - Allow passing multiple arguments to `Kind.of.<Type>.instance?(*args)`
1052
+
1053
+ * [#28](https://github.com/serradura/kind/pull/28) - Allow passing multiple arguments to `Kind.of.<Type>?(*args)`
1054
+
1055
+ * [#28](https://github.com/serradura/kind/pull/28) - Add `Kind::Some()` and `Kind::None()`. e.g:
1056
+ ```ruby
1057
+ Double = ->(arg) do
1058
+ number = Kind::Of::Numeric.or_nil(arg)
1059
+
1060
+ Kind::Optional[number].then { |number| number * 2 }
1061
+ end
1062
+
1063
+ Add = -> params do
1064
+ a, b = Kind::Of::Hash(params, or: Empty::HASH).values_at(:a, :b)
1065
+
1066
+ return Kind::None unless Kind::Of::Numeric?(a, b)
1067
+
1068
+ Kind::Some(a + b)
1069
+ end
1070
+
1071
+ Kind::Optional[a: 1, b: 2].then(&Add).value_or(0) # 3
1072
+
1073
+ Add.({}).some? # false
1074
+
1075
+ Add.(a: 1, b: '2').some? # false
1076
+
1077
+ Add.(a: 2, b: 2).then(&Double).value # 8
1078
+ ```
1079
+
1080
+ * [#28](https://github.com/serradura/kind/pull/28) - Add `Kind.of?(<Type>, *args)` to check if one or many values are the expected kind.
1081
+ ```ruby
1082
+ Kind.of?(Numeric, 1, 2.0, 3) # true
1083
+ Kind.of?(Numeric, 1, '2', 3) # false
1084
+ ```
1085
+
1086
+ * [#28](https://github.com/serradura/kind/pull/28) - Make the `Kind.of?(<Type>)` returns a lambda that knows how to do the type verification.
1087
+ ```ruby
1088
+ [1, '2', 3].select(&Kind.of?(Numeric)) # [1, 3]
1089
+ ```
1090
+
1091
+ ### Breaking Changes
1092
+
1093
+ * [#28](https://github.com/serradura/kind/pull/28) - Make `Kind.of.<Type>.to_proc` have the same behavior of `Kind.of.<Type>.instance(value)` (returns the value if it has the expected kind or raise `Kind::Error` if it haven't). This change is because now we have a method to verify if the given value is an instance of the expected kind (`Kind.of.<Type>?`), and this new method has a `to_proc` behavior when is called without arguments.
1094
+ ```ruby
1095
+ [1, 2, 3].map(&Kind::Of::Numeric) # [1, 2, 3]
1096
+
1097
+ [1, '2', 3].map(&Kind::Of::Numeric) # Kind::Error ("2" expected to be a kind of Numeric)
1098
+
1099
+ [1, '2', 3].select(&Kind::Of::Numeric?) # [1, 3]
1100
+ ```
1101
+
1102
+ [⬆️ &nbsp;Back to Top](#changelog-)
1103
+
1104
+ 2.0.0 (2020-05-07)
1105
+ ------------------
1106
+
1107
+ ### Added
1108
+
1109
+ * [#24](https://github.com/serradura/kind/pull/24) - Improve the `kind: { is: }` validation to check the inheritance of *classes*/*modules*.
1110
+
1111
+ ### Breaking Changes
1112
+
1113
+ * [#24](https://github.com/serradura/kind/pull/24) - Change the `Kind.{of,is}.Callable` verification. Now, it only verifies if the given object `respond_to?(:call)`.
1114
+
1115
+ ### Removed
1116
+
1117
+ * [#24](https://github.com/serradura/kind/pull/24) - Remove `kind: { is_a: }` from `Kind::Validator`.
1118
+ * [#24](https://github.com/serradura/kind/pull/24) - Remove `kind: { klass: }` from `Kind::Validator`.
1119
+
1120
+ [⬆️ &nbsp;Back to Top](#changelog-)
1121
+
1122
+ 1.9.0 (2020-05-06)
1123
+ ------------------
1124
+
1125
+ ### Added
1126
+
1127
+ * [#23](https://github.com/serradura/kind/pull/23) - Add `Kind.of.<Type>.to_proc` as an alias for `Kind.of.<Type>.instance?`.
1128
+ ```ruby
1129
+ collection = [
1130
+ {number: 1},
1131
+ 'number 0',
1132
+ {number: 2},
1133
+ [0],
1134
+ ]
1135
+
1136
+ collection
1137
+ .select(&Kind.of.Hash)
1138
+ .reduce(0) { |total, item| total + item[:number] }
1139
+ ```
1140
+
1141
+ * [#23](https://github.com/serradura/kind/pull/23) - Add `Kind::Validator` (`ActiveModel` validator) as an alternative (substitute) of [`type_validator`](https://github.com/serradura/type_validator).
1142
+ ```ruby
1143
+ require 'kind/active_model/validation'
1144
+
1145
+ class Person
1146
+ include ActiveModel::Validations
1147
+
1148
+ attr_reader :name, :age
1149
+
1150
+ validates :name, kind: { of: String }
1151
+ validates :age, kind: { of: Integer }
1152
+
1153
+ def initialize(name:, age:)
1154
+ @name, @age = name, age
1155
+ end
1156
+ end
1157
+
1158
+ person = Person.new(name: 'John', age: '21')
1159
+
1160
+ person.valid? # false
1161
+
1162
+ person.errors[:age] # ['must be a kind of: Integer']
1163
+ ```
1164
+
1165
+ [⬆️ &nbsp;Back to Top](#changelog-)
1166
+
1167
+ 1.8.0 (2020-05-03)
1168
+ ------------------
1169
+
1170
+ ### Added
1171
+
1172
+ * [#22](https://github.com/serradura/kind/pull/22) - `Kind.of.<Type>.instance?`returns a lambda when called without an argument.
1173
+ ```ruby
1174
+ collection = [
1175
+ {number: 1},
1176
+ 'number 0',
1177
+ {number: 2},
1178
+ [0],
1179
+ ]
1180
+
1181
+ collection
1182
+ .select(&Kind::Of::Hash.instance?)
1183
+ .reduce(0) { |total, item| total + item[:number] }
1184
+ ```
1185
+
1186
+ * [#22](https://github.com/serradura/kind/pull/22) - Add new methods `.as_optional`, `.as_maybe` in the type checkers.
1187
+ ```ruby
1188
+ def person_name(params)
1189
+ Kind::Of::Hash
1190
+ .as_optional(params)
1191
+ .map { |data| data.values_at(:first_name, :last_name).compact }
1192
+ .map { |first, last| "#{first} #{last}" if first && last }
1193
+ .value_or { 'John Doe' }
1194
+ end
1195
+
1196
+ person_name('') # "John Doe"
1197
+ person_name(nil) # "John Doe"
1198
+ person_name(first_name: 'Rodrigo') # "John Doe"
1199
+ person_name(last_name: 'Serradura') # "John Doe"
1200
+ person_name(first_name: 'Rodrigo', last_name: 'Serradura') # "Rodrigo Serradura"
1201
+
1202
+ # A lambda will be returned if these methods receive only one argument
1203
+
1204
+ collection = [
1205
+ {number: 1},
1206
+ 'number 0',
1207
+ {number: 2},
1208
+ [0],
1209
+ ]
1210
+
1211
+ collection
1212
+ .map(&Kind.of.Hash.as_optional)
1213
+ .select(&:some?)
1214
+ .reduce(0) { |total, item| total + item.value[:number] }
1215
+ ```
1216
+
1217
+ [⬆️ &nbsp;Back to Top](#changelog-)
1218
+
1219
+ 1.7.0 (2020-05-03)
1220
+ ------------------
1221
+
1222
+ ### Fixed
1223
+
1224
+ * [#20](https://github.com/serradura/kind/pull/20) - Fix the verification of modules using `Kind.is()`.
1225
+
1226
+ [⬆️ &nbsp;Back to Top](#changelog-)
1227
+
1228
+ 1.6.0 (2020-04-17)
1229
+ ------------------
1230
+
1231
+ ### Added
1232
+
1233
+ * [#19](https://github.com/serradura/kind/pull/19) - Add aliases to perform the strict type verification (in registered type checkers).
1234
+ ```ruby
1235
+ # Kind.of.<Type>[]
1236
+
1237
+ Kind.of.Hash[nil] # raise Kind::Error, "nil expected to be a kind of Hash"
1238
+ Kind.of.Hash[''] # raise Kind::Error, "'' expected to be a kind of Hash"
1239
+ Kind.of.Hash[a: 1] # {a: 1}
1240
+ Kind.of.Hash['', or: {}] # {}
1241
+
1242
+ # Kind.of.<Type>.instance()
1243
+
1244
+ Kind.of.Array.instance(nil) # raise Kind::Error, "nil expected to be a kind of Array"
1245
+ Kind.of.Array.instance('') # raise Kind::Error, "'' expected to be a kind of Array"
1246
+ Kind.of.Array.instance([]) # []
1247
+ Kind.of.Array.instance('', or: []) # []
1248
+ ```
1249
+
1250
+ * [#19](https://github.com/serradura/kind/pull/19) - Add `.or_undefined` method for any type checker.
1251
+ ```ruby
1252
+ Kind.of.String.or_undefined(nil) # Kind::Undefined
1253
+ Kind.of.String.or_undefined("something") # "something"
1254
+ ```
1255
+
1256
+ * [#19](https://github.com/serradura/kind/pull/19) - Allow a dynamical verification of types.
1257
+ ```ruby
1258
+ class User
1259
+ end
1260
+
1261
+ class AdminUser < User
1262
+ end
1263
+
1264
+ Kind.of(User, User.new) # #<User:0x0000...>
1265
+ Kind.of(User, {}) # Kind::Error ({} expected to be a kind of User)
1266
+ ```
1267
+
1268
+ * [#19](https://github.com/serradura/kind/pull/19) - Allow the creation of type checkers dynamically (without register one).
1269
+ ```ruby
1270
+ class User
1271
+ end
1272
+
1273
+ kind_of_user = Kind.of(User)
1274
+
1275
+ kind_of_user[{}] # Kind::Error ({} expected to be a kind of User)
1276
+ kind_of_user[User.new] # #<User:0x0000...>
1277
+
1278
+ kind_of_user.instance({}) # Kind::Error ({} expected to be a kind of User)
1279
+ kind_of_user.instance(User.new) # #<User:0x0000...>
1280
+
1281
+ kind_of_user.instance?({}) # false
1282
+ kind_of_user.instance?(User.new) # true
1283
+
1284
+ kind_of_user.class?(Hash) # false
1285
+ kind_of_user.class?(User) # true
1286
+
1287
+ kind_of_user.or_undefined({}) # Kind::Undefined
1288
+ kind_of_user.or_undefined(User.new) # #<User:0x0000...>
1289
+ ```
1290
+
1291
+ * [#19](https://github.com/serradura/kind/pull/19) - Add a new type checkers.
1292
+ - `Kind::Of::Set`
1293
+ - `Kind::Of::Maybe`, `Kind::Of::Optional`
1294
+
1295
+ * [#19](https://github.com/serradura/kind/pull/19) - Add `Kind::Empty` with several constants having empty frozen objects.
1296
+ ```ruby
1297
+ Kind::Empty::SET
1298
+ Kind::Empty::HASH
1299
+ Kind::Empty::ARRAY
1300
+ Kind::Empty::STRING
1301
+
1302
+ # If there isn't any constant named as Empty, the gem will use it to create an alias for Kind::Empty.
1303
+
1304
+ Empty::SET == Kind::Empty::SET
1305
+ Empty::HASH == Kind::Empty::HASH
1306
+ Empty::ARRAY == Kind::Empty::ARRAY
1307
+ Empty::STRING == Kind::Empty::STRING
1308
+ ```
1309
+
1310
+ ### Changes
1311
+
1312
+ * [#19](https://github.com/serradura/kind/pull/19) - Change the output of `Kind::Undefined.to_s`, `Kind::Undefined.inspect`, the previous output was `"Undefined"` and the new is `"Kind::Undefined"`
1313
+ ```ruby
1314
+ Kind::Undefined.to_s # "Kind::Undefined"
1315
+ Kind::Undefined.inspect # "Kind::Undefined"
1316
+ ```
1317
+
1318
+ [⬆️ &nbsp;Back to Top](#changelog-)
1319
+
1320
+ 1.5.0 (2020-04-12)
1321
+ ------------------
1322
+
1323
+ ### Added
1324
+
1325
+ * [#18](https://github.com/serradura/kind/pull/18) - Refactor `Kind::Maybe`.
1326
+
1327
+ * [#18](https://github.com/serradura/kind/pull/18) - Add `Kind::Maybe::Value` module, that has the `.some?` and `.none?` methods. Available to check if the given value is `Some` or `None`.
1328
+ ```ruby
1329
+ Kind::Maybe::Value.some?(1) # true
1330
+ Kind::Maybe::Value.some?(nil) # false
1331
+ Kind::Maybe::Value.some?(Kind::Undefined) # false
1332
+
1333
+ Kind::Maybe::Value.none?(1) # false
1334
+ Kind::Maybe::Value.none?(nil) # true
1335
+ Kind::Maybe::Value.none?(Kind::Undefined) # true
1336
+ ```
1337
+
1338
+ [⬆️ &nbsp;Back to Top](#changelog-)
1339
+
1340
+ 1.4.0 (2020-04-12)
1341
+ ------------------
1342
+
1343
+ ### Added
1344
+
1345
+ * [#17](https://github.com/serradura/kind/pull/17) - Transform `Kind::Optional` into `Kind::Maybe`. `Kind::Optional` still is available but as an alias for `Kind::Maybe`.
1346
+
1347
+ [⬆️ &nbsp;Back to Top](#changelog-)
1348
+
1349
+ 1.3.0 (2020-04-12)
1350
+ ------------------
1351
+
1352
+ ### Added
1353
+
1354
+ * [#16](https://github.com/serradura/kind/pull/16) - Add a new special type checkers.
1355
+ - `Kind::Of::Callable` for check if the given object respond to `call`.
1356
+ - `Kind::Is::Callable` if the given value is a `class`, it will verifies if its `public_instance_methods.include?(:call)`.
1357
+
1358
+ [⬆️ &nbsp;Back to Top](#changelog-)
1359
+
1360
+ 1.2.0 (2020-04-12)
1361
+ ------------------
1362
+
1363
+ ### Added
1364
+
1365
+ * [#15](https://github.com/serradura/kind/pull/15) - Add `Kind::Optional` the maybe monad, it encapsulates an optional value. A `Kind::Optional` either contains a value (represented as `Some`), or it is empty (represented as `None`). This data structure is helpful to transform a value through several operations, but if any of them returns `nil` or `Kind::Undefined` as its result, the next operations will be avoided.
1366
+ ```ruby
1367
+ # Some value
1368
+
1369
+ optional =
1370
+ Kind::Optional.new(2)
1371
+ .map { |value| value * 2 }
1372
+ .map { |value| value * 2 }
1373
+
1374
+ puts optional.value # 8
1375
+ puts optional.some? # true
1376
+ puts optional.none? # false
1377
+
1378
+ puts optional.value_or(0) # 8
1379
+ puts optional.value_or { 0 } # 8
1380
+
1381
+ # None value
1382
+
1383
+ even_number = Kind::Optional.new(3).map { |n| n if n.even? }
1384
+
1385
+ even_number.none? # true
1386
+
1387
+ even_number.value_or(0) # 0
1388
+
1389
+ # Utility method
1390
+
1391
+ # Kind::Optional#try
1392
+ # You could use the `#try` method to perform a method of the wrapped object and return its value.
1393
+
1394
+ Kind::Optional.new(' Rodrigo ').try(:strip) # "Rodrigo"
1395
+
1396
+ # Method aliases
1397
+
1398
+ # Kind::Optional[] is an alias for Kind::Optional.new
1399
+ Kind::Optional[2].map { |n| n if n.even? }.value_or(0) # 2
1400
+
1401
+ # Kind::Optional::Result#then is an alias for Kind::Optional::Result#map
1402
+ Kind::Optional[1].then { |n| n if n.even? }.value_or(0) # 0
1403
+ ```
1404
+
1405
+ * [#15](https://github.com/serradura/kind/pull/15) - Add new methods to `Kind::Undefined`.
1406
+ ```ruby
1407
+ Kind::Undefined.to_s # 'Undefined'
1408
+ Kind::Undefined.inspect # 'Undefined'
1409
+
1410
+ Kind::Undefined.clone # #<Kind::Undefined:0x0000...>
1411
+ Kind::Undefined.dup # #<Kind::Undefined:0x0000...>
1412
+
1413
+ Kind::Undefined.clone == Kind::Undefined # true
1414
+ Kind::Undefined.clone === Kind::Undefined # true
1415
+
1416
+ Kind::Undefined.dup == Kind::Undefined # true
1417
+ Kind::Undefined.dup === Kind::Undefined # true
1418
+
1419
+ value = Kind::Undefined
1420
+
1421
+ Kind::Undefined.default(value, 1) # 1
1422
+ ```
1423
+
1424
+ [⬆️ &nbsp;Back to Top](#changelog-)
1425
+
1426
+ 1.1.0 (2020-04-09)
1427
+ ------------------
1428
+
1429
+ ### Added
1430
+
1431
+ * [#14](https://github.com/serradura/kind/pull/14) - Add `Kind::Undefined` representing an undefined value to contrast with `nil`.
1432
+
1433
+ ### Fixed
1434
+
1435
+ * [#14](https://github.com/serradura/kind/pull/14) - Raise a `Kind::Error` if `nil` is the argument of any strict type checker.
1436
+ ```ruby
1437
+ Kind.of.Hash(nil) # raise Kind::Error, "nil expected to be a kind of Hash"
1438
+ ```
1439
+
1440
+ [⬆️ &nbsp;Back to Top](#changelog-)
1441
+
1442
+ 1.0.0 (2020-03-16)
1443
+ ------------------
1444
+
1445
+ ### Added
1446
+
1447
+ * [#12](https://github.com/serradura/kind/pull/12) - Register type checkers respecting their namespaces.
1448
+ ```ruby
1449
+ module Account
1450
+ class User
1451
+ Kind::Types.add(self)
1452
+ end
1453
+ end
1454
+
1455
+ module Account
1456
+ class User
1457
+ class Membership
1458
+ Kind::Types.add(self)
1459
+ end
1460
+ end
1461
+ end
1462
+
1463
+ account_user = Account::User.new
1464
+
1465
+ Kind.of.Account::User(account_user) # #<Account::User:0x0000...>
1466
+ Kind.of.Account::User({}) # Kind::Error ({} expected to be a kind of Account::User)
1467
+
1468
+ Kind.of.Account::User.or_nil({}) # nil
1469
+
1470
+ Kind.of.Account::User.instance?({}) # false
1471
+ Kind.of.Account::User.instance?(account_user) # true
1472
+
1473
+ Kind.of.Account::User.class?(Hash) # false
1474
+ Kind.of.Account::User.class?(Account::User) # true
1475
+
1476
+ # ---
1477
+
1478
+ membership = Account::User::Membership.new
1479
+
1480
+ Kind.of.Account::User::Membership(membership) # #<Account::User::Membership:0x0000...>
1481
+ Kind.of.Account::User::Membership({}) # Kind::Error ({} expected to be a kind of Account::User::Membership)
1482
+
1483
+ Kind.of.Account::User::Membership.or_nil({}) # nil
1484
+
1485
+ Kind.of.Account::User::Membership.instance?({}) # false
1486
+ Kind.of.Account::User::Membership.instance?(membership) # true
1487
+
1488
+ Kind.of.Account::User::Membership.class?(Hash) # false
1489
+ Kind.of.Account::User::Membership.class?(Account::User::Membership) # true
1490
+ ```
1491
+
1492
+ [⬆️ &nbsp;Back to Top](#changelog-)
1493
+
1494
+ 0.6.0 (2020-01-06)
1495
+ ------------------
1496
+
1497
+ ### Added
1498
+
1499
+ * [#11](https://github.com/serradura/kind/pull/11) - Register the `Queue` (`Thread::Queue`) type checker. This registering creates:
1500
+ - `Kind::Of::Queue`, `Kind::Is::Queue`
1501
+
1502
+ [⬆️ &nbsp;Back to Top](#changelog-)
1503
+
1504
+ 0.5.0 (2020-01-04)
1505
+ ------------------
1506
+
1507
+ ### Added
1508
+
1509
+ * [#4](https://github.com/serradura/kind/pull/4) - Allow defining a default value when the verified object is `nil`.
1510
+ ```ruby
1511
+ Kind.of.Hash(nil, or: {}) # {}
1512
+ ```
1513
+
1514
+ [⬆️ &nbsp;Back to Top](#changelog-)
1515
+
1516
+ 0.4.0 (2020-01-03)
1517
+ ------------------
1518
+
1519
+ ### Added
1520
+
1521
+ * [#3](https://github.com/serradura/kind/pull/3) - Require `2.2.0` as the minimum Ruby version.
1522
+
1523
+ [⬆️ &nbsp;Back to Top](#changelog-)
1524
+
1525
+ 0.3.0 (2020-01-03)
1526
+ ------------------
1527
+
1528
+ ### Added
1529
+
1530
+ * [#2](https://github.com/serradura/kind/pull/2) - Add `Kind::Checker` to create an object which knows how to do a type checking. (PR: #2)
1531
+ ```ruby
1532
+ class User
1533
+ end
1534
+
1535
+ user = User.new
1536
+
1537
+ UserKind = Kind::Checker.new(User)
1538
+
1539
+ UserKind.class?(User) # true
1540
+ UserKind.class?(String) # false
1541
+
1542
+ UserKind.instance?(user) # true
1543
+ UserKind.instance?(1) # false
1544
+
1545
+ UserKind.or_nil(user) # #<User:0x0000...>
1546
+ UserKind.or_nil(1) # nil
1547
+ ```
1548
+
1549
+ ### Breaking Changes
1550
+
1551
+ * [#2](https://github.com/serradura/kind/pull/2) - Replace `instance_eval` in modules by singleton objects to define the type checkers (via `Kind::Types.add()`). The behavior still the same, but the constants become a `Kind::Checker` object instead of a module.
1552
+
1553
+ [⬆️ &nbsp;Back to Top](#changelog-)
1554
+
1555
+ 0.2.0 (2020-01-02)
1556
+ ------------------
1557
+
1558
+ ### Added
1559
+
1560
+ * [#1](https://github.com/serradura/kind/pull/1) - Register type checkers for several Ruby classes/modules. (PR: #1)
1561
+ - **Classes:**
1562
+ - `Kind::Of::Symbol` , `Kind::Is::Symbol`
1563
+ - `Kind::Of::Numeric` , `Kind::Is::Numeric`
1564
+ - `Kind::Of::Integer` , `Kind::Is::Integer`
1565
+ - `Kind::Of::Float` , `Kind::Is::Float`
1566
+ - `Kind::Of::Regexp` , `Kind::Is::Regexp`
1567
+ - `Kind::Of::Time` , `Kind::Is::Time`
1568
+ - `Kind::Of::Array` , `Kind::Is::Array`
1569
+ - `Kind::Of::Range` , `Kind::Is::Range`
1570
+ - `Kind::Of::Hash` , `Kind::Is::Hash`
1571
+ - `Kind::Of::Struct` , `Kind::Is::Struct`
1572
+ - `Kind::Of::Enumerator`, `Kind::Is::Enumerator`
1573
+ - `Kind::Of::Method` , `Kind::Is::Method`
1574
+ - `Kind::Of::Proc` , `Kind::Is::Proc`
1575
+ - `Kind::Of::IO` , `Kind::Is::IO`
1576
+ - `Kind::Of::File` , `Kind::Is::File`
1577
+ - **Modules:**
1578
+ - `Kind::Of::Enumerable`, `Kind::Is::Enumerable`
1579
+ - `Kind::Of::Comparable`, `Kind::Is::Comparable`
1580
+
1581
+ * [#1](https://github.com/serradura/kind/pull/1) - Create special type checkers.
1582
+ - `Kind::Of::Boolean` for check if the given object is `true` or `false`.
1583
+ - `Kind::Of::Lambda` for check if the given object is a `lambda`.
1584
+ - `Kind::Of::Module` for check if the given object is a *module*.
1585
+
1586
+ [⬆️ &nbsp;Back to Top](#changelog-)
1587
+
1588
+ 0.1.0 (2019-12-26)
1589
+ ------------------
1590
+
1591
+ ### Added
1592
+
1593
+ * Require `2.3.0` as the minimum Ruby version.
1594
+
1595
+ * `Kind::Error` for defining type checkers errors.
1596
+
1597
+ * `Kind::Of.call()` for check if the given *class* is the *class/superclass* of object, or if the given *module* is included in the object. `Kind::Error` will be raised, If the object hasn't the expected kind.
1598
+ ```ruby
1599
+ Kind::Of.(String, '') # ''
1600
+ Kind::Of.(String, 1) # Kind::Error (1 expected to be a kind of String)
1601
+ ```
1602
+
1603
+ * `Kind::Of::Class()` for check if the given object is a *class*.
1604
+
1605
+ * `Kind::Is.call()` for check if the first kind is equal or a parent class/module of the second one.
1606
+ ```ruby
1607
+ Kind::Is.(String, String) # true
1608
+ Kind::Is.(Symbol, String) # false
1609
+ ```
1610
+
1611
+ * `Kind.of` is a shortcut for `Kind::Of`.
1612
+
1613
+ * `Kind.is` is a shortcut for `Kind::Is`.
1614
+
1615
+ * `Kind::Types.add` allows the registering of new type checkers.
1616
+ ```ruby
1617
+ # Registering Symbol
1618
+
1619
+ Kind::Type.add(Symbol)
1620
+
1621
+ # Adds a method in the Kind::Is module
1622
+
1623
+ class MySymbol < Symbol
1624
+ end
1625
+
1626
+ Kind.is.Symbol(Symbol) # true
1627
+ Kind.is.Symbol(MySymbol) # true
1628
+ Kind.is.Symbol(String) # false
1629
+
1630
+ # Adds a method in the Kind::Of module
1631
+
1632
+ Kind.of.Symbol(:a) # :a
1633
+ Kind.of.Symbol(1) # Kind::Error (1 expected to be a kind of Symbol)
1634
+
1635
+ # Creates a module in Kind::Of with type checking methods related to the given kind.
1636
+
1637
+ Kind::Of::Symbol.class?(Symbol) # true
1638
+ Kind::Of::Symbol.instance?(:a) # true
1639
+ Kind::Of::Symbol.or_nil(:b) # :b
1640
+ Kind::Of::Symbol.or_nil(1) # nil
1641
+ ```
1642
+
1643
+ * Register the `String` and `Hash` type checkers. This registering creates:
1644
+ - `Kind::Of::Hash`, `Kind::Is::Hash`
1645
+ - `Kind::Of::String`, `Kind::Is::String`
1646
+
1647
+ [⬆️ &nbsp;Back to Top](#changelog-)