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 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