kind 3.1.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.tool-versions +1 -0
  3. data/.travis.sh +37 -12
  4. data/.travis.yml +6 -3
  5. data/CHANGELOG.md +1230 -0
  6. data/Gemfile +10 -2
  7. data/README.md +817 -487
  8. data/lib/kind.rb +53 -283
  9. data/lib/kind/active_model/kind_validator.rb +7 -7
  10. data/lib/kind/core.rb +9 -0
  11. data/lib/kind/core/deprecation.rb +29 -0
  12. data/lib/kind/core/kind.rb +61 -0
  13. data/lib/kind/core/undefined.rb +7 -0
  14. data/lib/kind/deprecations/built_in_type_checkers.rb +23 -0
  15. data/lib/kind/{checker.rb → deprecations/checker.rb} +3 -2
  16. data/lib/kind/{checker → deprecations/checker}/factory.rb +1 -5
  17. data/lib/kind/{checker → deprecations/checker}/protocol.rb +3 -3
  18. data/lib/kind/deprecations/is.rb +35 -0
  19. data/lib/kind/deprecations/of.rb +258 -0
  20. data/lib/kind/{types.rb → deprecations/types.rb} +14 -8
  21. data/lib/kind/dig.rb +13 -9
  22. data/lib/kind/empty.rb +5 -11
  23. data/lib/kind/error.rb +2 -6
  24. data/lib/kind/maybe.rb +12 -161
  25. data/lib/kind/maybe/none.rb +57 -0
  26. data/lib/kind/maybe/result.rb +51 -0
  27. data/lib/kind/maybe/some.rb +90 -0
  28. data/lib/kind/maybe/typed.rb +29 -0
  29. data/lib/kind/maybe/wrappable.rb +35 -0
  30. data/lib/kind/presence.rb +33 -0
  31. data/lib/kind/try.rb +36 -0
  32. data/lib/kind/type_checker.rb +73 -0
  33. data/lib/kind/type_checkers.rb +30 -0
  34. data/lib/kind/type_checkers/core/array.rb +17 -0
  35. data/lib/kind/type_checkers/core/class.rb +13 -0
  36. data/lib/kind/type_checkers/core/comparable.rb +13 -0
  37. data/lib/kind/type_checkers/core/enumerable.rb +13 -0
  38. data/lib/kind/type_checkers/core/enumerator.rb +13 -0
  39. data/lib/kind/type_checkers/core/file.rb +13 -0
  40. data/lib/kind/type_checkers/core/float.rb +13 -0
  41. data/lib/kind/type_checkers/core/hash.rb +17 -0
  42. data/lib/kind/type_checkers/core/integer.rb +13 -0
  43. data/lib/kind/type_checkers/core/io.rb +13 -0
  44. data/lib/kind/type_checkers/core/method.rb +13 -0
  45. data/lib/kind/type_checkers/core/module.rb +17 -0
  46. data/lib/kind/type_checkers/core/numeric.rb +13 -0
  47. data/lib/kind/type_checkers/core/proc.rb +13 -0
  48. data/lib/kind/type_checkers/core/queue.rb +14 -0
  49. data/lib/kind/type_checkers/core/range.rb +13 -0
  50. data/lib/kind/type_checkers/core/regexp.rb +13 -0
  51. data/lib/kind/type_checkers/core/string.rb +17 -0
  52. data/lib/kind/type_checkers/core/struct.rb +13 -0
  53. data/lib/kind/type_checkers/core/symbol.rb +13 -0
  54. data/lib/kind/type_checkers/core/time.rb +13 -0
  55. data/lib/kind/type_checkers/custom/boolean.rb +19 -0
  56. data/lib/kind/type_checkers/custom/callable.rb +19 -0
  57. data/lib/kind/type_checkers/custom/lambda.rb +19 -0
  58. data/lib/kind/type_checkers/stdlib/open_struct.rb +13 -0
  59. data/lib/kind/type_checkers/stdlib/set.rb +17 -0
  60. data/lib/kind/undefined.rb +4 -2
  61. data/lib/kind/validator.rb +1 -1
  62. data/lib/kind/version.rb +1 -1
  63. data/test.sh +4 -4
  64. metadata +51 -9
  65. data/lib/kind/is.rb +0 -19
  66. data/lib/kind/of.rb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b62da87ad8443fb4d3f1d96535bc69259addf767721db70288401d677733967
4
- data.tar.gz: 995f0a3a34ed67a3244e181380d627e076d4576a2b6e0c7de2b34fa415c5f868
3
+ metadata.gz: 62e657cbbdd2af4aebab8cde9f010744a0321c7fceff24f3574a2f9ac4c2a9ad
4
+ data.tar.gz: c0f5690cef9d736b703ac2f1f75c23b30fccc309ad58c18972060074c8946d3b
5
5
  SHA512:
6
- metadata.gz: 3398bbf2b67fe8b81023d9c781b14ac115f5749f45641a74fc1ff1c8d05f4fd1c4e2a4bc817d6c71e7f8dc96b245ec9f9c80a776ae11d22ba69dbc878dfaa847
7
- data.tar.gz: 75c6c1e387548447edf601d4b73b18ff3e2700debf3f6715b2f772f582829d39a72e7bb9d8af61fe8bcd32e9f72aa8398f1703f2c706668a0ab5e59fc0612b05
6
+ metadata.gz: 15281701426737bbf5c5cd4fa669ecb7c245fb520c1feb79d344d271f6552388111f75245babeffd4eadb3cd98120a3041a4d65959c78f0983ad167ce7060104
7
+ data.tar.gz: 6bd17ee29bcbf1bda1273eebf7ef2999a74a0e55935622330b19a07efac4f0e448436eb26115042e828c1e7ed9e00c1435207ddbcb84bbd11dcb15a1371a2a3a
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 2.7.2
data/.travis.sh CHANGED
@@ -1,20 +1,45 @@
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_with_bundler {
6
+ rm Gemfile.lock
6
7
 
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
8
+ if [ ! -z "$1" ]; then
9
+ bundle_cmd="bundle _$1_"
10
+ else
11
+ bundle_cmd="bundle"
12
+ fi
13
13
 
14
- if [[ ! $ruby_v =~ '2.2.0' ]]; then
15
- ACTIVEMODEL_VERSION='5.2' bundle update && bundle exec rake test
14
+ eval "$2 $bundle_cmd update"
15
+ eval "$2 $bundle_cmd exec rake test"
16
+ }
17
+
18
+ function run_with_am_version_and_bundler {
19
+ run_with_bundler "$2" "ACTIVEMODEL_VERSION=$1"
20
+ }
21
+
22
+ RUBY_2_2345="ruby 2.[2345]."
23
+
24
+ if [[ $RUBY_V =~ $RUBY_2_2345 ]]; then
25
+ run_with_bundler "$BUNDLER_V1"
26
+
27
+ run_with_am_version_and_bundler "3.2" "$BUNDLER_V1"
28
+ run_with_am_version_and_bundler "4.0" "$BUNDLER_V1"
29
+ run_with_am_version_and_bundler "4.1" "$BUNDLER_V1"
30
+ run_with_am_version_and_bundler "4.2" "$BUNDLER_V1"
31
+ run_with_am_version_and_bundler "5.0" "$BUNDLER_V1"
32
+ run_with_am_version_and_bundler "5.1" "$BUNDLER_V1"
33
+ run_with_am_version_and_bundler "5.2" "$BUNDLER_V1"
16
34
  fi
17
35
 
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
36
+ RUBY_2_567="ruby 2.[567]."
37
+ RUBY_3_x_x="ruby 3.0."
38
+
39
+ if [[ $RUBY_V =~ $RUBY_2_567 ]] || [[ $RUBY_V =~ $RUBY_3_x_x ]]; then
40
+ gem install bundler -v ">= 2" --no-doc
41
+
42
+ run_with_bundler
43
+ run_with_am_version_and_bundler "6.0"
44
+ run_with_am_version_and_bundler "6.1"
20
45
  fi
data/.travis.yml CHANGED
@@ -9,16 +9,19 @@ cache:
9
9
  - /home/travis/.rvm/
10
10
 
11
11
  rvm:
12
- - 2.2.0
12
+ - 2.2.2
13
13
  - 2.3.0
14
14
  - 2.4.0
15
15
  - 2.5.0
16
16
  - 2.6.0
17
17
  - 2.7.0
18
+ - 3.0.0
19
+
20
+ env:
21
+ - BUNDLER_V1="1.17.3"
18
22
 
19
23
  before_install:
20
- - gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true
21
- - gem install bundler -v '< 2'
24
+ - gem install bundler -v "$BUNDLER_V1"
22
25
 
23
26
  install: bundle install --jobs=3 --retry=3
24
27
 
data/CHANGELOG.md ADDED
@@ -0,0 +1,1230 @@
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
+ - [4.0.0 (2021-02-22)](#400-2021-02-22)
7
+ - [Added](#added)
8
+ - [Deprecated](#deprecated)
9
+ - [Fixed](#fixed)
10
+ - [3.1.0 (2020-07-08)](#310-2020-07-08)
11
+ - [Added](#added-1)
12
+ - [3.0.0 (2020-06-25)](#300-2020-06-25)
13
+ - [Breaking Changes](#breaking-changes)
14
+ - [Added](#added-2)
15
+ - [2.3.0 (2020-06-24)](#230-2020-06-24)
16
+ - [Added](#added-3)
17
+ - [2.2.0 (2020-06-23)](#220-2020-06-23)
18
+ - [Added](#added-4)
19
+ - [2.1.0 (2020-05-12)](#210-2020-05-12)
20
+ - [Added](#added-5)
21
+ - [Breaking Changes](#breaking-changes-1)
22
+ - [2.0.0 (2020-05-07)](#200-2020-05-07)
23
+ - [Added](#added-6)
24
+ - [Breaking Changes](#breaking-changes-2)
25
+ - [Removed](#removed)
26
+ - [1.9.0 (2020-05-06)](#190-2020-05-06)
27
+ - [Added](#added-7)
28
+ - [1.8.0 (2020-05-03)](#180-2020-05-03)
29
+ - [Added](#added-8)
30
+ - [1.7.0 (2020-05-03)](#170-2020-05-03)
31
+ - [Fixed](#fixed-1)
32
+ - [1.6.0 (2020-04-17)](#160-2020-04-17)
33
+ - [Added](#added-9)
34
+ - [Changes](#changes)
35
+ - [1.5.0 (2020-04-12)](#150-2020-04-12)
36
+ - [Added](#added-10)
37
+ - [1.4.0 (2020-04-12)](#140-2020-04-12)
38
+ - [Added](#added-11)
39
+ - [1.3.0 (2020-04-12)](#130-2020-04-12)
40
+ - [Added](#added-12)
41
+ - [1.2.0 (2020-04-12)](#120-2020-04-12)
42
+ - [Added](#added-13)
43
+ - [1.1.0 (2020-04-09)](#110-2020-04-09)
44
+ - [Added](#added-14)
45
+ - [Fixed](#fixed-2)
46
+ - [1.0.0 (2020-03-16)](#100-2020-03-16)
47
+ - [Added](#added-15)
48
+ - [0.6.0 (2020-01-06)](#060-2020-01-06)
49
+ - [Added](#added-16)
50
+ - [0.5.0 (2020-01-04)](#050-2020-01-04)
51
+ - [Added](#added-17)
52
+ - [0.4.0 (2020-01-03)](#040-2020-01-03)
53
+ - [Added](#added-18)
54
+ - [0.3.0 (2020-01-03)](#030-2020-01-03)
55
+ - [Added](#added-19)
56
+ - [Breaking Changes](#breaking-changes-3)
57
+ - [0.2.0 (2020-01-02)](#020-2020-01-02)
58
+ - [Added](#added-20)
59
+ - [0.1.0 (2019-12-26)](#010-2019-12-26)
60
+ - [Added](#added-21)
61
+
62
+ ## Unreleased
63
+
64
+ <!--
65
+ ### Added
66
+ ### Breaking Changes
67
+ ### Deprecated
68
+ ### Removed
69
+ ### Fixed
70
+ -->
71
+
72
+ 4.0.0 (2021-02-22)
73
+ ------------------
74
+
75
+ ### Added
76
+
77
+ * [#40](https://github.com/serradura/kind/pull/40) - Add `Kind.of_class?` to verify if a given value is a `Class`.
78
+
79
+ * [#40](https://github.com/serradura/kind/pull/40) - Add `Kind.of_module?` to verify if a given value is a `Module`.
80
+
81
+ * [#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.
82
+ ```ruby
83
+ Kind.of_module_or_class(String) # String
84
+ Kind.of_module_or_class(1) # Kind::Error (1 expected to be a kind of Module/Class)
85
+ ```
86
+
87
+ * [#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.
88
+ ```ruby
89
+ Kind.respond_to('', :upcase) # ""
90
+ Kind.respond_to('', :upcase, :strip) # ""
91
+
92
+ Kind.respond_to(1, :upcase) # expected 1 to respond to :upcase
93
+ Kind.respond_to(2, :to_s, :upcase) # expected 2 to respond to :upcase
94
+ ```
95
+
96
+ * [#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.
97
+ ```ruby
98
+ Kind::Try.(' foo ', :strip) # "foo"
99
+ Kind::Try.({a: 1}, :[], :a) # 1
100
+ Kind::Try.({a: 1}, :[], :b) # nil
101
+ Kind::Try.({a: 1}, :fetch, :b, 2) # 2
102
+
103
+ Kind::Try.(:symbol, :strip) # nil
104
+ Kind::Try.(:symbol, :fetch, :b, 2) # nil
105
+
106
+ # It raises an exception if the method name isn't a string or a symbol
107
+ Kind::Try.({a: 1}, 1, :a) # TypeError (1 is not a symbol nor a string)
108
+ ```
109
+
110
+ * [#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.
111
+
112
+ * [#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.
113
+ ```ruby
114
+ # All of the methods used with the Kind::String can be used with any other type checker module.
115
+
116
+ # Kind::<Type>.name
117
+ # Kind::<Type>.kind
118
+ # The type checker can return its kind and its name
119
+ Kind::String.name # "String"
120
+ Kind::String.kind # ::String
121
+
122
+ # Kind::<Type>.===
123
+ # Can check if a given value is an instance of its kind.
124
+ Kind::String === 'foo' # true
125
+ Kind::String === :foo # false
126
+
127
+ # Kind::<Type>.value?(value)
128
+ # Can check if a given value is an instance of its kind.
129
+ Kind::String.value?('foo') # true
130
+ Kind::String.value?(:foo) # false
131
+
132
+ # If it doesn't receive an argument, a lambda will be returned and it will know how to do the type verification.
133
+ [1, 2, 'foo', 3, 'Bar'].select?(&Kind::String.value?) # ["foo", "bar"]
134
+
135
+ # Kind::<Type>.or_nil(value)
136
+ # Can return nil if the given value isn't an instance of its kind
137
+ Kind::String.or_nil('foo') # "foo"
138
+ Kind::String.or_nil(:foo) # nil
139
+
140
+ # Kind::<Type>.or_undefined(value)
141
+ # Can return Kind::Undefined if the given value isn't an instance of its kind
142
+ Kind::String.or_undefined('foo') # "foo"
143
+ Kind::String.or_undefined(:foo) # Kind::Undefined
144
+
145
+ # Kind::<Type>.or(fallback, value)
146
+ # Can return a fallback if the given value isn't an instance of its kind
147
+ Kind::String.or(nil, 'foo') # "foo"
148
+ Kind::String.or(nil, :foo) # nil
149
+
150
+ # 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.
151
+ [1, 2, 'foo', 3, 'Bar'].map(&Kind::String.or('')) # ["", "", "foo", "", "Bar"]
152
+ [1, 2, 'foo', 3, 'Bar'].map(&Kind::String.or(nil)) # [nil, nil, "foo", nil, "Bar"]
153
+
154
+ # An error will be raised if the fallback didn't have the expected kind or if not nil / Kind::Undefined.
155
+ [1, 2, 'foo', 3, 'Bar'].map(&Kind::String.or(:foo)) # Kind::Error (:foo expected to be a kind of String)
156
+
157
+ # Kind::<Type>[value]
158
+ # Will raise Kind::Error if the given value isn't an instance of the expected kind
159
+ Kind::String['foo'] # "foo"
160
+ Kind::String[:foo ] # Kind::Error (:foo expected to be a kind of String)
161
+ ```
162
+ * List of all type checkers:
163
+ * **Core:**
164
+ * `Kind::Array`
165
+ * `Kind::Class`
166
+ * `Kind::Comparable`
167
+ * `Kind::Enumerable`
168
+ * `Kind::Enumerator`
169
+ * `Kind::File`
170
+ * `Kind::Float`
171
+ * `Kind::Hash`
172
+ * `Kind::Integer`
173
+ * `Kind::IO`
174
+ * `Kind::Method`
175
+ * `Kind::Module`
176
+ * `Kind::Numeric`
177
+ * `Kind::Proc`
178
+ * `Kind::Queue`
179
+ * `Kind::Range`
180
+ * `Kind::Regexp`
181
+ * `Kind::String`
182
+ * `Kind::Struct`
183
+ * `Kind::Symbol`
184
+ * `Kind::Time`
185
+ * **Custom:**
186
+ * `Kind::Boolean`
187
+ * `Kind::Callable`
188
+ * `Kind::Lambda`
189
+ * **Stdlib:**
190
+ * `Kind::OpenStruct`
191
+ * `Kind::Set`
192
+
193
+ * [#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.
194
+ ```ruby
195
+ # Example using a class (an object which responds to .=== and has the .name method):
196
+ # This object will have all of the default methods that a standard type checker (e.g: Kind::String) has.
197
+ kind_of_string = Kind::Of(String)
198
+
199
+ kind_of_string[''] # ""
200
+ kind_of_string[{}] # Kind::Error ({} expected to be a kind of String)
201
+
202
+ # Example using a lambda (an object which responds to .===) and a hash with the kind name.
203
+
204
+ PositiveInteger = Kind::Of(-> value { value.kind_of?(Integer) && value > 0 }, name: 'PositiveInteger')
205
+
206
+ # PositiveInteger.name
207
+ # PositiveInteger.kind
208
+ # The type checker can return its kind and its name
209
+ PositiveInteger.name # "PositiveInteger"
210
+ PositiveInteger.kind # #<Proc:0x0000.... >
211
+
212
+ # PositiveInteger.===
213
+ # Can check if a given value is an instance of its kind.
214
+ PositiveInteger === 1 # true
215
+ PositiveInteger === 0 # false
216
+
217
+ # PositiveInteger.value?(value)
218
+ # Can check if a given value is an instance of its kind.
219
+ PositiveInteger.value?(1) # true
220
+ PositiveInteger.value?(-1) # false
221
+
222
+ # If it doesn't receive an argument, a lambda will be returned and it will know how to do the type verification.
223
+ [1, 2, 0, 3, -1].select?(&PositiveInteger.value?) # [1, 2, 3]
224
+
225
+ # PositiveInteger.or_nil(value)
226
+ # Can return nil if the given value isn't an instance of its kind
227
+ PositiveInteger.or_nil(1) # 1
228
+ PositiveInteger.or_nil(0) # nil
229
+
230
+ # PositiveInteger.or_undefined(value)
231
+ # Can return Kind::Undefined if the given value isn't an instance of its kind
232
+ PositiveInteger.or_undefined(2) # 2
233
+ PositiveInteger.or_undefined(-1) # Kind::Undefined
234
+
235
+ # PositiveInteger.or(fallback, value)
236
+ # Can return a fallback if the given value isn't an instance of its kind
237
+ PositiveInteger.or(nil, 1) # 1
238
+ PositiveInteger.or(nil, 0) # nil
239
+
240
+ # 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.
241
+ [1, 2, 0, 3, -1].map(&PositiveInteger.or(1)) # [1, 2, 1, 3, 1]
242
+ [1, 2, 0, 3, -1].map(&PositiveInteger.or(nil)) # [1, 2, nil, 3, nil]
243
+
244
+ # An error will be raised if the fallback didn't have the expected kind or if not nil / Kind::Undefined.
245
+ [1, 2, 0, 3, -1].map(&PositiveInteger.or(:foo)) # Kind::Error (:foo expected to be a kind of PositiveInteger)
246
+
247
+ # PositiveInteger[value]
248
+ # Will raise Kind::Error if the given value isn't an instance of the expected kind
249
+ PositiveInteger[1] # 1
250
+ PositiveInteger[:foo] # Kind::Error (:foo expected to be a kind of PositiveInteger)
251
+ ```
252
+ * [#40](https://github.com/serradura/kind/pull/40), [#41](https://github.com/serradura/kind/pull/40) - Add type checkers methods.
253
+ ```ruby
254
+ # All of the methods used with the Kind::String? can be used with any other type checker method.
255
+
256
+ # Kind::<Type>?(*values)
257
+ # Can check if a given value (one or many) is an instance of its kind.
258
+ Kind::String?('foo') # true
259
+ Kind::String?('foo', 'bar') # true
260
+ Kind::String?('foo', :bar) # false
261
+
262
+ # If it doesn't receive an argument, a lambda will be returned and it will know how to do the type verification.
263
+ [1, 2, 'foo', 3, 'Bar'].select?(&Kind::String?) # ["foo", "bar"]
264
+ ```
265
+ * List of all type checkers:
266
+ * **Core:**
267
+ * `Kind::Array?`
268
+ * `Kind::Class?`
269
+ * `Kind::Comparable?`
270
+ * `Kind::Enumerable?`
271
+ * `Kind::Enumerator?`
272
+ * `Kind::File?`
273
+ * `Kind::Float?`
274
+ * `Kind::Hash?`
275
+ * `Kind::Integer?`
276
+ * `Kind::IO?`
277
+ * `Kind::Method?`
278
+ * `Kind::Module?`
279
+ * `Kind::Numeric?`
280
+ * `Kind::Proc?`
281
+ * `Kind::Queue?`
282
+ * `Kind::Range?`
283
+ * `Kind::Regexp?`
284
+ * `Kind::String?`
285
+ * `Kind::Struct?`
286
+ * `Kind::Symbol?`
287
+ * `Kind::Time?`
288
+ * **Custom:**
289
+ * `Kind::Boolean?`
290
+ * `Kind::Callable?`
291
+ * `Kind::Lambda?`
292
+ * **Stdlib:**
293
+ * `Kind::OpenStruct?`
294
+ * `Kind::Set?`
295
+
296
+ * [#41](https://github.com/serradura/kind/pull/41) - Make `Kind::Dig.call` extract values from regular objects.
297
+ ```ruby
298
+ class Person
299
+ attr_reader :name
300
+
301
+ def initialize(name)
302
+ @name = name
303
+ end
304
+ end
305
+
306
+ person = Person.new('Rodrigo')
307
+
308
+ Kind::Dig.(person, [:name]) # "Rodrigo"
309
+
310
+ Kind::Dig.({people: [person]}, [:people, 0, :name]) # "Rodrigo"
311
+ ```
312
+
313
+ * [#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`.
314
+ ```ruby
315
+ Kind::Presence.(true) # true
316
+ Kind::Presence.('foo') # "foo"
317
+ Kind::Presence.([1, 2]) # [1, 2]
318
+ Kind::Presence.({a: 3}) # {a: 3}
319
+ Kind::Presence.(Set.new([4])) # #<Set: {4}>
320
+
321
+ Kind::Presence.('') # nil
322
+ Kind::Presence.(' ') # nil
323
+ Kind::Presence.("\t\n\r") # nil
324
+ Kind::Presence.("\u00a0") # nil
325
+
326
+ Kind::Presence.([]) # nil
327
+ Kind::Presence.({}) # nil
328
+ Kind::Presence.(Set.new) # nil
329
+
330
+ Kind::Presence.(nil) # nil
331
+ Kind::Presence.(false) # nil
332
+
333
+ # nil will be returned if the given object responds to the method blank? and this method result is true.
334
+ MyObject = Struct.new(:is_blank) do
335
+ def blank?
336
+ is_blank
337
+ end
338
+ end
339
+
340
+ my_object = MyObject.new
341
+
342
+ my_object.is_blank = true
343
+
344
+ Kind::Presence.(my_object) # nil
345
+
346
+ my_object.is_blank = false
347
+
348
+ Kind::Presence.(my_object) # #<struct MyObject is_blank=false>
349
+ ```
350
+
351
+ * [#41](https://github.com/serradura/kind/pull/41) - Add `Kind::Maybe#presence`, this method will return None if the wrapped value wasn't present.
352
+ ```ruby
353
+ result1 = Kind::Maybe(Hash).wrap(foo: '').dig(:foo).presence
354
+ result1.none? # true
355
+ result1.value # nil
356
+
357
+ result2 = Kind::Maybe(Hash).wrap(foo: 'bar').dig(:foo).presence
358
+ result2.none? # false
359
+ result2.value # "bar"
360
+ ```
361
+
362
+ * [#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.
363
+ ```ruby
364
+ Kind::Maybe.wrap { 2 / 0 } # #<Kind::Maybe::None:0x0000... @value=#<ZeroDivisionError: divided by 0>>
365
+
366
+ Kind::Maybe(Numeric).wrap(2) { |number| number / 0 } # #<Kind::Maybe::None:0x0000... @value=#<ZeroDivisionError: divided by 0>>
367
+ ```
368
+
369
+ * [#41](https://github.com/serradura/kind/pull/41) - Make `Kind::Maybe#map` intercept StandardError exceptions.
370
+ * Now the `#map` and `#then` methods will intercept any StandardError and return None with the exception or their values.
371
+ * 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.
372
+ * If an exception (StandardError) is returned by the methods `#then`, `#map` it will be resolved as None.
373
+ ```ruby
374
+ # Handling StandardError exceptions
375
+ result1 = Kind::Maybe[2].map { |number| number / 0 }
376
+ result1.none? # true
377
+ result1.value # #<ZeroDivisionError: divided by 0>
378
+
379
+ result2 = Kind::Maybe[3].then { |number| number / 0 }
380
+ result2.none? # true
381
+ result2.value # #<ZeroDivisionError: divided by 0>
382
+
383
+ # Leaking StandardError exceptions
384
+ Kind::Maybe[2].map! { |number| number / 0 } # ZeroDivisionError (divided by 0)
385
+
386
+ Kind::Maybe[2].then! { |number| number / 0 } # ZeroDivisionError (divided by 0)
387
+ ```
388
+
389
+ * [#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.
390
+ ```ruby
391
+ # Using built-in type checkers
392
+ Kind::String.value(1, default: '') # ""
393
+ Kind::String.value('1', default: '') # "1"
394
+
395
+ Kind::String.value('1', default: 1) # Kind::Error (1 expected to be a kind of String)
396
+
397
+ # Using custom type checkers
398
+ PositiveInteger = Kind::Of(-> value { value.kind_of?(Integer) && value > 0 }, name: 'PositiveInteger')
399
+
400
+ PositiveInteger.value(0, default: 1) # 1
401
+ PositiveInteger.value(2, default: 1) # 2
402
+
403
+ PositiveInteger.value(-1, default: 0) # Kind::Error (0 expected to be a kind of PositiveInteger)
404
+ ```
405
+
406
+ * [#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.
407
+ ```ruby
408
+ Kind::Array.value_or_empty({}) # []
409
+ Kind::Array.value_or_empty({}).frozen? # true
410
+ ```
411
+
412
+ * [#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.
413
+ ```ruby
414
+ Kind.value(String, '1', default: '') # "1"
415
+
416
+ Kind.value(String, 1, default: '') # ""
417
+
418
+ Kind.value(String, 1, default: 2) # Kind::Error (2 expected to be a kind of String)
419
+ ```
420
+
421
+ * [#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:
422
+ ```ruby
423
+ ['', [], {}, '1', [2]].map(&Kind::Presence) # [nil, nil, nil, "1", [2]]
424
+ ```
425
+
426
+ * [#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.
427
+ ```ruby
428
+ some_number = Kind::Some(2)
429
+
430
+ Kind::Maybe(Numeric)[some_number] # #<Kind::Maybe::Some:0x0000... @value=2>
431
+
432
+ Kind::Maybe(Numeric).new(some_number) # #<Kind::Maybe::Some:0x0000... @value=2>
433
+
434
+ Kind::Maybe(Numeric).wrap(some_number) # #<Kind::Maybe::Some:0x0000... @value=2>
435
+
436
+ Kind::Maybe(Numeric).wrap { some_number } # #<Kind::Maybe::Some:0x0000... @value=2>
437
+
438
+ Kind::Maybe(Numeric).wrap(some_number) { |number| number / 2 } # #<Kind::Maybe::Some:0x0000... @value=1>
439
+ ```
440
+
441
+ * [#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.
442
+ ```ruby
443
+ Kind::Maybe(Numeric).wrap('2') { |number| number / 0 } # #<Kind::Maybe::None:0x0000... @value=nil>
444
+
445
+ Kind::Maybe(Numeric).wrap(2) { |number| number / 0 } # #<Kind::Maybe::None:0x0000... @value=#<ZeroDivisionError: divided by 0>>
446
+ ```
447
+
448
+ * [#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.
449
+ ```ruby
450
+ person_name = ->(params) do
451
+ Kind::Maybe(Hash)
452
+ .wrap(params)
453
+ .then { |hash| hash.values_at(:first_name, :last_name) }
454
+ .then { |names| names.map(&Kind::Presence).tap(&:compact!) }
455
+ .check { |names| names.size == 2 }
456
+ .then { |(first_name, last_name)| "#{first_name} #{last_name}" }
457
+ .value_or { 'John Doe' }
458
+ end
459
+
460
+ person_name.('') # "John Doe"
461
+ person_name.(nil) # "John Doe"
462
+ person_name.(last_name: 'Serradura') # "John Doe"
463
+ person_name.(first_name: 'Rodrigo') # "John Doe"
464
+
465
+ person_name.(first_name: 'Rodrigo', last_name: 'Serradura') # "Rodrigo Serradura"
466
+ ```
467
+
468
+ * [#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.
469
+ ```ruby
470
+ results = [
471
+ { person: {} },
472
+ { person: { name: 'Foo Bar'} },
473
+ { person: { name: 'Rodrigo Serradura'} },
474
+ ].map(&Kind::Dig[:person, :name])
475
+
476
+ p results # [nil, "Foo Bar", "Rodrigo Serradura"],
477
+ ```
478
+
479
+ * [#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.
480
+ ```ruby
481
+ results =
482
+ [
483
+ {},
484
+ {name: 'Foo Bar'},
485
+ {name: 'Rodrigo Serradura'},
486
+ ].map(&Kind::Try[:fetch, :name, 'John Doe'])
487
+
488
+ p results # ["John Doe", "Foo Bar", "Rodrigo Serradura"]
489
+ ```
490
+
491
+ ### Deprecated
492
+
493
+ * [#40](https://github.com/serradura/kind/pull/40) - Deprecate `Kind::Is.call`
494
+
495
+ * [#40](https://github.com/serradura/kind/pull/40) - Deprecate `Kind::Of.call`
496
+
497
+ * [#40](https://github.com/serradura/kind/pull/40) - Deprecate `Kind::Types.add`.
498
+
499
+ * [#40](https://github.com/serradura/kind/pull/40) - Deprecate `Kind::Of::<Type>` and `Kind::Is::<Type>` modules.
500
+
501
+ * [#40](https://github.com/serradura/kind/pull/40) - Deprecate `Kind::Checker`, `Kind::Checker::Protocol`, `Kind::Checker::Factory`.
502
+
503
+ * [#40](https://github.com/serradura/kind/pull/40) - Deprecate the invocation of `Kind.is` without arguments.
504
+
505
+ * [#40](https://github.com/serradura/kind/pull/40) - Deprecate the invocation of `Kind.of` without arguments.
506
+
507
+ * [#40](https://github.com/serradura/kind/pull/40) - Deprecate the invocation of `Kind.of` with a single argument (the kind).
508
+
509
+ ### Fixed
510
+
511
+ * [#40](https://github.com/serradura/kind/pull/40) - Make `Kind::Maybe.try!()` raises an error if it was called without a block or arguments.
512
+
513
+ [⬆️ &nbsp;Back to Top](#changelog-)
514
+
515
+ 3.1.0 (2020-07-08)
516
+ ------------------
517
+
518
+ ### Added
519
+
520
+ * [#33](https://github.com/serradura/kind/pull/33) - Add new type checker.
521
+ - `Kind::Of::OpenStruct`, `Kind::Is::OpenStruct`
522
+
523
+ * [#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.
524
+ ```ruby
525
+ class User
526
+ def self.find_by(id:)
527
+ return :user_1 if id == 1
528
+ end
529
+ end
530
+
531
+ Kind::Optional(Hash).wrap(user: { id: 2 }).dig(:user).value # {:id=>2}
532
+
533
+ # --
534
+
535
+ user_id = Kind::Optional(Hash).wrap(user: { id: 1 }).dig(:user, :id)
536
+
537
+ user_id.then { |id| User.find_by(id: id) }.value # :user_id
538
+ ```
539
+
540
+ * [#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.
541
+ ```ruby
542
+ s = Struct.new(:a, :b).new(101, 102)
543
+ o = OpenStruct.new(c: 103, d: 104)
544
+ d = { struct: s, ostruct: o, data: [s, o]}
545
+
546
+ Kind::Dig.(s, [:a]) # 101
547
+ Kind::Dig.(o, [:c]) # 103
548
+
549
+ Kind::Dig.(d, [:struct, :b]) # 102
550
+ Kind::Dig.(d, [:data, 0, :b]) # 102
551
+ Kind::Dig.(d, [:data, 0, 'b']) # 102
552
+
553
+ Kind::Dig.(d, [:ostruct, :d]) # 104
554
+ Kind::Dig.(d, [:data, 1, :d]) # 104
555
+ Kind::Dig.(d, [:data, 1, 'd']) # 104
556
+
557
+ Kind::Dig.(d, [:struct, :f]) # nil
558
+ Kind::Dig.(d, [:ostruct, :f]) # nil
559
+ Kind::Dig.(d, [:data, 0, :f]) # nil
560
+ Kind::Dig.(d, [:data, 1, :f]) # nil
561
+ ```
562
+
563
+ [⬆️ &nbsp;Back to Top](#changelog-)
564
+
565
+ 3.0.0 (2020-06-25)
566
+ ------------------
567
+
568
+ ### Breaking Changes
569
+
570
+ * [#31](https://github.com/serradura/kind/pull/31) - Change the `Kind::Maybe::Result#try()` behavior.
571
+ - 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.
572
+ ```ruby
573
+ # Examples using Kind::Maybe
574
+
575
+ Kind::Maybe['foo'].try(:upcase).value # "FOO"
576
+
577
+ Kind::Maybe[{}].try(:fetch, :number, 0).value # 0
578
+
579
+ Kind::Maybe[{number: 1}].try(:fetch, :number).value # 1
580
+
581
+ Kind::Maybe[' foo '].try { |value| value.strip }.value # "foo"
582
+
583
+ # Examples using Kind::Optional
584
+
585
+ Kind::Optional[1].try(:strip).value_or('') # ""
586
+
587
+ Kind::Optional[' Rodrigo '].try(:strip).value_or("") # 'Rodrigo'
588
+ ```
589
+
590
+ ### Added
591
+
592
+ * [#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.
593
+ ```ruby
594
+ Kind::Maybe[{}].try(:upcase) # => #<Kind::Maybe::None:0x0000... @value=nil>
595
+
596
+ Kind::Maybe[{}].try!(:upcase) # => NoMethodError (undefined method `upcase' for {}:Hash)
597
+ ```
598
+
599
+ [⬆️ &nbsp;Back to Top](#changelog-)
600
+
601
+ 2.3.0 (2020-06-24)
602
+ ------------------
603
+
604
+ ### Added
605
+
606
+ * [#30](https://github.com/serradura/kind/pull/30) - Add `Kind::Maybe.wrap()` as an alias for `Kind::Maybe.new()`.
607
+
608
+ * [#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.
609
+ ```ruby
610
+ Double = ->(arg) do
611
+ Kind::Optional(Numeric)
612
+ .wrap(arg)
613
+ .then { |number| number * 2 }
614
+ end
615
+ ```
616
+
617
+ [⬆️ &nbsp;Back to Top](#changelog-)
618
+
619
+ 2.2.0 (2020-06-23)
620
+ ------------------
621
+
622
+ ### Added
623
+
624
+ * [#29](https://github.com/serradura/kind/pull/29) - Improve the comparison of values with `Kind::Undefined`.
625
+ - 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.
626
+
627
+ [⬆️ &nbsp;Back to Top](#changelog-)
628
+
629
+ 2.1.0 (2020-05-12)
630
+ ------------------
631
+
632
+ ### Added
633
+
634
+ * [#28](https://github.com/serradura/kind/pull/28) - Allow passing multiple arguments to `Kind.of.<Type>.instance?(*args)`
635
+
636
+ * [#28](https://github.com/serradura/kind/pull/28) - Allow passing multiple arguments to `Kind.of.<Type>?(*args)`
637
+
638
+ * [#28](https://github.com/serradura/kind/pull/28) - Add `Kind::Some()` and `Kind::None()`. e.g:
639
+ ```ruby
640
+ Double = ->(arg) do
641
+ number = Kind::Of::Numeric.or_nil(arg)
642
+
643
+ Kind::Optional[number].then { |number| number * 2 }
644
+ end
645
+
646
+ Add = -> params do
647
+ a, b = Kind::Of::Hash(params, or: Empty::HASH).values_at(:a, :b)
648
+
649
+ return Kind::None unless Kind::Of::Numeric?(a, b)
650
+
651
+ Kind::Some(a + b)
652
+ end
653
+
654
+ Kind::Optional[a: 1, b: 2].then(&Add).value_or(0) # 3
655
+
656
+ Add.({}).some? # false
657
+
658
+ Add.(a: 1, b: '2').some? # false
659
+
660
+ Add.(a: 2, b: 2).then(&Double).value # 8
661
+ ```
662
+
663
+ * [#28](https://github.com/serradura/kind/pull/28) - Add `Kind.of?(<Type>, *args)` to check if one or many values are the expected kind.
664
+ ```ruby
665
+ Kind.of?(Numeric, 1, 2.0, 3) # true
666
+ Kind.of?(Numeric, 1, '2', 3) # false
667
+ ```
668
+
669
+ * [#28](https://github.com/serradura/kind/pull/28) - Make the `Kind.of?(<Type>)` returns a lambda that knows how to do the type verification.
670
+ ```ruby
671
+ [1, '2', 3].select(&Kind.of?(Numeric)) # [1, 3]
672
+ ```
673
+
674
+ ### Breaking Changes
675
+
676
+ * [#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.
677
+ ```ruby
678
+ [1, 2, 3].map(&Kind::Of::Numeric) # [1, 2, 3]
679
+
680
+ [1, '2', 3].map(&Kind::Of::Numeric) # Kind::Error ("2" expected to be a kind of Numeric)
681
+
682
+ [1, '2', 3].select(&Kind::Of::Numeric?) # [1, 3]
683
+ ```
684
+
685
+ [⬆️ &nbsp;Back to Top](#changelog-)
686
+
687
+ 2.0.0 (2020-05-07)
688
+ ------------------
689
+
690
+ ### Added
691
+
692
+ * [#24](https://github.com/serradura/kind/pull/24) - Improve the `kind: { is: }` validation to check the inheritance of *classes*/*modules*.
693
+
694
+ ### Breaking Changes
695
+
696
+ * [#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)`.
697
+
698
+ ### Removed
699
+
700
+ * [#24](https://github.com/serradura/kind/pull/24) - Remove `kind: { is_a: }` from `Kind::Validator`.
701
+ * [#24](https://github.com/serradura/kind/pull/24) - Remove `kind: { klass: }` from `Kind::Validator`.
702
+
703
+ [⬆️ &nbsp;Back to Top](#changelog-)
704
+
705
+ 1.9.0 (2020-05-06)
706
+ ------------------
707
+
708
+ ### Added
709
+
710
+ * [#23](https://github.com/serradura/kind/pull/23) - Add `Kind.of.<Type>.to_proc` as an alias for `Kind.of.<Type>.instance?`.
711
+ ```ruby
712
+ collection = [
713
+ {number: 1},
714
+ 'number 0',
715
+ {number: 2},
716
+ [0],
717
+ ]
718
+
719
+ collection
720
+ .select(&Kind.of.Hash)
721
+ .reduce(0) { |total, item| total + item[:number] }
722
+ ```
723
+
724
+ * [#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).
725
+ ```ruby
726
+ require 'kind/active_model/validation'
727
+
728
+ class Person
729
+ include ActiveModel::Validations
730
+
731
+ attr_reader :name, :age
732
+
733
+ validates :name, kind: { of: String }
734
+ validates :age, kind: { of: Integer }
735
+
736
+ def initialize(name:, age:)
737
+ @name, @age = name, age
738
+ end
739
+ end
740
+
741
+ person = Person.new(name: 'John', age: '21')
742
+
743
+ person.valid? # false
744
+
745
+ person.errors[:age] # ['must be a kind of: Integer']
746
+ ```
747
+
748
+ [⬆️ &nbsp;Back to Top](#changelog-)
749
+
750
+ 1.8.0 (2020-05-03)
751
+ ------------------
752
+
753
+ ### Added
754
+
755
+ * [#22](https://github.com/serradura/kind/pull/22) - `Kind.of.<Type>.instance?`returns a lambda when called without an argument.
756
+ ```ruby
757
+ collection = [
758
+ {number: 1},
759
+ 'number 0',
760
+ {number: 2},
761
+ [0],
762
+ ]
763
+
764
+ collection
765
+ .select(&Kind::Of::Hash.instance?)
766
+ .reduce(0) { |total, item| total + item[:number] }
767
+ ```
768
+
769
+ * [#22](https://github.com/serradura/kind/pull/22) - Add new methods `.as_optional`, `.as_maybe` in the type checkers.
770
+ ```ruby
771
+ def person_name(params)
772
+ Kind::Of::Hash
773
+ .as_optional(params)
774
+ .map { |data| data.values_at(:first_name, :last_name).compact }
775
+ .map { |first, last| "#{first} #{last}" if first && last }
776
+ .value_or { 'John Doe' }
777
+ end
778
+
779
+ person_name('') # "John Doe"
780
+ person_name(nil) # "John Doe"
781
+ person_name(first_name: 'Rodrigo') # "John Doe"
782
+ person_name(last_name: 'Serradura') # "John Doe"
783
+ person_name(first_name: 'Rodrigo', last_name: 'Serradura') # "Rodrigo Serradura"
784
+
785
+ # A lambda will be returned if these methods receive only one argument
786
+
787
+ collection = [
788
+ {number: 1},
789
+ 'number 0',
790
+ {number: 2},
791
+ [0],
792
+ ]
793
+
794
+ collection
795
+ .map(&Kind.of.Hash.as_optional)
796
+ .select(&:some?)
797
+ .reduce(0) { |total, item| total + item.value[:number] }
798
+ ```
799
+
800
+ [⬆️ &nbsp;Back to Top](#changelog-)
801
+
802
+ 1.7.0 (2020-05-03)
803
+ ------------------
804
+
805
+ ### Fixed
806
+
807
+ * [#20](https://github.com/serradura/kind/pull/20) - Fix the verification of modules using `Kind.is()`.
808
+
809
+ [⬆️ &nbsp;Back to Top](#changelog-)
810
+
811
+ 1.6.0 (2020-04-17)
812
+ ------------------
813
+
814
+ ### Added
815
+
816
+ * [#19](https://github.com/serradura/kind/pull/19) - Add aliases to perform the strict type verification (in registered type checkers).
817
+ ```ruby
818
+ # Kind.of.<Type>[]
819
+
820
+ Kind.of.Hash[nil] # raise Kind::Error, "nil expected to be a kind of Hash"
821
+ Kind.of.Hash[''] # raise Kind::Error, "'' expected to be a kind of Hash"
822
+ Kind.of.Hash[a: 1] # {a: 1}
823
+ Kind.of.Hash['', or: {}] # {}
824
+
825
+ # Kind.of.<Type>.instance()
826
+
827
+ Kind.of.Array.instance(nil) # raise Kind::Error, "nil expected to be a kind of Array"
828
+ Kind.of.Array.instance('') # raise Kind::Error, "'' expected to be a kind of Array"
829
+ Kind.of.Array.instance([]) # []
830
+ Kind.of.Array.instance('', or: []) # []
831
+ ```
832
+
833
+ * [#19](https://github.com/serradura/kind/pull/19) - Add `.or_undefined` method for any type checker.
834
+ ```ruby
835
+ Kind.of.String.or_undefined(nil) # Kind::Undefined
836
+ Kind.of.String.or_undefined("something") # "something"
837
+ ```
838
+
839
+ * [#19](https://github.com/serradura/kind/pull/19) - Allow a dynamical verification of types.
840
+ ```ruby
841
+ class User
842
+ end
843
+
844
+ class AdminUser < User
845
+ end
846
+
847
+ Kind.of(User, User.new) # #<User:0x0000...>
848
+ Kind.of(User, {}) # Kind::Error ({} expected to be a kind of User)
849
+ ```
850
+
851
+ * [#19](https://github.com/serradura/kind/pull/19) - Allow the creation of type checkers dynamically (without register one).
852
+ ```ruby
853
+ class User
854
+ end
855
+
856
+ kind_of_user = Kind.of(User)
857
+
858
+ kind_of_user[{}] # Kind::Error ({} expected to be a kind of User)
859
+ kind_of_user[User.new] # #<User:0x0000...>
860
+
861
+ kind_of_user.instance({}) # Kind::Error ({} expected to be a kind of User)
862
+ kind_of_user.instance(User.new) # #<User:0x0000...>
863
+
864
+ kind_of_user.instance?({}) # false
865
+ kind_of_user.instance?(User.new) # true
866
+
867
+ kind_of_user.class?(Hash) # false
868
+ kind_of_user.class?(User) # true
869
+
870
+ kind_of_user.or_undefined({}) # Kind::Undefined
871
+ kind_of_user.or_undefined(User.new) # #<User:0x0000...>
872
+ ```
873
+
874
+ * [#19](https://github.com/serradura/kind/pull/19) - Add a new type checkers.
875
+ - `Kind::Of::Set`
876
+ - `Kind::Of::Maybe`, `Kind::Of::Optional`
877
+
878
+ * [#19](https://github.com/serradura/kind/pull/19) - Add `Kind::Empty` with several constants having empty frozen objects.
879
+ ```ruby
880
+ Kind::Empty::SET
881
+ Kind::Empty::HASH
882
+ Kind::Empty::ARRAY
883
+ Kind::Empty::STRING
884
+
885
+ # If there isn't any constant named as Empty, the gem will use it to create an alias for Kind::Empty.
886
+
887
+ Empty::SET == Kind::Empty::SET
888
+ Empty::HASH == Kind::Empty::HASH
889
+ Empty::ARRAY == Kind::Empty::ARRAY
890
+ Empty::STRING == Kind::Empty::STRING
891
+ ```
892
+
893
+ ### Changes
894
+
895
+ * [#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"`
896
+ ```ruby
897
+ Kind::Undefined.to_s # "Kind::Undefined"
898
+ Kind::Undefined.inspect # "Kind::Undefined"
899
+ ```
900
+
901
+ [⬆️ &nbsp;Back to Top](#changelog-)
902
+
903
+ 1.5.0 (2020-04-12)
904
+ ------------------
905
+
906
+ ### Added
907
+
908
+ * [#18](https://github.com/serradura/kind/pull/18) - Refactor `Kind::Maybe`.
909
+
910
+ * [#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`.
911
+ ```ruby
912
+ Kind::Maybe::Value.some?(1) # true
913
+ Kind::Maybe::Value.some?(nil) # false
914
+ Kind::Maybe::Value.some?(Kind::Undefined) # false
915
+
916
+ Kind::Maybe::Value.none?(1) # false
917
+ Kind::Maybe::Value.none?(nil) # true
918
+ Kind::Maybe::Value.none?(Kind::Undefined) # true
919
+ ```
920
+
921
+ [⬆️ &nbsp;Back to Top](#changelog-)
922
+
923
+ 1.4.0 (2020-04-12)
924
+ ------------------
925
+
926
+ ### Added
927
+
928
+ * [#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`.
929
+
930
+ [⬆️ &nbsp;Back to Top](#changelog-)
931
+
932
+ 1.3.0 (2020-04-12)
933
+ ------------------
934
+
935
+ ### Added
936
+
937
+ * [#16](https://github.com/serradura/kind/pull/16) - Add a new special type checkers.
938
+ - `Kind::Of::Callable` for check if the given object respond to `call`.
939
+ - `Kind::Is::Callable` if the given value is a `class`, it will verifies if its `public_instance_methods.include?(:call)`.
940
+
941
+ [⬆️ &nbsp;Back to Top](#changelog-)
942
+
943
+ 1.2.0 (2020-04-12)
944
+ ------------------
945
+
946
+ ### Added
947
+
948
+ * [#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.
949
+ ```ruby
950
+ # Some value
951
+
952
+ optional =
953
+ Kind::Optional.new(2)
954
+ .map { |value| value * 2 }
955
+ .map { |value| value * 2 }
956
+
957
+ puts optional.value # 8
958
+ puts optional.some? # true
959
+ puts optional.none? # false
960
+
961
+ puts optional.value_or(0) # 8
962
+ puts optional.value_or { 0 } # 8
963
+
964
+ # None value
965
+
966
+ even_number = Kind::Optional.new(3).map { |n| n if n.even? }
967
+
968
+ even_number.none? # true
969
+
970
+ even_number.value_or(0) # 0
971
+
972
+ # Utility method
973
+
974
+ # Kind::Optional#try
975
+ # You could use the `#try` method to perform a method of the wrapped object and return its value.
976
+
977
+ Kind::Optional.new(' Rodrigo ').try(:strip) # "Rodrigo"
978
+
979
+ # Method aliases
980
+
981
+ # Kind::Optional[] is an alias for Kind::Optional.new
982
+ Kind::Optional[2].map { |n| n if n.even? }.value_or(0) # 2
983
+
984
+ # Kind::Optional::Result#then is an alias for Kind::Optional::Result#map
985
+ Kind::Optional[1].then { |n| n if n.even? }.value_or(0) # 0
986
+ ```
987
+
988
+ * [#15](https://github.com/serradura/kind/pull/15) - Add new methods to `Kind::Undefined`.
989
+ ```ruby
990
+ Kind::Undefined.to_s # 'Undefined'
991
+ Kind::Undefined.inspect # 'Undefined'
992
+
993
+ Kind::Undefined.clone # #<Kind::Undefined:0x0000...>
994
+ Kind::Undefined.dup # #<Kind::Undefined:0x0000...>
995
+
996
+ Kind::Undefined.clone == Kind::Undefined # true
997
+ Kind::Undefined.clone === Kind::Undefined # true
998
+
999
+ Kind::Undefined.dup == Kind::Undefined # true
1000
+ Kind::Undefined.dup === Kind::Undefined # true
1001
+
1002
+ value = Kind::Undefined
1003
+
1004
+ Kind::Undefined.default(value, 1) # 1
1005
+ ```
1006
+
1007
+ [⬆️ &nbsp;Back to Top](#changelog-)
1008
+
1009
+ 1.1.0 (2020-04-09)
1010
+ ------------------
1011
+
1012
+ ### Added
1013
+
1014
+ * [#14](https://github.com/serradura/kind/pull/14) - Add `Kind::Undefined` representing an undefined value to contrast with `nil`.
1015
+
1016
+ ### Fixed
1017
+
1018
+ * [#14](https://github.com/serradura/kind/pull/14) - Raise a `Kind::Error` if `nil` is the argument of any strict type checker.
1019
+ ```ruby
1020
+ Kind.of.Hash(nil) # raise Kind::Error, "nil expected to be a kind of Hash"
1021
+ ```
1022
+
1023
+ [⬆️ &nbsp;Back to Top](#changelog-)
1024
+
1025
+ 1.0.0 (2020-03-16)
1026
+ ------------------
1027
+
1028
+ ### Added
1029
+
1030
+ * [#12](https://github.com/serradura/kind/pull/12) - Register type checkers respecting their namespaces.
1031
+ ```ruby
1032
+ module Account
1033
+ class User
1034
+ Kind::Types.add(self)
1035
+ end
1036
+ end
1037
+
1038
+ module Account
1039
+ class User
1040
+ class Membership
1041
+ Kind::Types.add(self)
1042
+ end
1043
+ end
1044
+ end
1045
+
1046
+ account_user = Account::User.new
1047
+
1048
+ Kind.of.Account::User(account_user) # #<Account::User:0x0000...>
1049
+ Kind.of.Account::User({}) # Kind::Error ({} expected to be a kind of Account::User)
1050
+
1051
+ Kind.of.Account::User.or_nil({}) # nil
1052
+
1053
+ Kind.of.Account::User.instance?({}) # false
1054
+ Kind.of.Account::User.instance?(account_user) # true
1055
+
1056
+ Kind.of.Account::User.class?(Hash) # false
1057
+ Kind.of.Account::User.class?(Account::User) # true
1058
+
1059
+ # ---
1060
+
1061
+ membership = Account::User::Membership.new
1062
+
1063
+ Kind.of.Account::User::Membership(membership) # #<Account::User::Membership:0x0000...>
1064
+ Kind.of.Account::User::Membership({}) # Kind::Error ({} expected to be a kind of Account::User::Membership)
1065
+
1066
+ Kind.of.Account::User::Membership.or_nil({}) # nil
1067
+
1068
+ Kind.of.Account::User::Membership.instance?({}) # false
1069
+ Kind.of.Account::User::Membership.instance?(membership) # true
1070
+
1071
+ Kind.of.Account::User::Membership.class?(Hash) # false
1072
+ Kind.of.Account::User::Membership.class?(Account::User::Membership) # true
1073
+ ```
1074
+
1075
+ [⬆️ &nbsp;Back to Top](#changelog-)
1076
+
1077
+ 0.6.0 (2020-01-06)
1078
+ ------------------
1079
+
1080
+ ### Added
1081
+
1082
+ * [#11](https://github.com/serradura/kind/pull/11) - Register the `Queue` (`Thread::Queue`) type checker. This registering creates:
1083
+ - `Kind::Of::Queue`, `Kind::Is::Queue`
1084
+
1085
+ [⬆️ &nbsp;Back to Top](#changelog-)
1086
+
1087
+ 0.5.0 (2020-01-04)
1088
+ ------------------
1089
+
1090
+ ### Added
1091
+
1092
+ * [#4](https://github.com/serradura/kind/pull/4) - Allow defining a default value when the verified object is `nil`.
1093
+ ```ruby
1094
+ Kind.of.Hash(nil, or: {}) # {}
1095
+ ```
1096
+
1097
+ [⬆️ &nbsp;Back to Top](#changelog-)
1098
+
1099
+ 0.4.0 (2020-01-03)
1100
+ ------------------
1101
+
1102
+ ### Added
1103
+
1104
+ * [#3](https://github.com/serradura/kind/pull/3) - Require `2.2.0` as the minimum Ruby version.
1105
+
1106
+ [⬆️ &nbsp;Back to Top](#changelog-)
1107
+
1108
+ 0.3.0 (2020-01-03)
1109
+ ------------------
1110
+
1111
+ ### Added
1112
+
1113
+ * [#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)
1114
+ ```ruby
1115
+ class User
1116
+ end
1117
+
1118
+ user = User.new
1119
+
1120
+ UserKind = Kind::Checker.new(User)
1121
+
1122
+ UserKind.class?(User) # true
1123
+ UserKind.class?(String) # false
1124
+
1125
+ UserKind.instance?(user) # true
1126
+ UserKind.instance?(1) # false
1127
+
1128
+ UserKind.or_nil(user) # #<User:0x0000...>
1129
+ UserKind.or_nil(1) # nil
1130
+ ```
1131
+
1132
+ ### Breaking Changes
1133
+
1134
+ * [#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.
1135
+
1136
+ [⬆️ &nbsp;Back to Top](#changelog-)
1137
+
1138
+ 0.2.0 (2020-01-02)
1139
+ ------------------
1140
+
1141
+ ### Added
1142
+
1143
+ * [#1](https://github.com/serradura/kind/pull/1) - Register type checkers for several Ruby classes/modules. (PR: #1)
1144
+ - **Classes:**
1145
+ - `Kind::Of::Symbol` , `Kind::Is::Symbol`
1146
+ - `Kind::Of::Numeric` , `Kind::Is::Numeric`
1147
+ - `Kind::Of::Integer` , `Kind::Is::Integer`
1148
+ - `Kind::Of::Float` , `Kind::Is::Float`
1149
+ - `Kind::Of::Regexp` , `Kind::Is::Regexp`
1150
+ - `Kind::Of::Time` , `Kind::Is::Time`
1151
+ - `Kind::Of::Array` , `Kind::Is::Array`
1152
+ - `Kind::Of::Range` , `Kind::Is::Range`
1153
+ - `Kind::Of::Hash` , `Kind::Is::Hash`
1154
+ - `Kind::Of::Struct` , `Kind::Is::Struct`
1155
+ - `Kind::Of::Enumerator`, `Kind::Is::Enumerator`
1156
+ - `Kind::Of::Method` , `Kind::Is::Method`
1157
+ - `Kind::Of::Proc` , `Kind::Is::Proc`
1158
+ - `Kind::Of::IO` , `Kind::Is::IO`
1159
+ - `Kind::Of::File` , `Kind::Is::File`
1160
+ - **Modules:**
1161
+ - `Kind::Of::Enumerable`, `Kind::Is::Enumerable`
1162
+ - `Kind::Of::Comparable`, `Kind::Is::Comparable`
1163
+
1164
+ * [#1](https://github.com/serradura/kind/pull/1) - Create special type checkers.
1165
+ - `Kind::Of::Boolean` for check if the given object is `true` or `false`.
1166
+ - `Kind::Of::Lambda` for check if the given object is a `lambda`.
1167
+ - `Kind::Of::Module` for check if the given object is a *module*.
1168
+
1169
+ [⬆️ &nbsp;Back to Top](#changelog-)
1170
+
1171
+ 0.1.0 (2019-12-26)
1172
+ ------------------
1173
+
1174
+ ### Added
1175
+
1176
+ * Require `2.3.0` as the minimum Ruby version.
1177
+
1178
+ * `Kind::Error` for defining type checkers errors.
1179
+
1180
+ * `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.
1181
+ ```ruby
1182
+ Kind::Of.(String, '') # ''
1183
+ Kind::Of.(String, 1) # Kind::Error (1 expected to be a kind of String)
1184
+ ```
1185
+
1186
+ * `Kind::Of::Class()` for check if the given object is a *class*.
1187
+
1188
+ * `Kind::Is.call()` for check if the first kind is equal or a parent class/module of the second one.
1189
+ ```ruby
1190
+ Kind::Is.(String, String) # true
1191
+ Kind::Is.(Symbol, String) # false
1192
+ ```
1193
+
1194
+ * `Kind.of` is a shortcut for `Kind::Of`.
1195
+
1196
+ * `Kind.is` is a shortcut for `Kind::Is`.
1197
+
1198
+ * `Kind::Types.add` allows the registering of new type checkers.
1199
+ ```ruby
1200
+ # Registering Symbol
1201
+
1202
+ Kind::Type.add(Symbol)
1203
+
1204
+ # Adds a method in the Kind::Is module
1205
+
1206
+ class MySymbol < Symbol
1207
+ end
1208
+
1209
+ Kind.is.Symbol(Symbol) # true
1210
+ Kind.is.Symbol(MySymbol) # true
1211
+ Kind.is.Symbol(String) # false
1212
+
1213
+ # Adds a method in the Kind::Of module
1214
+
1215
+ Kind.of.Symbol(:a) # :a
1216
+ Kind.of.Symbol(1) # Kind::Error (1 expected to be a kind of Symbol)
1217
+
1218
+ # Creates a module in Kind::Of with type checking methods related to the given kind.
1219
+
1220
+ Kind::Of::Symbol.class?(Symbol) # true
1221
+ Kind::Of::Symbol.instance?(:a) # true
1222
+ Kind::Of::Symbol.or_nil(:b) # :b
1223
+ Kind::Of::Symbol.or_nil(1) # nil
1224
+ ```
1225
+
1226
+ * Register the `String` and `Hash` type checkers. This registering creates:
1227
+ - `Kind::Of::Hash`, `Kind::Is::Hash`
1228
+ - `Kind::Of::String`, `Kind::Is::String`
1229
+
1230
+ [⬆️ &nbsp;Back to Top](#changelog-)