kind 1.7.0 → 2.2.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.
@@ -6,8 +6,8 @@ Gem::Specification.new do |spec|
6
6
  spec.authors = ['Rodrigo Serradura']
7
7
  spec.email = ['rodrigo.serradura@gmail.com']
8
8
 
9
- spec.summary = %q{Basic type system for Ruby.}
10
- spec.description = %q{Basic type system for Ruby (free of dependencies).}
9
+ spec.summary = %q{A simple type system (at runtime) for Ruby.}
10
+ spec.description = %q{A simple type system (at runtime) for Ruby - free of dependencies.}
11
11
  spec.homepage = 'https://github.com/serradura/kind'
12
12
  spec.license = 'MIT'
13
13
  spec.required_ruby_version = Gem::Requirement.new('>= 2.2.0')
@@ -18,9 +18,9 @@ module Kind
18
18
  private_constant :WRONG_NUMBER_OF_ARGUMENTS
19
19
 
20
20
  def self.is(expected = Undefined, object = Undefined)
21
- return Is if expected == Undefined && object == Undefined
21
+ return Is if Undefined == expected && Undefined == object
22
22
 
23
- return Kind::Is.(expected, object) if object != Undefined
23
+ return Kind::Is.(expected, object) if Undefined != object
24
24
 
25
25
  raise ArgumentError, WRONG_NUMBER_OF_ARGUMENTS
26
26
  end
@@ -29,10 +29,16 @@ module Kind
29
29
 
30
30
  private_constant :MODULE_OR_CLASS
31
31
 
32
+ private_class_method def self.__checkers__
33
+ @__checkers__ ||= {}
34
+ end
35
+
36
+ __checkers__
37
+
32
38
  def self.of(kind = Undefined, object = Undefined)
33
- return Of if kind == Undefined && object == Undefined
39
+ return Of if Undefined == kind && Undefined == object
34
40
 
35
- return Kind::Of.(kind, object) if object != Undefined
41
+ return Kind::Of.(kind, object) if Undefined != object
36
42
 
37
43
  __checkers__[kind] ||= begin
38
44
  kind_name = kind.name
@@ -45,8 +51,8 @@ module Kind
45
51
  end
46
52
  end
47
53
 
48
- private_class_method def self.__checkers__
49
- @__checkers__ ||= {}
54
+ def self.of?(kind, *args)
55
+ Kind.of(kind).instance?(*args)
50
56
  end
51
57
 
52
58
  # --------------------- #
@@ -55,20 +61,19 @@ module Kind
55
61
 
56
62
  module Is
57
63
  def self.Class(value)
58
- value.is_a?(::Class)
64
+ value.kind_of?(::Class)
59
65
  end
60
66
 
61
67
  def self.Module(value)
62
- value == ::Module || (value.is_a?(::Module) && !self.Class(value))
68
+ ::Module == value || (value.is_a?(::Module) && !self.Class(value))
63
69
  end
64
70
 
65
71
  def self.Boolean(value)
66
- klass = Kind.of.Class(value)
67
- klass <= TrueClass || klass <= FalseClass
72
+ Kind.of.Class(value) <= TrueClass || value <= FalseClass
68
73
  end
69
74
 
70
75
  def self.Callable(value)
71
- value.respond_to?(:call) || (value.is_a?(Module) && value.public_instance_methods.include?(:call))
76
+ value.respond_to?(:call)
72
77
  end
73
78
  end
74
79
 
@@ -76,7 +81,7 @@ module Kind
76
81
  # -- Class
77
82
 
78
83
  def self.Class(object = Undefined)
79
- return Class if object == Undefined
84
+ return Class if Undefined == object
80
85
 
81
86
  self.call(::Class, object)
82
87
  end
@@ -88,13 +93,17 @@ module Kind
88
93
 
89
94
  def self.class?(value); Kind::Is.Class(value); end
90
95
 
91
- def self.instance?(value); class?(value); end
96
+ def self.__is_instance__(value); class?(value); end
92
97
  end)
93
98
 
99
+ def self.Class?(*args)
100
+ Kind::Of::Class.instance?(*args)
101
+ end
102
+
94
103
  # -- Module
95
104
 
96
105
  def self.Module(object = Undefined)
97
- return Module if object == Undefined
106
+ return Module if Undefined == object
98
107
 
99
108
  self.call(::Module, object)
100
109
  end
@@ -102,7 +111,11 @@ module Kind
102
111
  const_set(:Module, ::Module.new do
103
112
  extend Checkable
104
113
 
105
- def self.__kind; ::Module; end
114
+ def self.__kind_undefined(value)
115
+ __kind_error(Kind::Undefined) if Kind::Undefined == value
116
+
117
+ yield
118
+ end
106
119
 
107
120
  def self.__kind_error(value)
108
121
  raise Kind::Error.new('Module'.freeze, value)
@@ -114,11 +127,7 @@ module Kind
114
127
  __kind_error(value)
115
128
  end
116
129
 
117
- def self.__kind_undefined(value)
118
- __kind_error(Kind::Undefined) if value == Kind::Undefined
119
-
120
- yield
121
- end
130
+ def self.__kind; ::Module; end
122
131
 
123
132
  def self.class?(value); Kind::Is.Module(value); end
124
133
 
@@ -128,21 +137,25 @@ module Kind
128
137
  if ::Kind::Maybe::Value.none?(default)
129
138
  __kind_undefined(value) { __kind_of(value) }
130
139
  else
131
- return value if instance?(value)
140
+ return value if Kind::Undefined != value && instance?(value)
132
141
 
133
142
  __kind_undefined(default) { __kind_of(default) }
134
143
  end
135
144
  end
136
145
 
137
- def self.instance?(value); class?(value); end
146
+ def self.__is_instance__(value); class?(value); end
138
147
  end)
139
148
 
149
+ def self.Module?(*args)
150
+ Kind::Of::Module.instance?(*args)
151
+ end
152
+
140
153
  # -- Boolean
141
154
 
142
155
  def self.Boolean(object = Undefined, options = Empty::HASH)
143
156
  default = options[:or]
144
157
 
145
- return Kind::Of::Boolean if object == Undefined && default.nil?
158
+ return Kind::Of::Boolean if Undefined == object && default.nil?
146
159
 
147
160
  bool = object.nil? ? default : object
148
161
 
@@ -164,21 +177,21 @@ module Kind
164
177
  if ::Kind::Maybe::Value.none?(default)
165
178
  __kind_undefined(value) { Kind::Of::Boolean(value) }
166
179
  else
167
- return value if instance?(value)
180
+ return value if Kind::Undefined != value && instance?(value)
168
181
 
169
182
  __kind_undefined(default) { Kind::Of::Boolean(default) }
170
183
  end
171
184
  end
172
185
 
173
186
  def self.__kind_undefined(value)
174
- if value == Kind::Undefined
187
+ if Kind::Undefined == value
175
188
  raise Kind::Error.new('Boolean'.freeze, Kind::Undefined)
176
189
  else
177
190
  yield
178
191
  end
179
192
  end
180
193
 
181
- def self.instance?(value);
194
+ def self.__is_instance__(value);
182
195
  value.is_a?(TrueClass) || value.is_a?(FalseClass)
183
196
  end
184
197
 
@@ -188,12 +201,16 @@ module Kind
188
201
  end
189
202
  end)
190
203
 
204
+ def self.Boolean?(*args)
205
+ Kind::Of::Boolean.instance?(*args)
206
+ end
207
+
191
208
  # -- Lambda
192
209
 
193
210
  def self.Lambda(object = Undefined, options = Empty::HASH)
194
211
  default = options[:or]
195
212
 
196
- return Kind::Of::Lambda if object == Undefined && default.nil?
213
+ return Kind::Of::Lambda if Undefined == object && default.nil?
197
214
 
198
215
  func = object || default
199
216
 
@@ -213,31 +230,35 @@ module Kind
213
230
  if ::Kind::Maybe::Value.none?(default)
214
231
  __kind_undefined(value) { Kind::Of::Lambda(value) }
215
232
  else
216
- return value if instance?(value)
233
+ return value if Kind::Undefined != value && instance?(value)
217
234
 
218
235
  __kind_undefined(default) { Kind::Of::Lambda(default) }
219
236
  end
220
237
  end
221
238
 
222
239
  def self.__kind_undefined(value)
223
- if value == Kind::Undefined
240
+ if Kind::Undefined == value
224
241
  raise Kind::Error.new('Lambda'.freeze, Kind::Undefined)
225
242
  else
226
243
  yield
227
244
  end
228
245
  end
229
246
 
230
- def self.instance?(value)
247
+ def self.__is_instance__(value)
231
248
  value.is_a?(__kind) && value.lambda?
232
249
  end
233
250
  end)
234
251
 
252
+ def self.Lambda?(*args)
253
+ Kind::Of::Lambda.instance?(*args)
254
+ end
255
+
235
256
  # -- Callable
236
257
 
237
258
  def self.Callable(object = Undefined, options = Empty::HASH)
238
259
  default = options[:or]
239
260
 
240
- return Kind::Of::Callable if object == Undefined && default.nil?
261
+ return Kind::Of::Callable if Undefined == object && default.nil?
241
262
 
242
263
  callable = object || default
243
264
 
@@ -261,25 +282,29 @@ module Kind
261
282
  if ::Kind::Maybe::Value.none?(default)
262
283
  __kind_undefined(value) { Kind::Of::Callable(value) }
263
284
  else
264
- return value if instance?(value)
285
+ return value if Kind::Undefined != value && instance?(value)
265
286
 
266
287
  __kind_undefined(default) { Kind::Of::Callable(default) }
267
288
  end
268
289
  end
269
290
 
270
291
  def self.__kind_undefined(value)
271
- if value == Kind::Undefined
292
+ if Kind::Undefined == value
272
293
  raise Kind::Error.new('Callable'.freeze, Kind::Undefined)
273
294
  else
274
295
  yield
275
296
  end
276
297
  end
277
298
 
278
- def self.instance?(value);
299
+ def self.__is_instance__(value);
279
300
  value.respond_to?(:call)
280
301
  end
281
302
  end)
282
303
 
304
+ def self.Callable?(*args)
305
+ Kind::Of::Callable.instance?(*args)
306
+ end
307
+
283
308
  # ---------------------- #
284
309
  # Built-in type checkers #
285
310
  # ---------------------- #
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ class KindValidator < ActiveModel::EachValidator
4
+ def validate_each(record, attribute, value)
5
+ return if options[:allow_nil] && value.nil?
6
+
7
+ return unless error = call_validation_for(attribute, value)
8
+
9
+ raise Kind::Error.new("#{attribute} #{error}") if options[:strict]
10
+
11
+ record.errors.add(attribute, error)
12
+ end
13
+
14
+ private
15
+
16
+ def call_validation_for(attribute, value)
17
+ expected = options[:with] || options[:in]
18
+
19
+ return validate_with_default_strategy(expected, value) if expected
20
+
21
+ return kind_of(expected, value) if expected = options[:of]
22
+ return kind_is(expected, value) if expected = options[:is]
23
+ return respond_to(expected, value) if expected = options[:respond_to]
24
+ return instance_of(expected, value) if expected = options[:instance_of]
25
+ return array_with(expected, value) if expected = options[:array_with]
26
+ return array_of(expected, value) if expected = options[:array_of]
27
+
28
+ raise Kind::Validator::InvalidDefinition.new(attribute)
29
+ end
30
+
31
+ def validate_with_default_strategy(expected, value)
32
+ send(Kind::Validator.default_strategy, expected, value)
33
+ end
34
+
35
+ def kind_of(expected, value)
36
+ types = Array(expected)
37
+
38
+ return if types.any? { |type| value.kind_of?(type) }
39
+
40
+ "must be a kind of: #{types.map { |klass| klass.name }.join(', ')}"
41
+ end
42
+
43
+ CLASS_OR_MODULE = 'Class/Module'.freeze
44
+
45
+ def kind_is(expected, value)
46
+ return kind_is_not(expected, value) unless expected.kind_of?(Array)
47
+
48
+ result = expected.map { |kind| kind_is_not(kind, value) }.compact
49
+
50
+ result.empty? || result.size < expected.size ? nil : result.join(', ')
51
+ end
52
+
53
+ def kind_is_not(expected, value)
54
+ case expected
55
+ when Class
56
+ return if expected == Kind.of.Class(value) || value < expected
57
+
58
+ "must be the class or a subclass of `#{expected.name}`"
59
+ when Module
60
+ return if value.kind_of?(Class) && value <= expected
61
+ return if expected == Kind.of.Module(value) || value.kind_of?(expected)
62
+
63
+ "must include the `#{expected.name}` module"
64
+ else
65
+ raise Kind::Error.new(CLASS_OR_MODULE, expected)
66
+ end
67
+ end
68
+
69
+ def respond_to(method_name, value)
70
+ return if value.respond_to?(method_name)
71
+
72
+ "must respond to the method `#{method_name}`"
73
+ end
74
+
75
+ def instance_of(expected, value)
76
+ types = Array(expected)
77
+
78
+ return if types.any? { |type| value.instance_of?(type) }
79
+
80
+ "must be an instance of: #{types.map { |klass| klass.name }.join(', ')}"
81
+ end
82
+
83
+ def array_with(expected, value)
84
+ return if value.kind_of?(Array) && !value.empty? && (value - Kind.of.Array(expected)).empty?
85
+
86
+ "must be an array with: #{expected.join(', ')}"
87
+ end
88
+
89
+ def array_of(expected, value)
90
+ types = Array(expected)
91
+
92
+ return if value.kind_of?(Array) && !value.empty? && value.all? { |value| types.any? { |type| value.kind_of?(type) } }
93
+
94
+ "must be an array of: #{types.map { |klass| klass.name }.join(', ')}"
95
+ end
96
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind'
4
+ require 'kind/validator'
5
+ require 'active_model'
6
+ require 'active_model/validations'
7
+ require_relative 'kind_validator'
@@ -11,15 +11,34 @@ module Kind
11
11
 
12
12
  return Kind::Of.(__kind, value) if ::Kind::Maybe::Value.none?(default)
13
13
 
14
- instance?(value) ? value : Kind::Of.(__kind, default)
14
+ Kind::Undefined != value && instance?(value) ? value : Kind::Of.(__kind, default)
15
15
  end
16
16
 
17
17
  def [](value, options = options = Empty::HASH)
18
18
  instance(value, options)
19
19
  end
20
20
 
21
- def instance?(value)
22
- value.is_a?(__kind)
21
+ def to_proc
22
+ @to_proc ||=
23
+ -> checker { -> value { checker.instance(value) } }.call(self)
24
+ end
25
+
26
+ def __is_instance__(value)
27
+ value.kind_of?(__kind)
28
+ end
29
+
30
+ def is_instance_to_proc
31
+ @is_instance_to_proc ||=
32
+ -> checker { -> value { checker.__is_instance__(value) } }.call(self)
33
+ end
34
+
35
+ def instance?(*args)
36
+ return is_instance_to_proc if args.empty?
37
+
38
+ return args.all? { |object| __is_instance__(object) } if args.size > 1
39
+
40
+ arg = args[0]
41
+ Kind::Undefined == arg ? is_instance_to_proc : __is_instance__(arg)
23
42
  end
24
43
 
25
44
  def or_nil(value)
@@ -29,6 +48,25 @@ module Kind
29
48
  def or_undefined(value)
30
49
  or_nil(value) || Kind::Undefined
31
50
  end
51
+
52
+ def __as_maybe__(value)
53
+ Kind::Maybe.new(or_nil(value))
54
+ end
55
+
56
+ def as_maybe_to_proc
57
+ @as_maybe_to_proc ||=
58
+ -> checker { -> value { checker.__as_maybe__(value) } }.call(self)
59
+ end
60
+
61
+ def as_maybe(value = Kind::Undefined)
62
+ return __as_maybe__(value) if Kind::Undefined != value
63
+
64
+ as_maybe_to_proc
65
+ end
66
+
67
+ def as_optional(value = Kind::Undefined)
68
+ as_maybe(value)
69
+ end
32
70
  end
33
71
 
34
72
  private_constant :Checkable
@@ -2,8 +2,18 @@
2
2
 
3
3
  module Kind
4
4
  class Error < TypeError
5
- def initialize(kind_name, object)
6
- super("#{object.inspect} expected to be a kind of #{kind_name}")
5
+ UNDEFINED_OBJECT = Object.new
6
+
7
+ private_constant :UNDEFINED_OBJECT
8
+
9
+ def initialize(arg, object = UNDEFINED_OBJECT)
10
+ if UNDEFINED_OBJECT == object
11
+ # Will be used when the exception was raised with a message. e.g:
12
+ # raise Kind::Error, "some message"
13
+ super(arg)
14
+ else
15
+ super("#{object.inspect} expected to be a kind of #{arg}")
16
+ end
7
17
  end
8
18
  end
9
19
  end