kind 1.8.0 → 2.3.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')
@@ -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