kind 1.8.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.sh +20 -0
- data/.travis.yml +7 -2
- data/Gemfile +31 -6
- data/README.md +478 -75
- data/kind.gemspec +2 -2
- data/lib/kind.rb +55 -48
- data/lib/kind/active_model/kind_validator.rb +96 -0
- data/lib/kind/active_model/validation.rb +7 -0
- data/lib/kind/checker.rb +3 -63
- data/lib/kind/checker/factory.rb +35 -0
- data/lib/kind/checker/protocol.rb +73 -0
- data/lib/kind/error.rb +12 -2
- data/lib/kind/maybe.rb +60 -10
- data/lib/kind/types.rb +15 -4
- data/lib/kind/undefined.rb +1 -1
- data/lib/kind/validator.rb +40 -0
- data/lib/kind/version.rb +1 -1
- data/test.sh +11 -0
- metadata +11 -4
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
|
5
|
+
module Kind
|
6
|
+
class Checker
|
7
|
+
class Factory
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
def self.create(kind)
|
11
|
+
instance.create(kind)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@__checkers__ = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
MODULE_OR_CLASS = 'Module/Class'.freeze
|
19
|
+
|
20
|
+
private_constant :MODULE_OR_CLASS
|
21
|
+
|
22
|
+
def create(kind)
|
23
|
+
@__checkers__[kind] ||= begin
|
24
|
+
kind_name = kind.name
|
25
|
+
|
26
|
+
if Kind::Of.const_defined?(kind_name, false)
|
27
|
+
Kind::Of.const_get(kind_name)
|
28
|
+
else
|
29
|
+
Kind::Checker.new(Kind::Of.(Module, kind, MODULE_OR_CLASS))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kind
|
4
|
+
class Checker
|
5
|
+
module Protocol
|
6
|
+
def class?(value)
|
7
|
+
Kind::Is.__call__(__kind, value)
|
8
|
+
end
|
9
|
+
|
10
|
+
def instance(value, options = Empty::HASH)
|
11
|
+
default = options[:or]
|
12
|
+
|
13
|
+
return Kind::Of.(__kind, value) if ::Kind::Maybe::Value.none?(default)
|
14
|
+
|
15
|
+
Kind::Undefined != value && instance?(value) ? value : Kind::Of.(__kind, default)
|
16
|
+
end
|
17
|
+
|
18
|
+
def [](value, options = options = Empty::HASH)
|
19
|
+
instance(value, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_proc
|
23
|
+
@to_proc ||=
|
24
|
+
-> checker { -> value { checker.instance(value) } }.call(self)
|
25
|
+
end
|
26
|
+
|
27
|
+
def __is_instance__(value)
|
28
|
+
value.kind_of?(__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 instance?(*args)
|
37
|
+
return is_instance_to_proc if args.empty?
|
38
|
+
|
39
|
+
return args.all? { |object| __is_instance__(object) } if args.size > 1
|
40
|
+
|
41
|
+
arg = args[0]
|
42
|
+
Kind::Undefined == arg ? is_instance_to_proc : __is_instance__(arg)
|
43
|
+
end
|
44
|
+
|
45
|
+
def or_nil(value)
|
46
|
+
return value if instance?(value)
|
47
|
+
end
|
48
|
+
|
49
|
+
def or_undefined(value)
|
50
|
+
or_nil(value) || Kind::Undefined
|
51
|
+
end
|
52
|
+
|
53
|
+
def __as_maybe__(value)
|
54
|
+
Kind::Maybe.new(or_nil(value))
|
55
|
+
end
|
56
|
+
|
57
|
+
def as_maybe_to_proc
|
58
|
+
@as_maybe_to_proc ||=
|
59
|
+
-> checker { -> value { checker.__as_maybe__(value) } }.call(self)
|
60
|
+
end
|
61
|
+
|
62
|
+
def as_maybe(value = Kind::Undefined)
|
63
|
+
return __as_maybe__(value) if Kind::Undefined != value
|
64
|
+
|
65
|
+
as_maybe_to_proc
|
66
|
+
end
|
67
|
+
|
68
|
+
def as_optional(value = Kind::Undefined)
|
69
|
+
as_maybe(value)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/kind/error.rb
CHANGED
@@ -2,8 +2,18 @@
|
|
2
2
|
|
3
3
|
module Kind
|
4
4
|
class Error < TypeError
|
5
|
-
|
6
|
-
|
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
|
data/lib/kind/maybe.rb
CHANGED
@@ -2,9 +2,22 @@
|
|
2
2
|
|
3
3
|
module Kind
|
4
4
|
module Maybe
|
5
|
+
class Typed
|
6
|
+
def initialize(kind)
|
7
|
+
@kind_checker = Kind::Checker::Factory.create(kind)
|
8
|
+
end
|
9
|
+
|
10
|
+
def wrap(value)
|
11
|
+
@kind_checker.as_maybe(value)
|
12
|
+
end
|
13
|
+
|
14
|
+
alias_method :new, :wrap
|
15
|
+
alias_method :[], :wrap
|
16
|
+
end
|
17
|
+
|
5
18
|
module Value
|
6
19
|
def self.none?(value)
|
7
|
-
value
|
20
|
+
value.nil? || Undefined == value
|
8
21
|
end
|
9
22
|
|
10
23
|
def self.some?(value)
|
@@ -16,7 +29,7 @@ module Kind
|
|
16
29
|
attr_reader :value
|
17
30
|
|
18
31
|
def initialize(value)
|
19
|
-
@value = value
|
32
|
+
@value = value.kind_of?(Result) ? value.value : value
|
20
33
|
end
|
21
34
|
|
22
35
|
def value_or(method_name = Undefined, &block)
|
@@ -42,9 +55,9 @@ module Kind
|
|
42
55
|
INVALID_DEFAULT_ARG = 'the default value must be defined as an argument or block'.freeze
|
43
56
|
|
44
57
|
def value_or(default = Undefined, &block)
|
45
|
-
raise ArgumentError, INVALID_DEFAULT_ARG if
|
58
|
+
raise ArgumentError, INVALID_DEFAULT_ARG if Undefined == default && !block
|
46
59
|
|
47
|
-
|
60
|
+
Undefined != default ? default : block.call
|
48
61
|
end
|
49
62
|
|
50
63
|
def none?; true; end
|
@@ -56,7 +69,7 @@ module Kind
|
|
56
69
|
alias_method :then, :map
|
57
70
|
|
58
71
|
def try(method_name = Undefined, &block)
|
59
|
-
Kind.of.Symbol(method_name) if
|
72
|
+
Kind.of.Symbol(method_name) if Undefined != method_name
|
60
73
|
|
61
74
|
nil
|
62
75
|
end
|
@@ -79,8 +92,9 @@ module Kind
|
|
79
92
|
def map(&fn)
|
80
93
|
result = fn.call(@value)
|
81
94
|
|
82
|
-
return
|
83
|
-
return
|
95
|
+
return result if Maybe::None === result
|
96
|
+
return NONE_WITH_NIL_VALUE if result.nil?
|
97
|
+
return NONE_WITH_UNDEFINED_VALUE if Undefined == result
|
84
98
|
|
85
99
|
Some.new(result)
|
86
100
|
end
|
@@ -88,7 +102,7 @@ module Kind
|
|
88
102
|
alias_method :then, :map
|
89
103
|
|
90
104
|
def try(method_name = Undefined, *args, &block)
|
91
|
-
fn =
|
105
|
+
fn = Undefined == method_name ? block : Kind.of.Symbol(method_name).to_proc
|
92
106
|
|
93
107
|
result = args.empty? ? fn.call(value) : fn.call(*args.unshift(value))
|
94
108
|
|
@@ -98,13 +112,49 @@ module Kind
|
|
98
112
|
|
99
113
|
def self.new(value)
|
100
114
|
result_type = Maybe::Value.none?(value) ? None : Some
|
101
|
-
result_type.new(value
|
115
|
+
result_type.new(value)
|
102
116
|
end
|
103
117
|
|
104
|
-
def self.[](value)
|
118
|
+
def self.[](value)
|
105
119
|
new(value)
|
106
120
|
end
|
121
|
+
|
122
|
+
def self.wrap(value)
|
123
|
+
new(value)
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.none
|
127
|
+
NONE_WITH_NIL_VALUE
|
128
|
+
end
|
129
|
+
|
130
|
+
VALUE_CANT_BE_NONE = "value can't be nil or Kind::Undefined".freeze
|
131
|
+
|
132
|
+
private_constant :VALUE_CANT_BE_NONE
|
133
|
+
|
134
|
+
def self.some(value)
|
135
|
+
return Maybe::Some.new(value) if Value.some?(value)
|
136
|
+
|
137
|
+
raise ArgumentError, VALUE_CANT_BE_NONE
|
138
|
+
end
|
107
139
|
end
|
108
140
|
|
109
141
|
Optional = Maybe
|
142
|
+
|
143
|
+
None = Maybe.none
|
144
|
+
|
145
|
+
def self.None
|
146
|
+
Kind::None
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.Some(value)
|
150
|
+
Maybe.some(value)
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.Maybe(kind)
|
154
|
+
Maybe::Typed.new(kind)
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.Optional(kind)
|
158
|
+
Maybe::Typed.new(kind)
|
159
|
+
end
|
110
160
|
end
|
data/lib/kind/types.rb
CHANGED
@@ -10,15 +10,25 @@ module Kind
|
|
10
10
|
def self.%{method_name}(object = Undefined, options = Empty::HASH)
|
11
11
|
default = options[:or]
|
12
12
|
|
13
|
-
return Kind::Of::%{kind_name} if
|
13
|
+
return Kind::Of::%{kind_name} if Undefined == object && default.nil?
|
14
14
|
|
15
|
-
Kind::Of
|
15
|
+
is_instance = Kind::Of::%{kind_name}.__is_instance__(object)
|
16
|
+
|
17
|
+
return object if is_instance
|
18
|
+
|
19
|
+
Kind::Of.(::%{kind_name_to_check}, object && default ? default : object || default)
|
20
|
+
end
|
21
|
+
RUBY
|
22
|
+
|
23
|
+
KIND_OF_IS = <<-RUBY
|
24
|
+
def self.%{method_name}?(*args)
|
25
|
+
Kind::Of::%{kind_name}.instance?(*args)
|
16
26
|
end
|
17
27
|
RUBY
|
18
28
|
|
19
29
|
KIND_IS = <<-RUBY
|
20
30
|
def self.%{method_name}(value = Undefined)
|
21
|
-
return Kind::Is::%{kind_name} if
|
31
|
+
return Kind::Is::%{kind_name} if Undefined == value
|
22
32
|
|
23
33
|
Kind::Is.__call__(::%{kind_name_to_check}, value)
|
24
34
|
end
|
@@ -88,11 +98,12 @@ module Kind
|
|
88
98
|
kind_name = params[:kind_name]
|
89
99
|
params[:kind_name_to_check] ||= kind_name
|
90
100
|
|
91
|
-
kind_checker = ::Module.new { extend
|
101
|
+
kind_checker = ::Module.new { extend Checker::Protocol }
|
92
102
|
kind_checker.module_eval("def self.__kind; #{params[:kind_name_to_check]}; end")
|
93
103
|
|
94
104
|
kind_of_mod.instance_eval(KIND_OF % params)
|
95
105
|
kind_of_mod.const_set(method_name, kind_checker)
|
106
|
+
kind_of_mod.instance_eval(KIND_OF_IS % params)
|
96
107
|
end
|
97
108
|
|
98
109
|
unless kind_is_mod.respond_to?(method_name)
|
data/lib/kind/undefined.rb
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kind
|
4
|
+
module Validator
|
5
|
+
DEFAULT_STRATEGIES = Set.new(%w[instance_of kind_of]).freeze
|
6
|
+
|
7
|
+
class InvalidDefinition < ArgumentError
|
8
|
+
OPTIONS = 'Options to define one: :of, :is, :respond_to, :instance_of, :array_of or :array_with'.freeze
|
9
|
+
|
10
|
+
def initialize(attribute)
|
11
|
+
super "invalid type definition for :#{attribute} attribute. #{OPTIONS}"
|
12
|
+
end
|
13
|
+
|
14
|
+
private_constant :OPTIONS
|
15
|
+
end
|
16
|
+
|
17
|
+
class InvalidDefaultStrategy < ArgumentError
|
18
|
+
OPTIONS =
|
19
|
+
DEFAULT_STRATEGIES.map { |option| ":#{option}" }.join(', ')
|
20
|
+
|
21
|
+
def initialize(option)
|
22
|
+
super "#{option.inspect} is an invalid option. Please use one of these: #{OPTIONS}"
|
23
|
+
end
|
24
|
+
|
25
|
+
private_constant :OPTIONS
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.default_strategy
|
29
|
+
@default_strategy ||= :kind_of
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.default_strategy=(option)
|
33
|
+
if DEFAULT_STRATEGIES.member?(String(option))
|
34
|
+
@default_strategy = option.to_sym
|
35
|
+
else
|
36
|
+
raise InvalidDefaultStrategy.new(option)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/kind/version.rb
CHANGED
data/test.sh
ADDED
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kind
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rodrigo Serradura
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description:
|
13
|
+
description: A simple type system (at runtime) for Ruby - free of dependencies.
|
14
14
|
email:
|
15
15
|
- rodrigo.serradura@gmail.com
|
16
16
|
executables: []
|
@@ -18,6 +18,7 @@ extensions: []
|
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
20
|
- ".gitignore"
|
21
|
+
- ".travis.sh"
|
21
22
|
- ".travis.yml"
|
22
23
|
- CODE_OF_CONDUCT.md
|
23
24
|
- Gemfile
|
@@ -28,7 +29,11 @@ files:
|
|
28
29
|
- bin/setup
|
29
30
|
- kind.gemspec
|
30
31
|
- lib/kind.rb
|
32
|
+
- lib/kind/active_model/kind_validator.rb
|
33
|
+
- lib/kind/active_model/validation.rb
|
31
34
|
- lib/kind/checker.rb
|
35
|
+
- lib/kind/checker/factory.rb
|
36
|
+
- lib/kind/checker/protocol.rb
|
32
37
|
- lib/kind/empty.rb
|
33
38
|
- lib/kind/error.rb
|
34
39
|
- lib/kind/is.rb
|
@@ -36,7 +41,9 @@ files:
|
|
36
41
|
- lib/kind/of.rb
|
37
42
|
- lib/kind/types.rb
|
38
43
|
- lib/kind/undefined.rb
|
44
|
+
- lib/kind/validator.rb
|
39
45
|
- lib/kind/version.rb
|
46
|
+
- test.sh
|
40
47
|
homepage: https://github.com/serradura/kind
|
41
48
|
licenses:
|
42
49
|
- MIT
|
@@ -61,5 +68,5 @@ requirements: []
|
|
61
68
|
rubygems_version: 3.0.6
|
62
69
|
signing_key:
|
63
70
|
specification_version: 4
|
64
|
-
summary:
|
71
|
+
summary: A simple type system (at runtime) for Ruby.
|
65
72
|
test_files: []
|