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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.tool-versions +1 -0
  3. data/.travis.sh +37 -12
  4. data/.travis.yml +6 -5
  5. data/CHANGELOG.md +1295 -0
  6. data/Gemfile +10 -2
  7. data/README.md +985 -490
  8. data/lib/kind.rb +35 -287
  9. data/lib/kind/active_model/kind_validator.rb +20 -13
  10. data/lib/kind/core.rb +8 -0
  11. data/lib/kind/core/kind.rb +61 -0
  12. data/lib/kind/core/undefined.rb +7 -0
  13. data/lib/kind/dig.rb +40 -0
  14. data/lib/kind/empty.rb +4 -12
  15. data/lib/kind/empty/constant.rb +7 -0
  16. data/lib/kind/error.rb +2 -6
  17. data/lib/kind/maybe.rb +14 -153
  18. data/lib/kind/maybe/none.rb +57 -0
  19. data/lib/kind/maybe/result.rb +51 -0
  20. data/lib/kind/maybe/some.rb +90 -0
  21. data/lib/kind/maybe/typed.rb +29 -0
  22. data/lib/kind/maybe/wrappable.rb +33 -0
  23. data/lib/kind/presence.rb +33 -0
  24. data/lib/kind/try.rb +34 -0
  25. data/lib/kind/type_checker.rb +87 -0
  26. data/lib/kind/type_checkers.rb +30 -0
  27. data/lib/kind/type_checkers/core/array.rb +17 -0
  28. data/lib/kind/type_checkers/core/class.rb +13 -0
  29. data/lib/kind/type_checkers/core/comparable.rb +13 -0
  30. data/lib/kind/type_checkers/core/enumerable.rb +13 -0
  31. data/lib/kind/type_checkers/core/enumerator.rb +13 -0
  32. data/lib/kind/type_checkers/core/file.rb +13 -0
  33. data/lib/kind/type_checkers/core/float.rb +13 -0
  34. data/lib/kind/type_checkers/core/hash.rb +17 -0
  35. data/lib/kind/type_checkers/core/integer.rb +13 -0
  36. data/lib/kind/type_checkers/core/io.rb +13 -0
  37. data/lib/kind/type_checkers/core/method.rb +13 -0
  38. data/lib/kind/type_checkers/core/module.rb +17 -0
  39. data/lib/kind/type_checkers/core/numeric.rb +13 -0
  40. data/lib/kind/type_checkers/core/proc.rb +13 -0
  41. data/lib/kind/type_checkers/core/queue.rb +14 -0
  42. data/lib/kind/type_checkers/core/range.rb +13 -0
  43. data/lib/kind/type_checkers/core/regexp.rb +13 -0
  44. data/lib/kind/type_checkers/core/string.rb +17 -0
  45. data/lib/kind/type_checkers/core/struct.rb +13 -0
  46. data/lib/kind/type_checkers/core/symbol.rb +13 -0
  47. data/lib/kind/type_checkers/core/time.rb +13 -0
  48. data/lib/kind/type_checkers/custom/boolean.rb +19 -0
  49. data/lib/kind/type_checkers/custom/callable.rb +19 -0
  50. data/lib/kind/type_checkers/custom/lambda.rb +19 -0
  51. data/lib/kind/type_checkers/stdlib/open_struct.rb +13 -0
  52. data/lib/kind/type_checkers/stdlib/set.rb +17 -0
  53. data/lib/kind/undefined.rb +4 -2
  54. data/lib/kind/validator.rb +1 -1
  55. data/lib/kind/version.rb +1 -1
  56. data/test.sh +4 -4
  57. metadata +45 -9
  58. data/lib/kind/checker.rb +0 -15
  59. data/lib/kind/checker/factory.rb +0 -35
  60. data/lib/kind/checker/protocol.rb +0 -73
  61. data/lib/kind/is.rb +0 -19
  62. data/lib/kind/of.rb +0 -11
  63. 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/error'
11
- require 'kind/of'
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
- 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 Undefined == expected && Undefined == object
22
-
23
- return Kind::Is.(expected, object) if Undefined != object
21
+ extend self
24
22
 
25
- raise ArgumentError, WRONG_NUMBER_OF_ARGUMENTS
23
+ def is?(kind, arg)
24
+ KIND.is?(kind, arg)
26
25
  end
27
26
 
28
- def self.of(kind = Undefined, object = Undefined)
29
- return Of if Undefined == kind && Undefined == object
27
+ alias is is?
30
28
 
31
- return Kind::Of.(kind, object) if Undefined != object
32
-
33
- Kind::Checker::Factory.create(kind)
29
+ def of?(kind, *args)
30
+ KIND.of?(kind, args)
34
31
  end
35
32
 
36
- def self.of?(kind, *args)
37
- Kind.of(kind).instance?(*args)
33
+ def of_class?(value)
34
+ KIND.of_class?(value)
38
35
  end
39
36
 
40
- # --------------------- #
41
- # Special type checkers #
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
- module Of
63
- # -- Class
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
- def self.class?(value); Kind.is.Boolean(value); end
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
- Types.add(Queue, name: 'Queue'.freeze)
47
+ def of_module_or_class(value)
48
+ KIND.of_module_or_class!(value)
49
+ end
303
50
 
304
- # -- Modules
305
- [
306
- Enumerable, Comparable
307
- ].each { |klass| Types.add(klass) }
51
+ def of(kind, object)
52
+ KIND.of!(kind, object)
53
+ end
308
54
 
309
- # -- Kind::Of::Maybe
55
+ def value(kind, value, default:)
56
+ KIND.value(kind, value, of(kind, default))
57
+ end
310
58
 
311
- Types.add(Kind::Maybe::Result, name: 'Maybe'.freeze)
312
- Types.add(Kind::Maybe::Result, name: 'Optional'.freeze)
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| value.kind_of?(type) }
38
+ return if types.any? { |type| type === value }
39
39
 
40
- "must be a kind of: #{types.map { |klass| klass.name }.join(', ')}"
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.of.Class(value) || value < expected
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.of.Module(value) || value.kind_of?(expected)
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(method_name, value)
70
- return if value.respond_to?(method_name)
69
+ def respond_to(expected, value)
70
+ method_names = Array(expected)
71
71
 
72
- "must respond to the method `#{method_name}`"
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.of.Array(expected)).empty?
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,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Core
5
+ require 'kind/core/kind'
6
+ require 'kind/core/undefined'
7
+ end
8
+ end
@@ -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