kind 1.5.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|