kind 3.0.0 → 5.0.0

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