kind 0.6.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +66 -34
- data/lib/kind.rb +104 -46
- data/lib/kind/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c1ca16c1feac58c7a1303c0ee1cda8cd5831a2270039ed8301912afc7a3a9f55
|
4
|
+
data.tar.gz: 61dba4fdf5c8c94118f93ca5d16ab55364601fc5846f9d574cf44df05f6dff2a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8397153bb070e3e27d4f2dfd0557a5f050a7ec292f4e2e779ffb7cdda26c125c29c64c283d8adc8ea09dce5ad4b23ed18575f35c711353a2184fbdff4effe34a
|
7
|
+
data.tar.gz: 7fee91750a8b24697e023a4be435b8c8db78cdd3f365dd70f5b9b0fda6daf549b3235528e94f6d9f985b5850ee031cbdd84475f8aabe4b549392c2304b7d8fa0
|
data/README.md
CHANGED
@@ -23,6 +23,7 @@ One of the goals of this project is to do simple type checking like `"some strin
|
|
23
23
|
- [Kind.of](#kindof)
|
24
24
|
- [Kind.is](#kindis)
|
25
25
|
- [How to create a new type checker?](#how-to-create-a-new-type-checker)
|
26
|
+
- [What happens if a custom type checker has a namespace?](#what-happens-if-a-custom-type-checker-has-a-namespace)
|
26
27
|
- [Development](#development)
|
27
28
|
- [Contributing](#contributing)
|
28
29
|
- [License](#license)
|
@@ -58,11 +59,9 @@ def sum(a, b)
|
|
58
59
|
Kind.of.Numeric(a) + Kind.of.Numeric(b)
|
59
60
|
end
|
60
61
|
|
61
|
-
sum(1, 1)
|
62
|
-
# 2
|
62
|
+
sum(1, 1) # 2
|
63
63
|
|
64
|
-
sum('1', 1)
|
65
|
-
# Kind::Error ("\"1\" expected to be a kind of Numeric")
|
64
|
+
sum('1', 1 # Kind::Error ("\"1\" expected to be a kind of Numeric")
|
66
65
|
```
|
67
66
|
|
68
67
|
### Verifying the kind of some object
|
@@ -70,16 +69,13 @@ sum('1', 1)
|
|
70
69
|
By default, basic verifications are strict. So, when you perform `Kind.of.Hash(value)`, if the given value was a Hash, the value itself will be returned, but if it isn't the right type, an error will be raised.
|
71
70
|
|
72
71
|
```ruby
|
73
|
-
Kind.of.Hash('')
|
74
|
-
# raise Kind::Error, "'' expected to be a kind of Hash"
|
72
|
+
Kind.of.Hash('') # raise Kind::Error, "'' expected to be a kind of Hash"
|
75
73
|
|
76
|
-
Kind.of.Hash({a: 1})
|
77
|
-
# {a: 1}
|
74
|
+
Kind.of.Hash({a: 1}) # {a: 1}
|
78
75
|
|
79
76
|
# ---
|
80
77
|
|
81
|
-
Kind.of.Boolean(nil)
|
82
|
-
# raise Kind::Error, "nil expected to be a kind of Boolean"
|
78
|
+
Kind.of.Boolean(nil) # raise Kind::Error, "nil expected to be a kind of Boolean"
|
83
79
|
|
84
80
|
Kind.of.Boolean(true) # true
|
85
81
|
Kind.of.Boolean(false) # false
|
@@ -90,13 +86,11 @@ When the verified value is nil, it is possible to define a default value with th
|
|
90
86
|
```ruby
|
91
87
|
value = nil
|
92
88
|
|
93
|
-
Kind.of.Hash(value, or: {})
|
94
|
-
# {}
|
89
|
+
Kind.of.Hash(value, or: {}) # {}
|
95
90
|
|
96
91
|
# ---
|
97
92
|
|
98
|
-
Kind.of.Boolean(nil, or: true)
|
99
|
-
# true
|
93
|
+
Kind.of.Boolean(nil, or: true) # true
|
100
94
|
```
|
101
95
|
|
102
96
|
As an alternative syntax, you can use the `Kind::Of` instead of the method. e.g: `Kind::Of::Hash('')`
|
@@ -134,24 +128,19 @@ Kind.of.Boolean.instance?(false) # true
|
|
134
128
|
You can use `Kind.is` to verify if some class has the expected type as its ancestor.
|
135
129
|
|
136
130
|
```ruby
|
137
|
-
Kind.is.Hash(String)
|
138
|
-
# false
|
131
|
+
Kind.is.Hash(String) # false
|
139
132
|
|
140
|
-
Kind.is.Hash(Hash)
|
141
|
-
# true
|
133
|
+
Kind.is.Hash(Hash) # true
|
142
134
|
|
143
|
-
Kind.is.Hash(ActiveSupport::HashWithIndifferentAccess)
|
144
|
-
# true
|
135
|
+
Kind.is.Hash(ActiveSupport::HashWithIndifferentAccess) # true
|
145
136
|
```
|
146
137
|
|
147
138
|
And just for convenience, you can use the method `Kind.of.*.class?` to verify if the given class has the expected type as its ancestor.
|
148
139
|
|
149
140
|
```ruby
|
150
|
-
Kind.of.Hash.class?(Hash)
|
151
|
-
# true
|
141
|
+
Kind.of.Hash.class?(Hash) # true
|
152
142
|
|
153
|
-
Kind.of.Hash.class?(ActiveSupport::HashWithIndifferentAccess)
|
154
|
-
# true
|
143
|
+
Kind.of.Hash.class?(ActiveSupport::HashWithIndifferentAccess) # true
|
155
144
|
```
|
156
145
|
|
157
146
|
[⬆️ Back to Top](#table-of-contents-)
|
@@ -218,24 +207,67 @@ end
|
|
218
207
|
# Usage examples: #
|
219
208
|
# --------------- #
|
220
209
|
|
221
|
-
Kind.of.User(User.new)
|
222
|
-
# #<User:0x0000...>
|
210
|
+
Kind.of.User(User.new) # #<User:0x0000...>
|
223
211
|
|
224
|
-
Kind.of.User({})
|
225
|
-
# Kind::Error ({} expected to be a kind of User)
|
212
|
+
Kind.of.User({}) # Kind::Error ({} expected to be a kind of User)
|
226
213
|
|
227
|
-
Kind.of.User.or_nil({})
|
228
|
-
# nil
|
229
|
-
|
230
|
-
Kind.of.User.instance?({}) # false
|
231
|
-
Kind.of.User.class?(Hash) # false
|
214
|
+
Kind.of.User.or_nil({}) # nil
|
232
215
|
|
216
|
+
Kind.of.User.instance?({}) # false
|
233
217
|
Kind.of.User.instance?(User) # true
|
234
|
-
|
218
|
+
|
219
|
+
Kind.of.User.class?(Hash) # false
|
220
|
+
Kind.of.User.class?(User) # true
|
235
221
|
```
|
236
222
|
|
237
223
|
[⬆️ Back to Top](#table-of-contents-)
|
238
224
|
|
225
|
+
## What happens if a custom type checker has a namespace?
|
226
|
+
|
227
|
+
The type checker will preserve the namespace. ;)
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
module Account
|
231
|
+
class User
|
232
|
+
Kind::Types.add(self)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
module Account
|
237
|
+
class User
|
238
|
+
class Membership
|
239
|
+
Kind::Types.add(self)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
Kind.of.Account::User(Account::User.new) # #<Account::User:0x0000...>
|
245
|
+
|
246
|
+
Kind.of.Account::User({}) # Kind::Error ({} expected to be a kind of Account::User)
|
247
|
+
|
248
|
+
Kind.of.Account::User.or_nil({}) # nil
|
249
|
+
|
250
|
+
Kind.of.Account::User.instance?({}) # false
|
251
|
+
Kind.of.Account::User.instance?(User) # true
|
252
|
+
|
253
|
+
Kind.of.Account::User.class?(Hash) # false
|
254
|
+
Kind.of.Account::User.class?(User) # true
|
255
|
+
|
256
|
+
# ---
|
257
|
+
|
258
|
+
Kind.of.Account::User::Membership(Account::User::Membership.new) # #<Account::User::Membership:0x0000...>
|
259
|
+
|
260
|
+
Kind.of.Account::User::Membership({}) # Kind::Error ({} expected to be a kind of Account::User::Membership)
|
261
|
+
|
262
|
+
Kind.of.Account::User::Membership.or_nil({}) # nil
|
263
|
+
|
264
|
+
Kind.of.Account::User::Membership.instance?({}) # false
|
265
|
+
Kind.of.Account::User::Membership.instance?(User) # true
|
266
|
+
|
267
|
+
Kind.of.Account::User::Membership.class?(Hash) # false
|
268
|
+
Kind.of.Account::User::Membership.class?(User) # true
|
269
|
+
```
|
270
|
+
|
239
271
|
## Development
|
240
272
|
|
241
273
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/kind.rb
CHANGED
@@ -26,19 +26,13 @@ module Kind
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
|
-
attr_reader :type
|
31
|
-
|
32
|
-
def initialize(type)
|
33
|
-
@type = type
|
34
|
-
end
|
35
|
-
|
29
|
+
module Checker
|
36
30
|
def class?(value)
|
37
|
-
Kind::Is.
|
31
|
+
Kind::Is.(__kind, value)
|
38
32
|
end
|
39
33
|
|
40
34
|
def instance?(value)
|
41
|
-
value.is_a?(
|
35
|
+
value.is_a?(__kind)
|
42
36
|
end
|
43
37
|
|
44
38
|
def or_nil(value)
|
@@ -59,13 +53,15 @@ module Kind
|
|
59
53
|
self.call(::Class, object)
|
60
54
|
end
|
61
55
|
|
62
|
-
const_set(:Class, ::
|
63
|
-
|
64
|
-
Kind::Is.Class(value)
|
65
|
-
end
|
56
|
+
const_set(:Class, ::Module.new do
|
57
|
+
extend Checker
|
66
58
|
|
67
|
-
|
68
|
-
|
59
|
+
def self.__kind; ::Class; end
|
60
|
+
|
61
|
+
def self.class?(value); Kind::Is.Class(value); end
|
62
|
+
|
63
|
+
def self.instance?(value); class?(value); end
|
64
|
+
end)
|
69
65
|
|
70
66
|
def self.Module(object = nil)
|
71
67
|
return Module if object.nil?
|
@@ -73,49 +69,105 @@ module Kind
|
|
73
69
|
self.call(::Module, object)
|
74
70
|
end
|
75
71
|
|
76
|
-
const_set(:Module, ::
|
77
|
-
|
78
|
-
|
79
|
-
end
|
72
|
+
const_set(:Module, ::Module.new do
|
73
|
+
extend Checker
|
74
|
+
|
75
|
+
def self.__kind; ::Module; end
|
80
76
|
|
81
|
-
|
82
|
-
|
77
|
+
def self.class?(value); Kind::Is.Module(value); end
|
78
|
+
|
79
|
+
def self.instance?(value); class?(value); end
|
80
|
+
end)
|
83
81
|
end
|
84
82
|
|
85
83
|
module Types
|
86
84
|
extend self
|
87
85
|
|
86
|
+
COLONS = '::'.freeze
|
87
|
+
|
88
88
|
KIND_OF = <<-RUBY
|
89
|
-
def self.%{
|
89
|
+
def self.%{method_name}(object = nil, options = {})
|
90
90
|
default = options[:or]
|
91
91
|
|
92
|
-
return Kind::Of::%{
|
92
|
+
return Kind::Of::%{kind_name} if object.nil? && default.nil?
|
93
93
|
|
94
|
-
Kind::Of.
|
94
|
+
Kind::Of.(::%{kind_name}, (object || default), name: "%{kind_name}".freeze)
|
95
95
|
end
|
96
96
|
RUBY
|
97
97
|
|
98
98
|
KIND_IS = <<-RUBY
|
99
|
-
def self.%{
|
100
|
-
Kind::Is
|
99
|
+
def self.%{method_name}(value = nil)
|
100
|
+
return Kind::Is::%{kind_name} if value.nil?
|
101
|
+
|
102
|
+
Kind::Is.(::%{kind_name}, value)
|
101
103
|
end
|
102
104
|
RUBY
|
103
105
|
|
104
|
-
private_constant :KIND_OF, :KIND_IS
|
106
|
+
private_constant :KIND_OF, :KIND_IS, :COLONS
|
105
107
|
|
106
|
-
def add(
|
107
|
-
|
108
|
-
|
108
|
+
def add(kind, name: nil)
|
109
|
+
kind_name = Kind.of.Module(kind).name
|
110
|
+
checker = name ? Kind::Of.(String, name) : kind_name
|
109
111
|
|
110
|
-
|
111
|
-
|
112
|
-
|
112
|
+
case
|
113
|
+
when checker.include?(COLONS)
|
114
|
+
add_kind_with_namespace(checker, method_name: name)
|
115
|
+
else
|
116
|
+
add_root(checker, kind_name, method_name: name, create_kind_is_mod: false)
|
113
117
|
end
|
118
|
+
end
|
114
119
|
|
115
|
-
|
116
|
-
|
120
|
+
private
|
121
|
+
|
122
|
+
def add_root(checker, kind_name, method_name:, create_kind_is_mod:)
|
123
|
+
root_checker = method_name ? method_name : checker
|
124
|
+
root_kind_name = method_name ? method_name : kind_name
|
125
|
+
|
126
|
+
add_kind(root_checker, root_kind_name, Kind::Of, Kind::Is, create_kind_is_mod)
|
127
|
+
end
|
128
|
+
|
129
|
+
def add_kind_with_namespace(checker, method_name:)
|
130
|
+
raise NotImplementedError if method_name
|
131
|
+
|
132
|
+
const_names = checker.split(COLONS)
|
133
|
+
const_names.each_with_index do |const_name, index|
|
134
|
+
if index == 0
|
135
|
+
add_root(const_name, const_name, method_name: nil, create_kind_is_mod: true)
|
136
|
+
else
|
137
|
+
add_node(const_names, index)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def add_node(const_names, index)
|
143
|
+
namespace = const_names[0..(index-1)]
|
144
|
+
namespace_name = namespace.join(COLONS)
|
145
|
+
|
146
|
+
kind_of_mod = Kind::Of.const_get(namespace_name)
|
147
|
+
kind_is_mod = Kind::Is.const_get(namespace_name)
|
148
|
+
|
149
|
+
checker = const_names[index]
|
150
|
+
kind_name = const_names[0..index].join(COLONS)
|
151
|
+
|
152
|
+
add_kind(checker, kind_name, kind_of_mod, kind_is_mod, true)
|
153
|
+
end
|
154
|
+
|
155
|
+
def add_kind(checker, kind_name, kind_of_mod, kind_is_mod, create_kind_is_mod)
|
156
|
+
params = { method_name: checker, kind_name: kind_name }
|
157
|
+
|
158
|
+
unless kind_of_mod.respond_to?(checker)
|
159
|
+
kind_checker = ::Module.new { extend Checker }
|
160
|
+
kind_checker.module_eval("def self.__kind; #{kind_name}; end")
|
161
|
+
|
162
|
+
kind_of_mod.instance_eval(KIND_OF % params)
|
163
|
+
kind_of_mod.const_set(checker, kind_checker)
|
164
|
+
end
|
165
|
+
|
166
|
+
unless kind_is_mod.respond_to?(checker)
|
167
|
+
kind_is_mod.instance_eval(KIND_IS % params)
|
168
|
+
kind_is_mod.const_set(checker, Module.new) if create_kind_is_mod
|
169
|
+
end
|
117
170
|
end
|
118
|
-
end
|
119
171
|
end
|
120
172
|
|
121
173
|
def self.is; Is; end
|
@@ -162,15 +214,17 @@ module Kind
|
|
162
214
|
raise Kind::Error.new('Boolean'.freeze, bool)
|
163
215
|
end
|
164
216
|
|
165
|
-
const_set(:Boolean, ::
|
166
|
-
|
167
|
-
Kind.is.Boolean(value)
|
168
|
-
end
|
217
|
+
const_set(:Boolean, ::Module.new do
|
218
|
+
extend Checker
|
169
219
|
|
170
|
-
def
|
220
|
+
def self.__kind; [TrueClass, FalseClass].freeze; end
|
221
|
+
|
222
|
+
def self.class?(value); Kind.is.Boolean(value); end
|
223
|
+
|
224
|
+
def self.instance?(value);
|
171
225
|
value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
172
226
|
end
|
173
|
-
end
|
227
|
+
end)
|
174
228
|
|
175
229
|
# -- Lambda
|
176
230
|
|
@@ -186,11 +240,15 @@ module Kind
|
|
186
240
|
raise Kind::Error.new('Lambda'.freeze, func)
|
187
241
|
end
|
188
242
|
|
189
|
-
const_set(:Lambda, ::
|
190
|
-
|
191
|
-
|
243
|
+
const_set(:Lambda, ::Module.new do
|
244
|
+
extend Checker
|
245
|
+
|
246
|
+
def self.__kind; ::Proc; end
|
247
|
+
|
248
|
+
def self.instance?(value)
|
249
|
+
value.is_a?(__kind) && value.lambda?
|
192
250
|
end
|
193
|
-
end
|
251
|
+
end)
|
194
252
|
end
|
195
253
|
|
196
254
|
private_constant :Checker
|
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: 0.
|
4
|
+
version: 1.0.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-
|
11
|
+
date: 2020-03-17 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Basic type system for Ruby (free of dependencies).
|
14
14
|
email:
|