kind 1.8.0 → 2.3.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')
@@ -4,12 +4,12 @@ require 'kind/version'
4
4
 
5
5
  require 'kind/empty'
6
6
  require 'kind/undefined'
7
+ require 'kind/checker'
7
8
  require 'kind/maybe'
8
9
 
9
10
  require 'kind/error'
10
11
  require 'kind/of'
11
12
  require 'kind/is'
12
- require 'kind/checker'
13
13
  require 'kind/types'
14
14
 
15
15
  module Kind
@@ -18,35 +18,23 @@ 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
27
27
 
28
- MODULE_OR_CLASS = 'Module/Class'.freeze
29
-
30
- private_constant :MODULE_OR_CLASS
31
-
32
28
  def self.of(kind = Undefined, object = Undefined)
33
- return Of if kind == Undefined && object == Undefined
29
+ return Of if Undefined == kind && Undefined == object
34
30
 
35
- return Kind::Of.(kind, object) if object != Undefined
31
+ return Kind::Of.(kind, object) if Undefined != object
36
32
 
37
- __checkers__[kind] ||= begin
38
- kind_name = kind.name
39
-
40
- if Kind::Of.const_defined?(kind_name, false)
41
- Kind::Of.const_get(kind_name)
42
- else
43
- Checker.new(Kind::Of.(Module, kind, MODULE_OR_CLASS))
44
- end
45
- end
33
+ Kind::Checker::Factory.create(kind)
46
34
  end
47
35
 
48
- private_class_method def self.__checkers__
49
- @__checkers__ ||= {}
36
+ def self.of?(kind, *args)
37
+ Kind.of(kind).instance?(*args)
50
38
  end
51
39
 
52
40
  # --------------------- #
@@ -55,20 +43,19 @@ module Kind
55
43
 
56
44
  module Is
57
45
  def self.Class(value)
58
- value.is_a?(::Class)
46
+ value.kind_of?(::Class)
59
47
  end
60
48
 
61
49
  def self.Module(value)
62
- value == ::Module || (value.is_a?(::Module) && !self.Class(value))
50
+ ::Module == value || (value.is_a?(::Module) && !self.Class(value))
63
51
  end
64
52
 
65
53
  def self.Boolean(value)
66
- klass = Kind.of.Class(value)
67
- klass <= TrueClass || klass <= FalseClass
54
+ Kind.of.Class(value) <= TrueClass || value <= FalseClass
68
55
  end
69
56
 
70
57
  def self.Callable(value)
71
- value.respond_to?(:call) || (value.is_a?(Module) && value.public_instance_methods.include?(:call))
58
+ value.respond_to?(:call)
72
59
  end
73
60
  end
74
61
 
@@ -76,13 +63,13 @@ module Kind
76
63
  # -- Class
77
64
 
78
65
  def self.Class(object = Undefined)
79
- return Class if object == Undefined
66
+ return Class if Undefined == object
80
67
 
81
68
  self.call(::Class, object)
82
69
  end
83
70
 
84
71
  const_set(:Class, ::Module.new do
85
- extend Checkable
72
+ extend Checker::Protocol
86
73
 
87
74
  def self.__kind; ::Class; end
88
75
 
@@ -91,18 +78,26 @@ module Kind
91
78
  def self.__is_instance__(value); class?(value); end
92
79
  end)
93
80
 
81
+ def self.Class?(*args)
82
+ Kind::Of::Class.instance?(*args)
83
+ end
84
+
94
85
  # -- Module
95
86
 
96
87
  def self.Module(object = Undefined)
97
- return Module if object == Undefined
88
+ return Module if Undefined == object
98
89
 
99
90
  self.call(::Module, object)
100
91
  end
101
92
 
102
93
  const_set(:Module, ::Module.new do
103
- extend Checkable
94
+ extend Checker::Protocol
104
95
 
105
- def self.__kind; ::Module; end
96
+ def self.__kind_undefined(value)
97
+ __kind_error(Kind::Undefined) if Kind::Undefined == value
98
+
99
+ yield
100
+ end
106
101
 
107
102
  def self.__kind_error(value)
108
103
  raise Kind::Error.new('Module'.freeze, value)
@@ -114,11 +109,7 @@ module Kind
114
109
  __kind_error(value)
115
110
  end
116
111
 
117
- def self.__kind_undefined(value)
118
- __kind_error(Kind::Undefined) if value == Kind::Undefined
119
-
120
- yield
121
- end
112
+ def self.__kind; ::Module; end
122
113
 
123
114
  def self.class?(value); Kind::Is.Module(value); end
124
115
 
@@ -128,7 +119,7 @@ module Kind
128
119
  if ::Kind::Maybe::Value.none?(default)
129
120
  __kind_undefined(value) { __kind_of(value) }
130
121
  else
131
- return value if value != Kind::Undefined && instance?(value)
122
+ return value if Kind::Undefined != value && instance?(value)
132
123
 
133
124
  __kind_undefined(default) { __kind_of(default) }
134
125
  end
@@ -137,12 +128,16 @@ module Kind
137
128
  def self.__is_instance__(value); class?(value); end
138
129
  end)
139
130
 
131
+ def self.Module?(*args)
132
+ Kind::Of::Module.instance?(*args)
133
+ end
134
+
140
135
  # -- Boolean
141
136
 
142
137
  def self.Boolean(object = Undefined, options = Empty::HASH)
143
138
  default = options[:or]
144
139
 
145
- return Kind::Of::Boolean if object == Undefined && default.nil?
140
+ return Kind::Of::Boolean if Undefined == object && default.nil?
146
141
 
147
142
  bool = object.nil? ? default : object
148
143
 
@@ -152,7 +147,7 @@ module Kind
152
147
  end
153
148
 
154
149
  const_set(:Boolean, ::Module.new do
155
- extend Checkable
150
+ extend Checker::Protocol
156
151
 
157
152
  def self.__kind; [TrueClass, FalseClass].freeze; end
158
153
 
@@ -164,14 +159,14 @@ module Kind
164
159
  if ::Kind::Maybe::Value.none?(default)
165
160
  __kind_undefined(value) { Kind::Of::Boolean(value) }
166
161
  else
167
- return value if value != Kind::Undefined && instance?(value)
162
+ return value if Kind::Undefined != value && instance?(value)
168
163
 
169
164
  __kind_undefined(default) { Kind::Of::Boolean(default) }
170
165
  end
171
166
  end
172
167
 
173
168
  def self.__kind_undefined(value)
174
- if value == Kind::Undefined
169
+ if Kind::Undefined == value
175
170
  raise Kind::Error.new('Boolean'.freeze, Kind::Undefined)
176
171
  else
177
172
  yield
@@ -188,12 +183,16 @@ module Kind
188
183
  end
189
184
  end)
190
185
 
186
+ def self.Boolean?(*args)
187
+ Kind::Of::Boolean.instance?(*args)
188
+ end
189
+
191
190
  # -- Lambda
192
191
 
193
192
  def self.Lambda(object = Undefined, options = Empty::HASH)
194
193
  default = options[:or]
195
194
 
196
- return Kind::Of::Lambda if object == Undefined && default.nil?
195
+ return Kind::Of::Lambda if Undefined == object && default.nil?
197
196
 
198
197
  func = object || default
199
198
 
@@ -203,7 +202,7 @@ module Kind
203
202
  end
204
203
 
205
204
  const_set(:Lambda, ::Module.new do
206
- extend Checkable
205
+ extend Checker::Protocol
207
206
 
208
207
  def self.__kind; ::Proc; end
209
208
 
@@ -213,14 +212,14 @@ module Kind
213
212
  if ::Kind::Maybe::Value.none?(default)
214
213
  __kind_undefined(value) { Kind::Of::Lambda(value) }
215
214
  else
216
- return value if value != Kind::Undefined && instance?(value)
215
+ return value if Kind::Undefined != value && instance?(value)
217
216
 
218
217
  __kind_undefined(default) { Kind::Of::Lambda(default) }
219
218
  end
220
219
  end
221
220
 
222
221
  def self.__kind_undefined(value)
223
- if value == Kind::Undefined
222
+ if Kind::Undefined == value
224
223
  raise Kind::Error.new('Lambda'.freeze, Kind::Undefined)
225
224
  else
226
225
  yield
@@ -232,12 +231,16 @@ module Kind
232
231
  end
233
232
  end)
234
233
 
234
+ def self.Lambda?(*args)
235
+ Kind::Of::Lambda.instance?(*args)
236
+ end
237
+
235
238
  # -- Callable
236
239
 
237
240
  def self.Callable(object = Undefined, options = Empty::HASH)
238
241
  default = options[:or]
239
242
 
240
- return Kind::Of::Callable if object == Undefined && default.nil?
243
+ return Kind::Of::Callable if Undefined == object && default.nil?
241
244
 
242
245
  callable = object || default
243
246
 
@@ -247,7 +250,7 @@ module Kind
247
250
  end
248
251
 
249
252
  const_set(:Callable, ::Module.new do
250
- extend Checkable
253
+ extend Checker::Protocol
251
254
 
252
255
  def self.__kind; Object; end
253
256
 
@@ -261,14 +264,14 @@ module Kind
261
264
  if ::Kind::Maybe::Value.none?(default)
262
265
  __kind_undefined(value) { Kind::Of::Callable(value) }
263
266
  else
264
- return value if value != Kind::Undefined && instance?(value)
267
+ return value if Kind::Undefined != value && instance?(value)
265
268
 
266
269
  __kind_undefined(default) { Kind::Of::Callable(default) }
267
270
  end
268
271
  end
269
272
 
270
273
  def self.__kind_undefined(value)
271
- if value == Kind::Undefined
274
+ if Kind::Undefined == value
272
275
  raise Kind::Error.new('Callable'.freeze, Kind::Undefined)
273
276
  else
274
277
  yield
@@ -280,6 +283,10 @@ module Kind
280
283
  end
281
284
  end)
282
285
 
286
+ def self.Callable?(*args)
287
+ Kind::Of::Callable.instance?(*args)
288
+ end
289
+
283
290
  # ---------------------- #
284
291
  # Built-in type checkers #
285
292
  # ---------------------- #
@@ -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'
@@ -1,70 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'kind/checker/factory'
4
+ require 'kind/checker/protocol'
3
5
  module Kind
4
- module Checkable
5
- def class?(value)
6
- Kind::Is.__call__(__kind, value)
7
- end
8
-
9
- def instance(value, options = Empty::HASH)
10
- default = options[:or]
11
-
12
- return Kind::Of.(__kind, value) if ::Kind::Maybe::Value.none?(default)
13
-
14
- value != Kind::Undefined && instance?(value) ? value : Kind::Of.(__kind, default)
15
- end
16
-
17
- def [](value, options = options = Empty::HASH)
18
- instance(value, options)
19
- end
20
-
21
- def instance?(value = Kind::Undefined)
22
- return __is_instance__(value) if value != Kind::Undefined
23
-
24
- is_instance_to_proc
25
- end
26
-
27
- def __is_instance__(value)
28
- value.is_a?(__kind)
29
- end
30
-
31
- def is_instance_to_proc
32
- @is_instance_to_proc ||=
33
- -> checker { -> value { checker.__is_instance__(value) } }.call(self)
34
- end
35
-
36
- def or_nil(value)
37
- return value if instance?(value)
38
- end
39
-
40
- def or_undefined(value)
41
- or_nil(value) || Kind::Undefined
42
- end
43
-
44
- def as_maybe(value = Kind::Undefined)
45
- return __as_maybe__(value) if value != Kind::Undefined
46
-
47
- as_maybe_to_proc
48
- end
49
-
50
- def as_optional(value = Kind::Undefined)
51
- as_maybe(value)
52
- end
53
-
54
- def __as_maybe__(value)
55
- Kind::Maybe.new(or_nil(value))
56
- end
57
-
58
- def as_maybe_to_proc
59
- @as_maybe_to_proc ||=
60
- -> checker { -> value { checker.__as_maybe__(value) } }.call(self)
61
- end
62
- end
63
-
64
- private_constant :Checkable
65
-
66
6
  class Checker
67
- include Checkable
7
+ include Protocol
68
8
 
69
9
  attr_reader :__kind
70
10