kind 1.7.0 → 2.2.0

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