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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5426cb7f865320e961b1ad352dce372af3154afc520d1d9d4f64d7095ae0ddc2
4
- data.tar.gz: b4ebb397ff928f49c2616b1464a964117b879c72ad37532381235d4e03e4d8f3
3
+ metadata.gz: 7dd5fb43e1dfd7c69d1a7ae3ffbf875769a675bbf7f8682bcfc9c8b3aa45c739
4
+ data.tar.gz: db15639b472a6b75d990b83f8b317ba7919f820b36e9a49a9141d387c6623768
5
5
  SHA512:
6
- metadata.gz: 249dcd8cbb89239e035254ff28cffdd0d26a42e811fa86ae3682bf80d9ccf5154e129f1a5756e50ac4710db698361f0ab242d9018d3464384d72b7209d829bbe
7
- data.tar.gz: 39723e362d309fddbd01b6732f962b60c361a29da36bf8ba73d9af65a2a579b750a98477c673d965bf3d81ef4cda60b215bf570d6aad00321bc44e497d997136
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
- - [What happens if a custom type checker has a namespace?](#what-happens-if-a-custom-type-checker-has-a-namespace)
25
- - [Built-in type checkers](#built-in-type-checkers)
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
- # nil
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
- #### What happens if a custom type checker has a namespace?
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(Account::User.new) # #<Account::User:0x0000...>
280
+ Kind.of.Account::User({}) # Kind::Error ({} expected to be a kind of Account::User)
214
281
 
215
- Kind.of.Account::User({}) # Kind::Error ({} expected to be a 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?({}) # false
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) # false
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(Account::User::Membership.new) # #<Account::User::Membership:0x0000...>
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({}) # Kind::Error ({} expected to be a 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?({}) # false
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) # false
237
- Kind.of.Account::User::Membership.class?(User) # true
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
- ## Built-in type checkers
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
- | Classes | Modules |
247
- | ---------- | ---------- |
248
- | String | Enumerable |
249
- | Symbol | Comparable |
250
- | Numeric | |
251
- | Integer | |
252
- | Float | |
253
- | Regexp | |
254
- | Time | |
255
- | Array | |
256
- | Range | |
257
- | Hash | |
258
- | Struct | |
259
- | Enumerator | |
260
- | Method | |
261
- | Proc | |
262
- | IO | |
263
- | File | |
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
- - `Kind.is.Class()`
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
@@ -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
- def self.is; Is; end
14
- def self.of; Of; end
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
- # -- Classes
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
- Types.add(Queue, name: 'Queue'.freeze)
32
+ def self.of(kind = Undefined, object = Undefined)
33
+ return Of if kind == Undefined && object == Undefined
25
34
 
26
- # -- Modules
27
- [
28
- Enumerable, Comparable
29
- ].each { |klass| Types.add(klass) }
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 Checker
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 Checker
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 Checker
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
@@ -1,9 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kind
4
- module Checker
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 :Checker
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
@@ -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
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Kind
4
4
  class Error < TypeError
5
- def initialize(klass, object)
6
- super("#{object.inspect} expected to be a kind of #{klass}")
5
+ def initialize(kind_name, object)
6
+ super("#{object.inspect} expected to be a kind of #{kind_name}")
7
7
  end
8
8
  end
9
9
  end
@@ -2,19 +2,14 @@
2
2
 
3
3
  module Kind
4
4
  module Is
5
- def self.call(expected, value)
6
- expected_mod = Kind::Of.Module(expected)
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.Class(value)
13
- value.is_a?(::Class)
14
- end
9
+ def self.__call__(expected_kind, object)
10
+ kind = Kind::Of.Module(object)
15
11
 
16
- def self.Module(value)
17
- value == ::Module || (value.is_a?(::Module) && !self.Class(value))
12
+ kind <= expected_kind || false
18
13
  end
19
14
  end
20
15
  end
@@ -19,19 +19,25 @@ module Kind
19
19
  @value = value
20
20
  end
21
21
 
22
- def value_or(default, &block); end
22
+ def value_or(method_name = Undefined, &block)
23
+ raise NotImplementedError
24
+ end
23
25
 
24
- def none?; end
26
+ def none?
27
+ raise NotImplementedError
28
+ end
25
29
 
26
30
  def some?; !none?; end
27
31
 
28
- def map(&fn); end
32
+ def map(&fn)
33
+ raise NotImplementedError
34
+ end
29
35
 
30
- def try(method_name, &block); end
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
 
@@ -2,42 +2,10 @@
2
2
 
3
3
  module Kind
4
4
  module Of
5
- def self.call(klass, object, name: nil)
6
- return object if object.is_a?(klass)
5
+ def self.call(kind, object, kind_name = nil)
6
+ return object if kind === object
7
7
 
8
- raise Kind::Error.new((name || klass.name), object)
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
@@ -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.(::%{kind_name}, (object || default), name: "%{kind_name}".freeze)
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.(::%{kind_name}, value)
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
- case
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, create_kind_is_mod: false)
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
- add_kind(root_checker, root_kind_name, Kind::Of, Kind::Is, create_kind_is_mod)
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
- add_kind(checker, kind_name, kind_of_mod, kind_is_mod, true)
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(checker, kind_name, kind_of_mod, kind_is_mod, create_kind_is_mod)
77
- params = { method_name: checker, kind_name: kind_name }
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
- unless kind_of_mod.respond_to?(checker)
80
- kind_checker = ::Module.new { extend 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(checker, kind_checker)
95
+ kind_of_mod.const_set(method_name, kind_checker)
85
96
  end
86
97
 
87
- unless kind_is_mod.respond_to?(checker)
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(checker, Module.new) if create_kind_is_mod
100
+ kind_is_mod.const_set(method_name, Module.new) if create_kind_is_mod
90
101
  end
91
102
  end
92
103
  end
@@ -3,7 +3,7 @@
3
3
  module Kind
4
4
  Undefined = Object.new.tap do |undefined|
5
5
  def undefined.inspect
6
- @inspect ||= 'Undefined'.freeze
6
+ @inspect ||= 'Kind::Undefined'.freeze
7
7
  end
8
8
 
9
9
  def undefined.to_s
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kind
4
- VERSION = '1.5.0'
4
+ VERSION = '1.6.0'
5
5
  end
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.5.0
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-12 00:00:00.000000000 Z
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