kind 2.3.0 → 4.1.0

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