kind 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +207 -50
- data/lib/kind.rb +203 -21
- data/lib/kind/checker.rb +29 -3
- data/lib/kind/empty.rb +21 -0
- data/lib/kind/error.rb +2 -2
- data/lib/kind/is.rb +5 -10
- data/lib/kind/maybe.rb +12 -6
- data/lib/kind/of.rb +3 -35
- data/lib/kind/types.rb +29 -18
- data/lib/kind/undefined.rb +1 -1
- data/lib/kind/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7dd5fb43e1dfd7c69d1a7ae3ffbf875769a675bbf7f8682bcfc9c8b3aa45c739
|
4
|
+
data.tar.gz: db15639b472a6b75d990b83f8b317ba7919f820b36e9a49a9141d387c6623768
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd548af2d4a1d93d5c4f2ef344318d35f140e1a7e4e82289897286ed6563004d7a8cc4577f94fb38b41e194c093ababf05e9d620b96a4ed420277dc94e154d87
|
7
|
+
data.tar.gz: 24ef063c68dc91f160b24a6d16992582cef56902b98bbc8fc54931c4e0076166f40c4795481e86e21cf546ae030f4c5aba49259785e8c00e46ab77debe374a27
|
data/README.md
CHANGED
@@ -21,16 +21,22 @@ One of the goals of this project is to do simple type checking like `"some strin
|
|
21
21
|
- [Verifying the kind of some object](#verifying-the-kind-of-some-object)
|
22
22
|
- [Verifying the kind of some class/module](#verifying-the-kind-of-some-classmodule)
|
23
23
|
- [How to create a new type checker?](#how-to-create-a-new-type-checker)
|
24
|
-
- [
|
25
|
-
- [
|
24
|
+
- [Creating/Verifiyng type checkers dynamically](#creatingverifiyng-type-checkers-dynamically)
|
25
|
+
- [Registering new (custom) type checkers](#registering-new-custom-type-checkers)
|
26
|
+
- [What happens if a custom type checker has a namespace?](#what-happens-if-a-custom-type-checker-has-a-namespace)
|
27
|
+
- [Type checkers](#type-checkers)
|
28
|
+
- [Classes' type checker](#classes-type-checker)
|
29
|
+
- [Module type checkers](#module-type-checkers)
|
26
30
|
- [Special type checkers](#special-type-checkers)
|
27
31
|
- [Kind.of](#kindof)
|
28
|
-
- [Kind.is](#kindis)
|
29
32
|
- [Kind::Undefined](#kindundefined)
|
33
|
+
- [Kind.of.<Type>.or_undefined()](#kindoftypeor_undefined)
|
30
34
|
- [Kind::Maybe](#kindmaybe)
|
31
35
|
- [Kind::Maybe[] and Kind::Maybe#then](#kindmaybe-and-kindmaybethen)
|
32
36
|
- [Kind::Maybe#try](#kindmaybetry)
|
37
|
+
- [Kind.of.Maybe()](#kindofmaybe)
|
33
38
|
- [Kind::Optional](#kindoptional)
|
39
|
+
- [Kind::Empty](#kindempty)
|
34
40
|
- [Development](#development)
|
35
41
|
- [Contributing](#contributing)
|
36
42
|
- [License](#license)
|
@@ -77,15 +83,12 @@ By default, basic verifications are strict. So, when you perform `Kind.of.Hash(v
|
|
77
83
|
|
78
84
|
```ruby
|
79
85
|
Kind.of.Hash(nil) # raise Kind::Error, "nil expected to be a kind of Hash"
|
80
|
-
|
81
86
|
Kind.of.Hash('') # raise Kind::Error, "'' expected to be a kind of Hash"
|
82
|
-
|
83
87
|
Kind.of.Hash({a: 1}) # {a: 1}
|
84
88
|
|
85
89
|
# ---
|
86
90
|
|
87
91
|
Kind.of.Boolean(nil) # raise Kind::Error, "nil expected to be a kind of Boolean"
|
88
|
-
|
89
92
|
Kind.of.Boolean(true) # true
|
90
93
|
Kind.of.Boolean(false) # false
|
91
94
|
```
|
@@ -107,11 +110,8 @@ As an alternative syntax, you can use the `Kind::Of` instead of the method. e.g:
|
|
107
110
|
But if you don't need a strict type verification, use the `.or_nil`method
|
108
111
|
|
109
112
|
```ruby
|
110
|
-
Kind.of.Hash.or_nil('')
|
111
|
-
#
|
112
|
-
|
113
|
-
Kind.of.Hash.or_nil({a: 1})
|
114
|
-
# {a: 1}
|
113
|
+
Kind.of.Hash.or_nil('') # nil
|
114
|
+
Kind.of.Hash.or_nil({a: 1}) # {a: 1}
|
115
115
|
|
116
116
|
# ---
|
117
117
|
|
@@ -132,6 +132,22 @@ Kind.of.Boolean.instance?(true) # true
|
|
132
132
|
Kind.of.Boolean.instance?(false) # true
|
133
133
|
```
|
134
134
|
|
135
|
+
Also, there are aliases to perform the strict type verification. e.g:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
Kind.of.Hash[nil] # raise Kind::Error, "nil expected to be a kind of Hash"
|
139
|
+
Kind.of.Hash[''] # raise Kind::Error, "'' expected to be a kind of Hash"
|
140
|
+
Kind.of.Hash[a: 1] # {a: 1}
|
141
|
+
Kind.of.Hash['', or: {}] # {}
|
142
|
+
|
143
|
+
# or
|
144
|
+
|
145
|
+
Kind.of.Hash.instance(nil) # raise Kind::Error, "nil expected to be a kind of Hash"
|
146
|
+
Kind.of.Hash.instance('') # raise Kind::Error, "'' expected to be a kind of Hash"
|
147
|
+
Kind.of.Hash.instance(a: 1) # {a: 1}
|
148
|
+
Kind.of.Hash.instance('', or: {}) # {}
|
149
|
+
```
|
150
|
+
|
135
151
|
### Verifying the kind of some class/module
|
136
152
|
|
137
153
|
You can use `Kind.is` to verify if some class has the expected type as its ancestor.
|
@@ -156,6 +172,57 @@ Kind.of.Hash.class?(ActiveSupport::HashWithIndifferentAccess) # true
|
|
156
172
|
|
157
173
|
### How to create a new type checker?
|
158
174
|
|
175
|
+
There are two ways to do this, you can create type checkers dynamically or register new ones.
|
176
|
+
|
177
|
+
#### Creating/Verifiyng type checkers dynamically
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
class User
|
181
|
+
end
|
182
|
+
|
183
|
+
user = User.new
|
184
|
+
|
185
|
+
# ------------------------ #
|
186
|
+
# Verifiyng the value kind #
|
187
|
+
# ------------------------ #
|
188
|
+
|
189
|
+
Kind.of(User, user) # <User ...>
|
190
|
+
Kind.of(User, {}) # Kind::Error ({} expected to be a kind of User)
|
191
|
+
|
192
|
+
Kind.of(Hash, {}) # {}
|
193
|
+
Kind.of(Hash, user) # Kind::Error (<User ...> expected to be a kind of Hash)
|
194
|
+
|
195
|
+
# ---------------------------------- #
|
196
|
+
# Creating type checkers dynamically #
|
197
|
+
# ---------------------------------- #
|
198
|
+
|
199
|
+
kind_of_user = Kind.of(User)
|
200
|
+
|
201
|
+
kind_of_user.or_nil({}) # nil
|
202
|
+
|
203
|
+
kind_of_user.instance?({}) # false
|
204
|
+
kind_of_user.instance?(User) # true
|
205
|
+
|
206
|
+
kind_of_user.class?(Hash) # false
|
207
|
+
kind_of_user.class?(User) # true
|
208
|
+
|
209
|
+
# Create type checkers dynamically is cheap
|
210
|
+
# because of a singleton object is created to be available for use.
|
211
|
+
|
212
|
+
kind_of_user.object_id == Kind.of(User).object_id # true
|
213
|
+
|
214
|
+
# --------------------------------------------- #
|
215
|
+
# Kind.is() can be used to check a class/module #
|
216
|
+
# --------------------------------------------- #
|
217
|
+
|
218
|
+
class Admin < User
|
219
|
+
end
|
220
|
+
|
221
|
+
Kind.is(Admin, User) # true
|
222
|
+
```
|
223
|
+
|
224
|
+
#### Registering new (custom) type checkers
|
225
|
+
|
159
226
|
Use `Kind::Types.add()`. e.g:
|
160
227
|
|
161
228
|
```ruby
|
@@ -191,7 +258,7 @@ Kind.of.User.class?(User) # true
|
|
191
258
|
|
192
259
|
[⬆️ Back to Top](#table-of-contents-)
|
193
260
|
|
194
|
-
|
261
|
+
##### What happens if a custom type checker has a namespace?
|
195
262
|
|
196
263
|
The type checker will preserve the namespace. ;)
|
197
264
|
|
@@ -210,57 +277,63 @@ module Account
|
|
210
277
|
end
|
211
278
|
end
|
212
279
|
|
213
|
-
Kind.of.Account::User(
|
280
|
+
Kind.of.Account::User({}) # Kind::Error ({} expected to be a kind of Account::User)
|
214
281
|
|
215
|
-
Kind.of.Account::User(
|
282
|
+
Kind.of.Account::User(Account::User.new) # #<Account::User:0x0000...>
|
216
283
|
|
217
284
|
Kind.of.Account::User.or_nil({}) # nil
|
218
285
|
|
219
|
-
Kind.of.Account::User.instance?({})
|
220
|
-
Kind.of.Account::User.instance?(User) # true
|
286
|
+
Kind.of.Account::User.instance?({}) # false
|
287
|
+
Kind.of.Account::User.instance?(Account::User.new) # true
|
221
288
|
|
222
|
-
Kind.of.Account::User.class?(Hash)
|
223
|
-
Kind.of.Account::User.class?(User) # true
|
289
|
+
Kind.of.Account::User.class?(Hash) # false
|
290
|
+
Kind.of.Account::User.class?(Account::User) # true
|
224
291
|
|
225
292
|
# ---
|
226
293
|
|
227
|
-
Kind.of.Account::User::Membership(
|
294
|
+
Kind.of.Account::User::Membership({}) # Kind::Error ({} expected to be a kind of Account::User::Membership)
|
228
295
|
|
229
|
-
Kind.of.Account::User::Membership(
|
296
|
+
Kind.of.Account::User::Membership(Account::User::Membership.new) # #<Account::User::Membership:0x0000...>
|
230
297
|
|
231
298
|
Kind.of.Account::User::Membership.or_nil({}) # nil
|
232
299
|
|
233
|
-
Kind.of.Account::User::Membership.instance?({})
|
234
|
-
Kind.of.Account::User::Membership.instance?(User) # true
|
300
|
+
Kind.of.Account::User::Membership.instance?({}) # false
|
301
|
+
Kind.of.Account::User::Membership.instance?(Account::User::Membership.new) # true
|
235
302
|
|
236
|
-
Kind.of.Account::User::Membership.class?(Hash)
|
237
|
-
Kind.of.Account::User::Membership.class?(User)
|
303
|
+
Kind.of.Account::User::Membership.class?(Hash) # false
|
304
|
+
Kind.of.Account::User::Membership.class?(Account::User::Membership) # true
|
238
305
|
```
|
239
306
|
|
240
307
|
[⬆️ Back to Top](#table-of-contents-)
|
241
308
|
|
242
|
-
##
|
309
|
+
## Type checkers
|
243
310
|
|
244
311
|
The list of types (classes and modules) available to use with `Kind.of.*` or `Kind.is.*` are:
|
245
312
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
313
|
+
## Classes' type checker
|
314
|
+
|
315
|
+
- `Kind.of.String`
|
316
|
+
- `Kind.of.Symbol`
|
317
|
+
- `Kind.of.Numeric`
|
318
|
+
- `Kind.of.Integer`
|
319
|
+
- `Kind.of.Float`
|
320
|
+
- `Kind.of.Regexp`
|
321
|
+
- `Kind.of.Time`
|
322
|
+
- `Kind.of.Array`
|
323
|
+
- `Kind.of.Range`
|
324
|
+
- `Kind.of.Hash`
|
325
|
+
- `Kind.of.Struct`
|
326
|
+
- `Kind.of.Enumerator`
|
327
|
+
- `Kind.of.Set`
|
328
|
+
- `Kind.of.Method`
|
329
|
+
- `Kind.of.Proc`
|
330
|
+
- `Kind.of.IO`
|
331
|
+
- `Kind.of.File`
|
332
|
+
|
333
|
+
### Module type checkers
|
334
|
+
|
335
|
+
- `Kind.of.Enumerable`
|
336
|
+
- `Kind.of.Comparable`
|
264
337
|
|
265
338
|
### Special type checkers
|
266
339
|
|
@@ -270,14 +343,10 @@ The list of types (classes and modules) available to use with `Kind.of.*` or `Ki
|
|
270
343
|
- `Kind.of.Module()`
|
271
344
|
- `Kind.of.Lambda()`
|
272
345
|
- `Kind.of.Boolean()`
|
273
|
-
- `Kind.of.Callable()`
|
274
|
-
|
275
|
-
#### Kind.is
|
346
|
+
- `Kind.of.Callable()`: verifies if the given value `respond_to?(:call)` or if it's a class/module and if its `public_instance_methods.include?(:call)`.
|
347
|
+
- `Kind.of.Maybe()` or its alias `Kind.of.Optional()`
|
276
348
|
|
277
|
-
|
278
|
-
- `Kind.is.Module()`
|
279
|
-
- `Kind.is.Boolean()`
|
280
|
-
- `Kind.is.Callable()`: verifies if the given value `respond_to?(:call)` or if it's a class/module and if its `public_instance_methods.include?(:call)`.
|
349
|
+
PS: Remember, you can use the `Kind.is.*` method to check if some given value is a class/module with all type checkers above.
|
281
350
|
|
282
351
|
[⬆️ Back to Top](#table-of-contents-)
|
283
352
|
|
@@ -287,6 +356,15 @@ The [`Kind::Undefined`](https://github.com/serradura/kind/blob/834f6b8ebdc737de8
|
|
287
356
|
|
288
357
|
If you are interested, check out [the tests](https://github.com/serradura/kind/blob/834f6b8ebdc737de8e5628986585f30c1a5aa41b/test/kind/undefined_test.rb) to understand its methods.
|
289
358
|
|
359
|
+
### Kind.of.<Type>.or_undefined()
|
360
|
+
|
361
|
+
If you interested in use `Kind::Undefined` you can use the method `.or_undefined` with any of the [available type checkers](#type-checkers). e.g:
|
362
|
+
|
363
|
+
```ruby
|
364
|
+
Kind.of.String.or_undefined(nil) # Kind::Undefined
|
365
|
+
Kind.of.String.or_undefined("something") # "something"
|
366
|
+
```
|
367
|
+
|
290
368
|
[⬆️ Back to Top](#table-of-contents-)
|
291
369
|
|
292
370
|
## Kind::Maybe
|
@@ -384,6 +462,36 @@ p Kind::Maybe[object].try { |value| value.upcase } # nil
|
|
384
462
|
|
385
463
|
[⬆️ Back to Top](#table-of-contents-)
|
386
464
|
|
465
|
+
### Kind.of.Maybe()
|
466
|
+
|
467
|
+
You can use the `Kind.of.Maybe()` to know if the given value is a kind of `Kind::Maybe`object. e.g:
|
468
|
+
|
469
|
+
```ruby
|
470
|
+
def double(maybe_number)
|
471
|
+
Kind.of.Maybe(maybe_number)
|
472
|
+
.map { |value| value * 2 }
|
473
|
+
.value_or(0)
|
474
|
+
end
|
475
|
+
|
476
|
+
number = Kind::Maybe[4]
|
477
|
+
|
478
|
+
puts double(number) # 8
|
479
|
+
|
480
|
+
# -------------------------------------------------------#
|
481
|
+
# All the type checker methods are available to use too. #
|
482
|
+
# -------------------------------------------------------#
|
483
|
+
|
484
|
+
Kind.of.Maybe.instance?(number) # true
|
485
|
+
|
486
|
+
Kind.of.Maybe.or_nil(number) # <Kind::Maybe::Some @value=4 ...>
|
487
|
+
|
488
|
+
Kind.of.Maybe.instance(number) # <Kind::Maybe::Some @value=4 ...>
|
489
|
+
Kind.of.Maybe.instance(4) # Kind::Error (4 expected to be a kind of Kind::Maybe::Result)
|
490
|
+
|
491
|
+
Kind.of.Maybe[number] # <Kind::Maybe::Some @value=4 ...>
|
492
|
+
Kind.of.Maybe[4] # Kind::Error (4 expected to be a kind of Kind::Maybe::Result)
|
493
|
+
```
|
494
|
+
|
387
495
|
### Kind::Optional
|
388
496
|
|
389
497
|
The `Kind::Optional` constant is an alias for `Kind::Maybe`. e.g:
|
@@ -409,6 +517,55 @@ result2 =
|
|
409
517
|
puts result2 # 35
|
410
518
|
```
|
411
519
|
|
520
|
+
PS: The `Kind.of.Optional` is available to check if some value is a `Kind::Optional`.
|
521
|
+
|
522
|
+
[⬆️ Back to Top](#table-of-contents-)
|
523
|
+
|
524
|
+
### Kind::Empty
|
525
|
+
|
526
|
+
There is a common need to define default argument values. In case you don't know, depending on the argument data type, when a method is invoked a new object will be created in the program memory to fills some default argument value. e.g:
|
527
|
+
|
528
|
+
```ruby
|
529
|
+
def something(params = {})
|
530
|
+
params.object_id
|
531
|
+
end
|
532
|
+
|
533
|
+
puts something # 70312470300460
|
534
|
+
puts something # 70312470295800
|
535
|
+
puts something # 70312470278400
|
536
|
+
puts something # 70312470273800
|
537
|
+
```
|
538
|
+
|
539
|
+
So, to avoid an unnecessary allocation in memory, the `kind` gem exposes some frozen objects to be used as default values.
|
540
|
+
|
541
|
+
- `Kind::Empty::SET`
|
542
|
+
- `Kind::Empty::HASH`
|
543
|
+
- `Kind::Empty::ARRAY`
|
544
|
+
- `Kind::Empty::STRING`
|
545
|
+
|
546
|
+
Usage example:
|
547
|
+
|
548
|
+
```ruby
|
549
|
+
def do_something(value, with_options: Kind::Empty::HASH)
|
550
|
+
# ...
|
551
|
+
end
|
552
|
+
```
|
553
|
+
|
554
|
+
One last thing, if there is no constant declared as Empty, the `kind` gem will define `Empty` as an alias for `Kind::Empty`. Knowing this, the previous example could be written like this:
|
555
|
+
|
556
|
+
```ruby
|
557
|
+
def do_something(value, with_options: Empty::HASH)
|
558
|
+
# ...
|
559
|
+
end
|
560
|
+
```
|
561
|
+
|
562
|
+
Follows the list of constants, if the alias is available to be created:
|
563
|
+
|
564
|
+
- `Empty::SET`
|
565
|
+
- `Empty::HASH`
|
566
|
+
- `Empty::ARRAY`
|
567
|
+
- `Empty::STRING`
|
568
|
+
|
412
569
|
[⬆️ Back to Top](#table-of-contents-)
|
413
570
|
|
414
571
|
## Development
|
data/lib/kind.rb
CHANGED
@@ -1,38 +1,67 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'kind/version'
|
4
|
+
|
5
|
+
require 'kind/empty'
|
4
6
|
require 'kind/undefined'
|
5
7
|
require 'kind/maybe'
|
8
|
+
|
6
9
|
require 'kind/error'
|
10
|
+
require 'kind/of'
|
7
11
|
require 'kind/is'
|
8
12
|
require 'kind/checker'
|
9
|
-
require 'kind/of'
|
10
13
|
require 'kind/types'
|
11
14
|
|
12
15
|
module Kind
|
13
|
-
|
14
|
-
|
16
|
+
WRONG_NUMBER_OF_ARGUMENTS = 'wrong number of arguments (given 1, expected 2)'.freeze
|
17
|
+
|
18
|
+
private_constant :WRONG_NUMBER_OF_ARGUMENTS
|
19
|
+
|
20
|
+
def self.is(expected = Undefined, object = Undefined)
|
21
|
+
return Is if expected == Undefined && object == Undefined
|
22
|
+
|
23
|
+
return Kind::Is.(expected, object) if object != Undefined
|
24
|
+
|
25
|
+
raise ArgumentError, WRONG_NUMBER_OF_ARGUMENTS
|
26
|
+
end
|
27
|
+
|
28
|
+
MODULE_OR_CLASS = 'Module/Class'.freeze
|
15
29
|
|
16
|
-
|
17
|
-
[
|
18
|
-
String, Symbol, Numeric, Integer, Float, Regexp, Time,
|
19
|
-
Array, Range, Hash, Struct, Enumerator,
|
20
|
-
Method, Proc,
|
21
|
-
IO, File
|
22
|
-
].each { |klass| Types.add(klass) }
|
30
|
+
private_constant :MODULE_OR_CLASS
|
23
31
|
|
24
|
-
|
32
|
+
def self.of(kind = Undefined, object = Undefined)
|
33
|
+
return Of if kind == Undefined && object == Undefined
|
25
34
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
35
|
+
return Kind::Of.(kind, object) if object != Undefined
|
36
|
+
|
37
|
+
__checkers__[kind] ||= begin
|
38
|
+
kind_name = kind.name
|
39
|
+
|
40
|
+
if Kind::Of.const_defined?(kind_name, false)
|
41
|
+
Kind::Of.const_get(kind_name)
|
42
|
+
else
|
43
|
+
Checker.new(Kind::Of.(Module, kind, MODULE_OR_CLASS))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private_class_method def self.__checkers__
|
49
|
+
@__checkers__ ||= {}
|
50
|
+
end
|
30
51
|
|
31
52
|
# --------------------- #
|
32
53
|
# Special type checkers #
|
33
54
|
# --------------------- #
|
34
55
|
|
35
56
|
module Is
|
57
|
+
def self.Class(value)
|
58
|
+
value.is_a?(::Class)
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.Module(value)
|
62
|
+
value == ::Module || (value.is_a?(::Module) && !self.Class(value))
|
63
|
+
end
|
64
|
+
|
36
65
|
def self.Boolean(value)
|
37
66
|
klass = Kind.of.Class(value)
|
38
67
|
klass <= TrueClass || klass <= FalseClass
|
@@ -44,9 +73,73 @@ module Kind
|
|
44
73
|
end
|
45
74
|
|
46
75
|
module Of
|
76
|
+
# -- Class
|
77
|
+
|
78
|
+
def self.Class(object = Undefined)
|
79
|
+
return Class if object == Undefined
|
80
|
+
|
81
|
+
self.call(::Class, object)
|
82
|
+
end
|
83
|
+
|
84
|
+
const_set(:Class, ::Module.new do
|
85
|
+
extend Checkable
|
86
|
+
|
87
|
+
def self.__kind; ::Class; end
|
88
|
+
|
89
|
+
def self.class?(value); Kind::Is.Class(value); end
|
90
|
+
|
91
|
+
def self.instance?(value); class?(value); end
|
92
|
+
end)
|
93
|
+
|
94
|
+
# -- Module
|
95
|
+
|
96
|
+
def self.Module(object = Undefined)
|
97
|
+
return Module if object == Undefined
|
98
|
+
|
99
|
+
self.call(::Module, object)
|
100
|
+
end
|
101
|
+
|
102
|
+
const_set(:Module, ::Module.new do
|
103
|
+
extend Checkable
|
104
|
+
|
105
|
+
def self.__kind; ::Module; end
|
106
|
+
|
107
|
+
def self.__kind_error(value)
|
108
|
+
raise Kind::Error.new('Module'.freeze, value)
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.__kind_of(value)
|
112
|
+
return value if Kind::Is.Module(value)
|
113
|
+
|
114
|
+
__kind_error(value)
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.__kind_undefined(value)
|
118
|
+
__kind_error(Kind::Undefined) if value == Kind::Undefined
|
119
|
+
|
120
|
+
yield
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.class?(value); Kind::Is.Module(value); end
|
124
|
+
|
125
|
+
def self.instance(value, options = Empty::HASH)
|
126
|
+
default = options[:or]
|
127
|
+
|
128
|
+
if ::Kind::Maybe::Value.none?(default)
|
129
|
+
__kind_undefined(value) { __kind_of(value) }
|
130
|
+
else
|
131
|
+
return value if instance?(value)
|
132
|
+
|
133
|
+
__kind_undefined(default) { __kind_of(default) }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.instance?(value); class?(value); end
|
138
|
+
end)
|
139
|
+
|
47
140
|
# -- Boolean
|
48
141
|
|
49
|
-
def self.Boolean(object = Undefined, options =
|
142
|
+
def self.Boolean(object = Undefined, options = Empty::HASH)
|
50
143
|
default = options[:or]
|
51
144
|
|
52
145
|
return Kind::Of::Boolean if object == Undefined && default.nil?
|
@@ -59,20 +152,45 @@ module Kind
|
|
59
152
|
end
|
60
153
|
|
61
154
|
const_set(:Boolean, ::Module.new do
|
62
|
-
extend
|
155
|
+
extend Checkable
|
63
156
|
|
64
157
|
def self.__kind; [TrueClass, FalseClass].freeze; end
|
65
158
|
|
66
159
|
def self.class?(value); Kind.is.Boolean(value); end
|
67
160
|
|
161
|
+
def self.instance(value, options= Empty::HASH)
|
162
|
+
default = options[:or]
|
163
|
+
|
164
|
+
if ::Kind::Maybe::Value.none?(default)
|
165
|
+
__kind_undefined(value) { Kind::Of::Boolean(value) }
|
166
|
+
else
|
167
|
+
return value if instance?(value)
|
168
|
+
|
169
|
+
__kind_undefined(default) { Kind::Of::Boolean(default) }
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def self.__kind_undefined(value)
|
174
|
+
if value == Kind::Undefined
|
175
|
+
raise Kind::Error.new('Boolean'.freeze, Kind::Undefined)
|
176
|
+
else
|
177
|
+
yield
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
68
181
|
def self.instance?(value);
|
69
182
|
value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
70
183
|
end
|
184
|
+
|
185
|
+
def self.or_undefined(value)
|
186
|
+
result = or_nil(value)
|
187
|
+
result.nil? ? Kind::Undefined : result
|
188
|
+
end
|
71
189
|
end)
|
72
190
|
|
73
191
|
# -- Lambda
|
74
192
|
|
75
|
-
def self.Lambda(object = Undefined, options =
|
193
|
+
def self.Lambda(object = Undefined, options = Empty::HASH)
|
76
194
|
default = options[:or]
|
77
195
|
|
78
196
|
return Kind::Of::Lambda if object == Undefined && default.nil?
|
@@ -85,10 +203,30 @@ module Kind
|
|
85
203
|
end
|
86
204
|
|
87
205
|
const_set(:Lambda, ::Module.new do
|
88
|
-
extend
|
206
|
+
extend Checkable
|
89
207
|
|
90
208
|
def self.__kind; ::Proc; end
|
91
209
|
|
210
|
+
def self.instance(value, options = Empty::HASH)
|
211
|
+
default = options[:or]
|
212
|
+
|
213
|
+
if ::Kind::Maybe::Value.none?(default)
|
214
|
+
__kind_undefined(value) { Kind::Of::Lambda(value) }
|
215
|
+
else
|
216
|
+
return value if instance?(value)
|
217
|
+
|
218
|
+
__kind_undefined(default) { Kind::Of::Lambda(default) }
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def self.__kind_undefined(value)
|
223
|
+
if value == Kind::Undefined
|
224
|
+
raise Kind::Error.new('Lambda'.freeze, Kind::Undefined)
|
225
|
+
else
|
226
|
+
yield
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
92
230
|
def self.instance?(value)
|
93
231
|
value.is_a?(__kind) && value.lambda?
|
94
232
|
end
|
@@ -96,7 +234,7 @@ module Kind
|
|
96
234
|
|
97
235
|
# -- Callable
|
98
236
|
|
99
|
-
def self.Callable(object = Undefined, options =
|
237
|
+
def self.Callable(object = Undefined, options = Empty::HASH)
|
100
238
|
default = options[:or]
|
101
239
|
|
102
240
|
return Kind::Of::Callable if object == Undefined && default.nil?
|
@@ -109,7 +247,7 @@ module Kind
|
|
109
247
|
end
|
110
248
|
|
111
249
|
const_set(:Callable, ::Module.new do
|
112
|
-
extend
|
250
|
+
extend Checkable
|
113
251
|
|
114
252
|
def self.__kind; Object; end
|
115
253
|
|
@@ -117,9 +255,53 @@ module Kind
|
|
117
255
|
Kind::Is::Callable(value)
|
118
256
|
end
|
119
257
|
|
258
|
+
def self.instance(value, options = Empty::HASH)
|
259
|
+
default = options[:or]
|
260
|
+
|
261
|
+
if ::Kind::Maybe::Value.none?(default)
|
262
|
+
__kind_undefined(value) { Kind::Of::Callable(value) }
|
263
|
+
else
|
264
|
+
return value if instance?(value)
|
265
|
+
|
266
|
+
__kind_undefined(default) { Kind::Of::Callable(default) }
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def self.__kind_undefined(value)
|
271
|
+
if value == Kind::Undefined
|
272
|
+
raise Kind::Error.new('Callable'.freeze, Kind::Undefined)
|
273
|
+
else
|
274
|
+
yield
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
120
278
|
def self.instance?(value);
|
121
279
|
value.respond_to?(:call)
|
122
280
|
end
|
123
281
|
end)
|
282
|
+
|
283
|
+
# ---------------------- #
|
284
|
+
# Built-in type checkers #
|
285
|
+
# ---------------------- #
|
286
|
+
|
287
|
+
# -- Classes
|
288
|
+
[
|
289
|
+
String, Symbol, Numeric, Integer, Float, Regexp, Time,
|
290
|
+
Array, Range, Hash, Struct, Enumerator, Set,
|
291
|
+
Method, Proc,
|
292
|
+
IO, File
|
293
|
+
].each { |klass| Types.add(klass) }
|
294
|
+
|
295
|
+
Types.add(Queue, name: 'Queue'.freeze)
|
296
|
+
|
297
|
+
# -- Modules
|
298
|
+
[
|
299
|
+
Enumerable, Comparable
|
300
|
+
].each { |klass| Types.add(klass) }
|
301
|
+
|
302
|
+
# -- Kind::Of::Maybe
|
303
|
+
|
304
|
+
Types.add(Kind::Maybe::Result, name: 'Maybe'.freeze)
|
305
|
+
Types.add(Kind::Maybe::Result, name: 'Optional'.freeze)
|
124
306
|
end
|
125
307
|
end
|
data/lib/kind/checker.rb
CHANGED
@@ -1,9 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Kind
|
4
|
-
module
|
4
|
+
module Checkable
|
5
5
|
def class?(value)
|
6
|
-
Kind::Is.(__kind, value)
|
6
|
+
Kind::Is.__call__(__kind, value)
|
7
|
+
end
|
8
|
+
|
9
|
+
def instance(value, options = Empty::HASH)
|
10
|
+
default = options[:or]
|
11
|
+
|
12
|
+
return Kind::Of.(__kind, value) if ::Kind::Maybe::Value.none?(default)
|
13
|
+
|
14
|
+
instance?(value) ? value : Kind::Of.(__kind, default)
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](value, options = options = Empty::HASH)
|
18
|
+
instance(value, options)
|
7
19
|
end
|
8
20
|
|
9
21
|
def instance?(value)
|
@@ -13,7 +25,21 @@ module Kind
|
|
13
25
|
def or_nil(value)
|
14
26
|
return value if instance?(value)
|
15
27
|
end
|
28
|
+
|
29
|
+
def or_undefined(value)
|
30
|
+
or_nil(value) || Kind::Undefined
|
31
|
+
end
|
16
32
|
end
|
17
33
|
|
18
|
-
private_constant :
|
34
|
+
private_constant :Checkable
|
35
|
+
|
36
|
+
class Checker
|
37
|
+
include Checkable
|
38
|
+
|
39
|
+
attr_reader :__kind
|
40
|
+
|
41
|
+
def initialize(kind)
|
42
|
+
@__kind = kind
|
43
|
+
end
|
44
|
+
end
|
19
45
|
end
|
data/lib/kind/empty.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module Kind
|
6
|
+
module Empty
|
7
|
+
SET = ::Set.new.freeze
|
8
|
+
|
9
|
+
HASH = {}.freeze
|
10
|
+
|
11
|
+
ARY = [].freeze
|
12
|
+
ARRAY = ARY
|
13
|
+
|
14
|
+
STR = ''.freeze
|
15
|
+
STRING = STR
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
unless defined?(Empty)
|
20
|
+
Empty = Kind::Empty
|
21
|
+
end
|
data/lib/kind/error.rb
CHANGED
data/lib/kind/is.rb
CHANGED
@@ -2,19 +2,14 @@
|
|
2
2
|
|
3
3
|
module Kind
|
4
4
|
module Is
|
5
|
-
def self.call(expected,
|
6
|
-
|
7
|
-
mod = Kind::Of.Module(value)
|
8
|
-
|
9
|
-
mod <= expected_mod || false
|
5
|
+
def self.call(expected, object)
|
6
|
+
__call__(Kind::Of.Module(expected), object)
|
10
7
|
end
|
11
8
|
|
12
|
-
def self.
|
13
|
-
|
14
|
-
end
|
9
|
+
def self.__call__(expected_kind, object)
|
10
|
+
kind = Kind::Of.Module(object)
|
15
11
|
|
16
|
-
|
17
|
-
value == ::Module || (value.is_a?(::Module) && !self.Class(value))
|
12
|
+
kind <= expected_kind || false
|
18
13
|
end
|
19
14
|
end
|
20
15
|
end
|
data/lib/kind/maybe.rb
CHANGED
@@ -19,19 +19,25 @@ module Kind
|
|
19
19
|
@value = value
|
20
20
|
end
|
21
21
|
|
22
|
-
def value_or(
|
22
|
+
def value_or(method_name = Undefined, &block)
|
23
|
+
raise NotImplementedError
|
24
|
+
end
|
23
25
|
|
24
|
-
def none
|
26
|
+
def none?
|
27
|
+
raise NotImplementedError
|
28
|
+
end
|
25
29
|
|
26
30
|
def some?; !none?; end
|
27
31
|
|
28
|
-
def map(&fn)
|
32
|
+
def map(&fn)
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
29
35
|
|
30
|
-
def try(method_name, &block)
|
36
|
+
def try(method_name = Undefined, &block)
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
31
39
|
end
|
32
40
|
|
33
|
-
private_constant :Result
|
34
|
-
|
35
41
|
class None < Result
|
36
42
|
INVALID_DEFAULT_ARG = 'the default value must be defined as an argument or block'.freeze
|
37
43
|
|
data/lib/kind/of.rb
CHANGED
@@ -2,42 +2,10 @@
|
|
2
2
|
|
3
3
|
module Kind
|
4
4
|
module Of
|
5
|
-
def self.call(
|
6
|
-
return object if object
|
5
|
+
def self.call(kind, object, kind_name = nil)
|
6
|
+
return object if kind === object
|
7
7
|
|
8
|
-
raise Kind::Error.new(
|
8
|
+
raise Kind::Error.new(kind_name || kind.name, object)
|
9
9
|
end
|
10
|
-
|
11
|
-
def self.Class(object = Undefined)
|
12
|
-
return Class if object == Undefined
|
13
|
-
|
14
|
-
self.call(::Class, object)
|
15
|
-
end
|
16
|
-
|
17
|
-
const_set(:Class, ::Module.new do
|
18
|
-
extend Checker
|
19
|
-
|
20
|
-
def self.__kind; ::Class; end
|
21
|
-
|
22
|
-
def self.class?(value); Kind::Is.Class(value); end
|
23
|
-
|
24
|
-
def self.instance?(value); class?(value); end
|
25
|
-
end)
|
26
|
-
|
27
|
-
def self.Module(object = Undefined)
|
28
|
-
return Module if object == Undefined
|
29
|
-
|
30
|
-
self.call(::Module, object)
|
31
|
-
end
|
32
|
-
|
33
|
-
const_set(:Module, ::Module.new do
|
34
|
-
extend Checker
|
35
|
-
|
36
|
-
def self.__kind; ::Module; end
|
37
|
-
|
38
|
-
def self.class?(value); Kind::Is.Module(value); end
|
39
|
-
|
40
|
-
def self.instance?(value); class?(value); end
|
41
|
-
end)
|
42
10
|
end
|
43
11
|
end
|
data/lib/kind/types.rb
CHANGED
@@ -7,12 +7,12 @@ module Kind
|
|
7
7
|
COLONS = '::'.freeze
|
8
8
|
|
9
9
|
KIND_OF = <<-RUBY
|
10
|
-
def self.%{method_name}(object = Undefined, options =
|
10
|
+
def self.%{method_name}(object = Undefined, options = Empty::HASH)
|
11
11
|
default = options[:or]
|
12
12
|
|
13
13
|
return Kind::Of::%{kind_name} if object == Undefined && default.nil?
|
14
14
|
|
15
|
-
Kind::Of.(::%{
|
15
|
+
Kind::Of.(::%{kind_name_to_check}, (object || default))
|
16
16
|
end
|
17
17
|
RUBY
|
18
18
|
|
@@ -20,7 +20,7 @@ module Kind
|
|
20
20
|
def self.%{method_name}(value = Undefined)
|
21
21
|
return Kind::Is::%{kind_name} if value == Undefined
|
22
22
|
|
23
|
-
Kind::Is.(::%{
|
23
|
+
Kind::Is.__call__(::%{kind_name_to_check}, value)
|
24
24
|
end
|
25
25
|
RUBY
|
26
26
|
|
@@ -30,21 +30,27 @@ module Kind
|
|
30
30
|
kind_name = Kind.of.Module(kind).name
|
31
31
|
checker = name ? Kind::Of.(String, name) : kind_name
|
32
32
|
|
33
|
-
|
34
|
-
when checker.include?(COLONS)
|
33
|
+
if checker.include?(COLONS)
|
35
34
|
add_kind_with_namespace(checker, method_name: name)
|
36
35
|
else
|
37
|
-
add_root(checker, kind_name, method_name: name,
|
36
|
+
add_root(checker, kind_name, method_name: name,
|
37
|
+
kind_name_to_check: kind_name,
|
38
|
+
create_kind_is_mod: false)
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
42
|
private
|
42
43
|
|
43
|
-
def add_root(checker, kind_name, method_name:, create_kind_is_mod:)
|
44
|
-
root_checker = method_name ? method_name : checker
|
44
|
+
def add_root(checker, kind_name, method_name:, create_kind_is_mod:, kind_name_to_check: nil)
|
45
45
|
root_kind_name = method_name ? method_name : kind_name
|
46
46
|
|
47
|
-
|
47
|
+
params = {
|
48
|
+
method_name: method_name ? method_name : checker,
|
49
|
+
kind_name: method_name ? method_name : kind_name,
|
50
|
+
kind_name_to_check: kind_name_to_check || root_kind_name
|
51
|
+
}
|
52
|
+
|
53
|
+
add_kind(params, Kind::Of, Kind::Is, create_kind_is_mod)
|
48
54
|
end
|
49
55
|
|
50
56
|
def add_kind_with_namespace(checker, method_name:)
|
@@ -70,23 +76,28 @@ module Kind
|
|
70
76
|
checker = const_names[index]
|
71
77
|
kind_name = const_names[0..index].join(COLONS)
|
72
78
|
|
73
|
-
|
79
|
+
params = { method_name: checker, kind_name: kind_name }
|
80
|
+
|
81
|
+
add_kind(params, kind_of_mod, kind_is_mod, true)
|
74
82
|
end
|
75
83
|
|
76
|
-
def add_kind(
|
77
|
-
|
84
|
+
def add_kind(params, kind_of_mod, kind_is_mod, create_kind_is_mod)
|
85
|
+
method_name = params[:method_name]
|
86
|
+
|
87
|
+
unless kind_of_mod.respond_to?(method_name)
|
88
|
+
kind_name = params[:kind_name]
|
89
|
+
params[:kind_name_to_check] ||= kind_name
|
78
90
|
|
79
|
-
|
80
|
-
kind_checker
|
81
|
-
kind_checker.module_eval("def self.__kind; #{kind_name}; end")
|
91
|
+
kind_checker = ::Module.new { extend Checkable }
|
92
|
+
kind_checker.module_eval("def self.__kind; #{params[:kind_name_to_check]}; end")
|
82
93
|
|
83
94
|
kind_of_mod.instance_eval(KIND_OF % params)
|
84
|
-
kind_of_mod.const_set(
|
95
|
+
kind_of_mod.const_set(method_name, kind_checker)
|
85
96
|
end
|
86
97
|
|
87
|
-
unless kind_is_mod.respond_to?(
|
98
|
+
unless kind_is_mod.respond_to?(method_name)
|
88
99
|
kind_is_mod.instance_eval(KIND_IS % params)
|
89
|
-
kind_is_mod.const_set(
|
100
|
+
kind_is_mod.const_set(method_name, Module.new) if create_kind_is_mod
|
90
101
|
end
|
91
102
|
end
|
92
103
|
end
|
data/lib/kind/undefined.rb
CHANGED
data/lib/kind/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kind
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rodrigo Serradura
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-04-
|
11
|
+
date: 2020-04-17 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Basic type system for Ruby (free of dependencies).
|
14
14
|
email:
|
@@ -29,6 +29,7 @@ files:
|
|
29
29
|
- kind.gemspec
|
30
30
|
- lib/kind.rb
|
31
31
|
- lib/kind/checker.rb
|
32
|
+
- lib/kind/empty.rb
|
32
33
|
- lib/kind/error.rb
|
33
34
|
- lib/kind/is.rb
|
34
35
|
- lib/kind/maybe.rb
|