kind 3.0.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
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