kind 2.2.0 → 4.0.0

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