kind 3.0.0 → 5.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/.tool-versions +1 -0
- data/.travis.sh +37 -12
- data/.travis.yml +6 -5
- data/CHANGELOG.md +1295 -0
- data/Gemfile +10 -2
- data/README.md +985 -490
- data/lib/kind.rb +35 -287
- data/lib/kind/active_model/kind_validator.rb +20 -13
- data/lib/kind/core.rb +8 -0
- data/lib/kind/core/kind.rb +61 -0
- data/lib/kind/core/undefined.rb +7 -0
- data/lib/kind/dig.rb +40 -0
- data/lib/kind/empty.rb +4 -12
- data/lib/kind/empty/constant.rb +7 -0
- data/lib/kind/error.rb +2 -6
- data/lib/kind/maybe.rb +14 -153
- data/lib/kind/maybe/none.rb +57 -0
- data/lib/kind/maybe/result.rb +51 -0
- data/lib/kind/maybe/some.rb +90 -0
- data/lib/kind/maybe/typed.rb +29 -0
- data/lib/kind/maybe/wrappable.rb +33 -0
- data/lib/kind/presence.rb +33 -0
- data/lib/kind/try.rb +34 -0
- data/lib/kind/type_checker.rb +87 -0
- data/lib/kind/type_checkers.rb +30 -0
- data/lib/kind/type_checkers/core/array.rb +17 -0
- data/lib/kind/type_checkers/core/class.rb +13 -0
- data/lib/kind/type_checkers/core/comparable.rb +13 -0
- data/lib/kind/type_checkers/core/enumerable.rb +13 -0
- data/lib/kind/type_checkers/core/enumerator.rb +13 -0
- data/lib/kind/type_checkers/core/file.rb +13 -0
- data/lib/kind/type_checkers/core/float.rb +13 -0
- data/lib/kind/type_checkers/core/hash.rb +17 -0
- data/lib/kind/type_checkers/core/integer.rb +13 -0
- data/lib/kind/type_checkers/core/io.rb +13 -0
- data/lib/kind/type_checkers/core/method.rb +13 -0
- data/lib/kind/type_checkers/core/module.rb +17 -0
- data/lib/kind/type_checkers/core/numeric.rb +13 -0
- data/lib/kind/type_checkers/core/proc.rb +13 -0
- data/lib/kind/type_checkers/core/queue.rb +14 -0
- data/lib/kind/type_checkers/core/range.rb +13 -0
- data/lib/kind/type_checkers/core/regexp.rb +13 -0
- data/lib/kind/type_checkers/core/string.rb +17 -0
- data/lib/kind/type_checkers/core/struct.rb +13 -0
- data/lib/kind/type_checkers/core/symbol.rb +13 -0
- data/lib/kind/type_checkers/core/time.rb +13 -0
- data/lib/kind/type_checkers/custom/boolean.rb +19 -0
- data/lib/kind/type_checkers/custom/callable.rb +19 -0
- data/lib/kind/type_checkers/custom/lambda.rb +19 -0
- data/lib/kind/type_checkers/stdlib/open_struct.rb +13 -0
- data/lib/kind/type_checkers/stdlib/set.rb +17 -0
- data/lib/kind/undefined.rb +4 -2
- data/lib/kind/validator.rb +1 -1
- data/lib/kind/version.rb +1 -1
- data/test.sh +4 -4
- metadata +45 -9
- data/lib/kind/checker.rb +0 -15
- data/lib/kind/checker/factory.rb +0 -35
- data/lib/kind/checker/protocol.rb +0 -73
- data/lib/kind/is.rb +0 -19
- data/lib/kind/of.rb +0 -11
- data/lib/kind/types.rb +0 -115
data/lib/kind.rb
CHANGED
@@ -1,314 +1,62 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'set'
|
4
|
+
require 'ostruct'
|
5
|
+
|
3
6
|
require 'kind/version'
|
7
|
+
require 'kind/core'
|
4
8
|
|
9
|
+
require 'kind/error'
|
5
10
|
require 'kind/empty'
|
11
|
+
require 'kind/dig'
|
12
|
+
require 'kind/try'
|
13
|
+
require 'kind/presence'
|
6
14
|
require 'kind/undefined'
|
7
|
-
require 'kind/checker'
|
8
15
|
require 'kind/maybe'
|
9
16
|
|
10
|
-
require 'kind/
|
11
|
-
require 'kind/
|
12
|
-
require 'kind/is'
|
13
|
-
require 'kind/types'
|
17
|
+
require 'kind/type_checker'
|
18
|
+
require 'kind/type_checkers'
|
14
19
|
|
15
20
|
module Kind
|
16
|
-
|
17
|
-
|
18
|
-
private_constant :WRONG_NUMBER_OF_ARGUMENTS
|
19
|
-
|
20
|
-
def self.is(expected = Undefined, object = Undefined)
|
21
|
-
return Is if Undefined == expected && Undefined == object
|
22
|
-
|
23
|
-
return Kind::Is.(expected, object) if Undefined != object
|
21
|
+
extend self
|
24
22
|
|
25
|
-
|
23
|
+
def is?(kind, arg)
|
24
|
+
KIND.is?(kind, arg)
|
26
25
|
end
|
27
26
|
|
28
|
-
|
29
|
-
return Of if Undefined == kind && Undefined == object
|
27
|
+
alias is is?
|
30
28
|
|
31
|
-
|
32
|
-
|
33
|
-
Kind::Checker::Factory.create(kind)
|
29
|
+
def of?(kind, *args)
|
30
|
+
KIND.of?(kind, args)
|
34
31
|
end
|
35
32
|
|
36
|
-
def
|
37
|
-
|
33
|
+
def of_class?(value)
|
34
|
+
KIND.of_class?(value)
|
38
35
|
end
|
39
36
|
|
40
|
-
|
41
|
-
|
42
|
-
# --------------------- #
|
43
|
-
|
44
|
-
module Is
|
45
|
-
def self.Class(value)
|
46
|
-
value.kind_of?(::Class)
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.Module(value)
|
50
|
-
::Module == value || (value.is_a?(::Module) && !self.Class(value))
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.Boolean(value)
|
54
|
-
Kind.of.Class(value) <= TrueClass || value <= FalseClass
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.Callable(value)
|
58
|
-
value.respond_to?(:call)
|
59
|
-
end
|
37
|
+
def of_module?(value)
|
38
|
+
KIND.of_module?(value)
|
60
39
|
end
|
61
40
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
def self.Class(object = Undefined)
|
66
|
-
return Class if Undefined == object
|
67
|
-
|
68
|
-
self.call(::Class, object)
|
69
|
-
end
|
70
|
-
|
71
|
-
const_set(:Class, ::Module.new do
|
72
|
-
extend Checker::Protocol
|
73
|
-
|
74
|
-
def self.__kind; ::Class; end
|
75
|
-
|
76
|
-
def self.class?(value); Kind::Is.Class(value); end
|
77
|
-
|
78
|
-
def self.__is_instance__(value); class?(value); end
|
79
|
-
end)
|
80
|
-
|
81
|
-
def self.Class?(*args)
|
82
|
-
Kind::Of::Class.instance?(*args)
|
83
|
-
end
|
84
|
-
|
85
|
-
# -- Module
|
86
|
-
|
87
|
-
def self.Module(object = Undefined)
|
88
|
-
return Module if Undefined == object
|
89
|
-
|
90
|
-
self.call(::Module, object)
|
91
|
-
end
|
92
|
-
|
93
|
-
const_set(:Module, ::Module.new do
|
94
|
-
extend Checker::Protocol
|
95
|
-
|
96
|
-
def self.__kind_undefined(value)
|
97
|
-
__kind_error(Kind::Undefined) if Kind::Undefined == value
|
98
|
-
|
99
|
-
yield
|
100
|
-
end
|
101
|
-
|
102
|
-
def self.__kind_error(value)
|
103
|
-
raise Kind::Error.new('Module'.freeze, value)
|
104
|
-
end
|
105
|
-
|
106
|
-
def self.__kind_of(value)
|
107
|
-
return value if Kind::Is.Module(value)
|
108
|
-
|
109
|
-
__kind_error(value)
|
110
|
-
end
|
111
|
-
|
112
|
-
def self.__kind; ::Module; end
|
113
|
-
|
114
|
-
def self.class?(value); Kind::Is.Module(value); end
|
115
|
-
|
116
|
-
def self.instance(value, options = Empty::HASH)
|
117
|
-
default = options[:or]
|
118
|
-
|
119
|
-
if ::Kind::Maybe::Value.none?(default)
|
120
|
-
__kind_undefined(value) { __kind_of(value) }
|
121
|
-
else
|
122
|
-
return value if Kind::Undefined != value && instance?(value)
|
123
|
-
|
124
|
-
__kind_undefined(default) { __kind_of(default) }
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def self.__is_instance__(value); class?(value); end
|
129
|
-
end)
|
130
|
-
|
131
|
-
def self.Module?(*args)
|
132
|
-
Kind::Of::Module.instance?(*args)
|
133
|
-
end
|
134
|
-
|
135
|
-
# -- Boolean
|
136
|
-
|
137
|
-
def self.Boolean(object = Undefined, options = Empty::HASH)
|
138
|
-
default = options[:or]
|
139
|
-
|
140
|
-
return Kind::Of::Boolean if Undefined == object && default.nil?
|
141
|
-
|
142
|
-
bool = object.nil? ? default : object
|
143
|
-
|
144
|
-
return bool if bool.is_a?(::TrueClass) || bool.is_a?(::FalseClass)
|
145
|
-
|
146
|
-
raise Kind::Error.new('Boolean'.freeze, bool)
|
147
|
-
end
|
148
|
-
|
149
|
-
const_set(:Boolean, ::Module.new do
|
150
|
-
extend Checker::Protocol
|
151
|
-
|
152
|
-
def self.__kind; [TrueClass, FalseClass].freeze; end
|
41
|
+
def respond_to(value, *method_names)
|
42
|
+
method_names.each { |method_name| KIND.respond_to!(method_name, value) }
|
153
43
|
|
154
|
-
|
155
|
-
|
156
|
-
def self.instance(value, options= Empty::HASH)
|
157
|
-
default = options[:or]
|
158
|
-
|
159
|
-
if ::Kind::Maybe::Value.none?(default)
|
160
|
-
__kind_undefined(value) { Kind::Of::Boolean(value) }
|
161
|
-
else
|
162
|
-
return value if Kind::Undefined != value && instance?(value)
|
163
|
-
|
164
|
-
__kind_undefined(default) { Kind::Of::Boolean(default) }
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
def self.__kind_undefined(value)
|
169
|
-
if Kind::Undefined == value
|
170
|
-
raise Kind::Error.new('Boolean'.freeze, Kind::Undefined)
|
171
|
-
else
|
172
|
-
yield
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
def self.__is_instance__(value);
|
177
|
-
value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
178
|
-
end
|
179
|
-
|
180
|
-
def self.or_undefined(value)
|
181
|
-
result = or_nil(value)
|
182
|
-
result.nil? ? Kind::Undefined : result
|
183
|
-
end
|
184
|
-
end)
|
185
|
-
|
186
|
-
def self.Boolean?(*args)
|
187
|
-
Kind::Of::Boolean.instance?(*args)
|
188
|
-
end
|
189
|
-
|
190
|
-
# -- Lambda
|
191
|
-
|
192
|
-
def self.Lambda(object = Undefined, options = Empty::HASH)
|
193
|
-
default = options[:or]
|
194
|
-
|
195
|
-
return Kind::Of::Lambda if Undefined == object && default.nil?
|
196
|
-
|
197
|
-
func = object || default
|
198
|
-
|
199
|
-
return func if func.is_a?(::Proc) && func.lambda?
|
200
|
-
|
201
|
-
raise Kind::Error.new('Lambda'.freeze, func)
|
202
|
-
end
|
203
|
-
|
204
|
-
const_set(:Lambda, ::Module.new do
|
205
|
-
extend Checker::Protocol
|
206
|
-
|
207
|
-
def self.__kind; ::Proc; end
|
208
|
-
|
209
|
-
def self.instance(value, options = Empty::HASH)
|
210
|
-
default = options[:or]
|
211
|
-
|
212
|
-
if ::Kind::Maybe::Value.none?(default)
|
213
|
-
__kind_undefined(value) { Kind::Of::Lambda(value) }
|
214
|
-
else
|
215
|
-
return value if Kind::Undefined != value && instance?(value)
|
216
|
-
|
217
|
-
__kind_undefined(default) { Kind::Of::Lambda(default) }
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
def self.__kind_undefined(value)
|
222
|
-
if Kind::Undefined == value
|
223
|
-
raise Kind::Error.new('Lambda'.freeze, Kind::Undefined)
|
224
|
-
else
|
225
|
-
yield
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
def self.__is_instance__(value)
|
230
|
-
value.is_a?(__kind) && value.lambda?
|
231
|
-
end
|
232
|
-
end)
|
233
|
-
|
234
|
-
def self.Lambda?(*args)
|
235
|
-
Kind::Of::Lambda.instance?(*args)
|
236
|
-
end
|
237
|
-
|
238
|
-
# -- Callable
|
239
|
-
|
240
|
-
def self.Callable(object = Undefined, options = Empty::HASH)
|
241
|
-
default = options[:or]
|
242
|
-
|
243
|
-
return Kind::Of::Callable if Undefined == object && default.nil?
|
244
|
-
|
245
|
-
callable = object || default
|
246
|
-
|
247
|
-
return callable if callable.respond_to?(:call)
|
248
|
-
|
249
|
-
raise Kind::Error.new('Callable'.freeze, callable)
|
250
|
-
end
|
251
|
-
|
252
|
-
const_set(:Callable, ::Module.new do
|
253
|
-
extend Checker::Protocol
|
254
|
-
|
255
|
-
def self.__kind; Object; end
|
256
|
-
|
257
|
-
def self.class?(value)
|
258
|
-
Kind::Is::Callable(value)
|
259
|
-
end
|
260
|
-
|
261
|
-
def self.instance(value, options = Empty::HASH)
|
262
|
-
default = options[:or]
|
263
|
-
|
264
|
-
if ::Kind::Maybe::Value.none?(default)
|
265
|
-
__kind_undefined(value) { Kind::Of::Callable(value) }
|
266
|
-
else
|
267
|
-
return value if Kind::Undefined != value && instance?(value)
|
268
|
-
|
269
|
-
__kind_undefined(default) { Kind::Of::Callable(default) }
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
|
-
def self.__kind_undefined(value)
|
274
|
-
if Kind::Undefined == value
|
275
|
-
raise Kind::Error.new('Callable'.freeze, Kind::Undefined)
|
276
|
-
else
|
277
|
-
yield
|
278
|
-
end
|
279
|
-
end
|
280
|
-
|
281
|
-
def self.__is_instance__(value);
|
282
|
-
value.respond_to?(:call)
|
283
|
-
end
|
284
|
-
end)
|
285
|
-
|
286
|
-
def self.Callable?(*args)
|
287
|
-
Kind::Of::Callable.instance?(*args)
|
288
|
-
end
|
289
|
-
|
290
|
-
# ---------------------- #
|
291
|
-
# Built-in type checkers #
|
292
|
-
# ---------------------- #
|
293
|
-
|
294
|
-
# -- Classes
|
295
|
-
[
|
296
|
-
String, Symbol, Numeric, Integer, Float, Regexp, Time,
|
297
|
-
Array, Range, Hash, Struct, Enumerator, Set,
|
298
|
-
Method, Proc,
|
299
|
-
IO, File
|
300
|
-
].each { |klass| Types.add(klass) }
|
44
|
+
value
|
45
|
+
end
|
301
46
|
|
302
|
-
|
47
|
+
def of_module_or_class(value)
|
48
|
+
KIND.of_module_or_class!(value)
|
49
|
+
end
|
303
50
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
].each { |klass| Types.add(klass) }
|
51
|
+
def of(kind, object)
|
52
|
+
KIND.of!(kind, object)
|
53
|
+
end
|
308
54
|
|
309
|
-
|
55
|
+
def value(kind, value, default:)
|
56
|
+
KIND.value(kind, value, of(kind, default))
|
57
|
+
end
|
310
58
|
|
311
|
-
|
312
|
-
|
59
|
+
def Of(kind, opt = Empty::HASH)
|
60
|
+
TypeChecker::Object.new(kind, opt)
|
313
61
|
end
|
314
62
|
end
|
@@ -35,30 +35,30 @@ class KindValidator < ActiveModel::EachValidator
|
|
35
35
|
def kind_of(expected, value)
|
36
36
|
types = Array(expected)
|
37
37
|
|
38
|
-
return if types.any? { |type|
|
38
|
+
return if types.any? { |type| type === value }
|
39
39
|
|
40
|
-
"must be a kind of: #{types.map { |
|
40
|
+
"must be a kind of: #{types.map { |type| type.name }.join(', ')}"
|
41
41
|
end
|
42
42
|
|
43
43
|
CLASS_OR_MODULE = 'Class/Module'.freeze
|
44
44
|
|
45
45
|
def kind_is(expected, value)
|
46
|
-
return kind_is_not(expected, value) unless expected.kind_of?(Array)
|
46
|
+
return kind_is_not(expected, value) unless expected.kind_of?(::Array)
|
47
47
|
|
48
|
-
result = expected.map { |kind| kind_is_not(kind, value) }.compact
|
48
|
+
result = expected.map { |kind| kind_is_not(kind, value) }.tap(&:compact!)
|
49
49
|
|
50
50
|
result.empty? || result.size < expected.size ? nil : result.join(', ')
|
51
51
|
end
|
52
52
|
|
53
53
|
def kind_is_not(expected, value)
|
54
54
|
case expected
|
55
|
-
when Class
|
56
|
-
return if expected == Kind
|
55
|
+
when ::Class
|
56
|
+
return if expected == Kind::Class[value] || value < expected
|
57
57
|
|
58
58
|
"must be the class or a subclass of `#{expected.name}`"
|
59
|
-
when Module
|
59
|
+
when ::Module
|
60
60
|
return if value.kind_of?(Class) && value <= expected
|
61
|
-
return if expected == Kind.
|
61
|
+
return if expected == Kind.of_module_or_class(value) || value.kind_of?(expected)
|
62
62
|
|
63
63
|
"must include the `#{expected.name}` module"
|
64
64
|
else
|
@@ -66,10 +66,17 @@ class KindValidator < ActiveModel::EachValidator
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
-
def respond_to(
|
70
|
-
|
69
|
+
def respond_to(expected, value)
|
70
|
+
method_names = Array(expected)
|
71
71
|
|
72
|
-
|
72
|
+
expected_methods = method_names.select { |method_name| !value.respond_to?(method_name) }
|
73
|
+
expected_methods.map! { |method_name| "`#{method_name}`" }
|
74
|
+
|
75
|
+
return if expected_methods.empty?
|
76
|
+
|
77
|
+
methods = expected_methods.size == 1 ? 'method' : 'methods'
|
78
|
+
|
79
|
+
"must respond to the #{methods}: #{expected_methods.join(', ')}"
|
73
80
|
end
|
74
81
|
|
75
82
|
def instance_of(expected, value)
|
@@ -81,7 +88,7 @@ class KindValidator < ActiveModel::EachValidator
|
|
81
88
|
end
|
82
89
|
|
83
90
|
def array_with(expected, value)
|
84
|
-
return if value.kind_of?(Array) && !value.empty? && (value - Kind
|
91
|
+
return if value.kind_of?(::Array) && !value.empty? && (value - Kind::Array[expected]).empty?
|
85
92
|
|
86
93
|
"must be an array with: #{expected.join(', ')}"
|
87
94
|
end
|
@@ -89,7 +96,7 @@ class KindValidator < ActiveModel::EachValidator
|
|
89
96
|
def array_of(expected, value)
|
90
97
|
types = Array(expected)
|
91
98
|
|
92
|
-
return if value.kind_of?(Array) && !value.empty? && value.all? { |value| types.any? { |type| value.kind_of?(type) } }
|
99
|
+
return if value.kind_of?(::Array) && !value.empty? && value.all? { |value| types.any? { |type| value.kind_of?(type) } }
|
93
100
|
|
94
101
|
"must be an array of: #{types.map { |klass| klass.name }.join(', ')}"
|
95
102
|
end
|
data/lib/kind/core.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kind
|
4
|
+
module KIND
|
5
|
+
def self.null?(value) # :nodoc:
|
6
|
+
value.nil? || Undefined == value
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.of?(kind, values) # :nodoc:
|
10
|
+
of_kind = -> value { kind === value }
|
11
|
+
|
12
|
+
values.empty? ? of_kind : values.all?(&of_kind)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.of!(kind, value, kind_name = nil) # :nodoc:
|
16
|
+
return value if kind === value
|
17
|
+
|
18
|
+
error!(kind_name || kind.name, value)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.error!(kind_name, value) # :nodoc:
|
22
|
+
raise Error.new(kind_name, value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.of_class?(value) # :nodoc:
|
26
|
+
value.kind_of?(::Class)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.of_module?(value) # :nodoc:
|
30
|
+
::Module == value || (value.kind_of?(::Module) && !of_class?(value))
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.of_module_or_class!(value) # :nodoc:
|
34
|
+
of!(::Module, value, 'Module/Class')
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.respond_to!(method_name, value) # :nodoc:
|
38
|
+
return value if value.respond_to?(method_name)
|
39
|
+
|
40
|
+
raise Error.new("expected #{value} to respond to :#{method_name}")
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.is?(expected, value) # :nodoc:
|
44
|
+
is!(of_module_or_class!(expected), value)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.is!(expected_kind, value) # :nodoc:
|
48
|
+
kind = of_module_or_class!(value)
|
49
|
+
|
50
|
+
if of_class?(kind)
|
51
|
+
kind <= expected_kind || expected_kind == ::Class
|
52
|
+
else
|
53
|
+
kind == expected_kind || kind.kind_of?(expected_kind)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.value(kind, arg, default) # :nodoc:
|
58
|
+
kind === arg ? arg : default
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|