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.
- 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: []
|