kind 2.2.0 → 4.0.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/.tool-versions +1 -0
- data/.travis.sh +37 -12
- data/.travis.yml +10 -4
- data/CHANGELOG.md +1230 -0
- data/Gemfile +10 -2
- data/README.md +938 -460
- data/lib/kind.rb +52 -298
- data/lib/kind/active_model/kind_validator.rb +7 -7
- data/lib/kind/core.rb +9 -0
- data/lib/kind/core/deprecation.rb +29 -0
- data/lib/kind/core/kind.rb +61 -0
- data/lib/kind/core/undefined.rb +7 -0
- data/lib/kind/deprecations/built_in_type_checkers.rb +23 -0
- data/lib/kind/deprecations/checker.rb +16 -0
- data/lib/kind/deprecations/checker/factory.rb +31 -0
- data/lib/kind/deprecations/checker/protocol.rb +73 -0
- data/lib/kind/deprecations/is.rb +35 -0
- data/lib/kind/deprecations/of.rb +258 -0
- data/lib/kind/{types.rb → deprecations/types.rb} +15 -9
- data/lib/kind/dig.rb +40 -0
- data/lib/kind/empty.rb +5 -11
- data/lib/kind/error.rb +2 -6
- data/lib/kind/maybe.rb +22 -113
- data/lib/kind/maybe/none.rb +57 -0
- data/lib/kind/maybe/result.rb +51 -0
- data/lib/kind/maybe/some.rb +90 -0
- data/lib/kind/maybe/typed.rb +29 -0
- data/lib/kind/maybe/wrappable.rb +35 -0
- data/lib/kind/presence.rb +33 -0
- data/lib/kind/try.rb +36 -0
- data/lib/kind/type_checker.rb +73 -0
- data/lib/kind/type_checkers.rb +30 -0
- data/lib/kind/type_checkers/core/array.rb +17 -0
- data/lib/kind/type_checkers/core/class.rb +13 -0
- data/lib/kind/type_checkers/core/comparable.rb +13 -0
- data/lib/kind/type_checkers/core/enumerable.rb +13 -0
- data/lib/kind/type_checkers/core/enumerator.rb +13 -0
- data/lib/kind/type_checkers/core/file.rb +13 -0
- data/lib/kind/type_checkers/core/float.rb +13 -0
- data/lib/kind/type_checkers/core/hash.rb +17 -0
- data/lib/kind/type_checkers/core/integer.rb +13 -0
- data/lib/kind/type_checkers/core/io.rb +13 -0
- data/lib/kind/type_checkers/core/method.rb +13 -0
- data/lib/kind/type_checkers/core/module.rb +17 -0
- data/lib/kind/type_checkers/core/numeric.rb +13 -0
- data/lib/kind/type_checkers/core/proc.rb +13 -0
- data/lib/kind/type_checkers/core/queue.rb +14 -0
- data/lib/kind/type_checkers/core/range.rb +13 -0
- data/lib/kind/type_checkers/core/regexp.rb +13 -0
- data/lib/kind/type_checkers/core/string.rb +17 -0
- data/lib/kind/type_checkers/core/struct.rb +13 -0
- data/lib/kind/type_checkers/core/symbol.rb +13 -0
- data/lib/kind/type_checkers/core/time.rb +13 -0
- data/lib/kind/type_checkers/custom/boolean.rb +19 -0
- data/lib/kind/type_checkers/custom/callable.rb +19 -0
- data/lib/kind/type_checkers/custom/lambda.rb +19 -0
- data/lib/kind/type_checkers/stdlib/open_struct.rb +13 -0
- data/lib/kind/type_checkers/stdlib/set.rb +17 -0
- data/lib/kind/undefined.rb +4 -2
- data/lib/kind/validator.rb +1 -1
- data/lib/kind/version.rb +1 -1
- data/test.sh +4 -4
- metadata +52 -7
- data/lib/kind/checker.rb +0 -83
- data/lib/kind/is.rb +0 -19
- data/lib/kind/of.rb +0 -11
@@ -8,6 +8,8 @@ module Kind
|
|
8
8
|
|
9
9
|
KIND_OF = <<-RUBY
|
10
10
|
def self.%{method_name}(object = Undefined, options = Empty::HASH)
|
11
|
+
DEPRECATION.warn_method_replacement('Kind::Of::%{method_name}', 'Kind::%{method_name}')
|
12
|
+
|
11
13
|
default = options[:or]
|
12
14
|
|
13
15
|
return Kind::Of::%{kind_name} if Undefined == object && default.nil?
|
@@ -16,29 +18,33 @@ module Kind
|
|
16
18
|
|
17
19
|
return object if is_instance
|
18
20
|
|
19
|
-
|
21
|
+
KIND.of!(::%{kind_name_to_check}, object && default ? default : object || default)
|
20
22
|
end
|
21
23
|
RUBY
|
22
24
|
|
23
25
|
KIND_OF_IS = <<-RUBY
|
24
26
|
def self.%{method_name}?(*args)
|
27
|
+
DEPRECATION.warn_method_replacement('Kind::Of::%{method_name}?', 'Kind::%{method_name}?')
|
28
|
+
|
25
29
|
Kind::Of::%{kind_name}.instance?(*args)
|
26
30
|
end
|
27
31
|
RUBY
|
28
32
|
|
29
33
|
KIND_IS = <<-RUBY
|
30
|
-
def self.%{method_name}(value
|
31
|
-
|
34
|
+
def self.%{method_name}(value)
|
35
|
+
DEPRECATION.warn_removal('Kind::Is::%{method_name}')
|
32
36
|
|
33
|
-
|
37
|
+
KIND.is!(::%{kind_name_to_check}, value)
|
34
38
|
end
|
35
39
|
RUBY
|
36
40
|
|
37
41
|
private_constant :KIND_OF, :KIND_IS, :COLONS
|
38
42
|
|
39
43
|
def add(kind, name: nil)
|
40
|
-
|
41
|
-
|
44
|
+
DEPRECATION.warn_removal('Kind::Types')
|
45
|
+
|
46
|
+
kind_name = KIND.of_module_or_class!(kind).name
|
47
|
+
checker = name ? Kind::String[name] : kind_name
|
42
48
|
|
43
49
|
if checker.include?(COLONS)
|
44
50
|
add_kind_with_namespace(checker, method_name: name)
|
@@ -98,8 +104,8 @@ module Kind
|
|
98
104
|
kind_name = params[:kind_name]
|
99
105
|
params[:kind_name_to_check] ||= kind_name
|
100
106
|
|
101
|
-
kind_checker = ::Module.new { extend
|
102
|
-
kind_checker.module_eval("def self.__kind;
|
107
|
+
kind_checker = ::Module.new { extend Checker::Protocol }
|
108
|
+
kind_checker.module_eval("def self.__kind; ::#{params[:kind_name_to_check]}; end")
|
103
109
|
|
104
110
|
kind_of_mod.instance_eval(KIND_OF % params)
|
105
111
|
kind_of_mod.const_set(method_name, kind_checker)
|
@@ -108,7 +114,7 @@ module Kind
|
|
108
114
|
|
109
115
|
unless kind_is_mod.respond_to?(method_name)
|
110
116
|
kind_is_mod.instance_eval(KIND_IS % params)
|
111
|
-
kind_is_mod.const_set(method_name, Module.new) if create_kind_is_mod
|
117
|
+
kind_is_mod.const_set(method_name, ::Module.new) if create_kind_is_mod
|
112
118
|
end
|
113
119
|
end
|
114
120
|
end
|
data/lib/kind/dig.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kind
|
4
|
+
module Dig
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def call(data, keys)
|
8
|
+
return unless keys.is_a?(::Array)
|
9
|
+
|
10
|
+
keys.reduce(data) do |memo, key|
|
11
|
+
value = get(memo, key)
|
12
|
+
|
13
|
+
break if KIND.null?(value)
|
14
|
+
|
15
|
+
value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def [](*keys)
|
20
|
+
->(data) { call(data, keys) }
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def get(data, key)
|
26
|
+
return data[key] if ::Hash === data
|
27
|
+
|
28
|
+
case data
|
29
|
+
when ::Array
|
30
|
+
data[key] if key.respond_to?(:to_int)
|
31
|
+
when ::OpenStruct
|
32
|
+
data[key] if key.respond_to?(:to_sym)
|
33
|
+
when ::Struct
|
34
|
+
data[key] rescue nil if key.respond_to?(:to_int) || key.respond_to?(:to_sym)
|
35
|
+
else
|
36
|
+
data.public_send(key) if key.respond_to?(:to_sym) && data.respond_to?(key)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/kind/empty.rb
CHANGED
@@ -1,21 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'set'
|
4
|
-
|
5
3
|
module Kind
|
6
4
|
module Empty
|
7
5
|
SET = ::Set.new.freeze
|
8
|
-
|
9
6
|
HASH = {}.freeze
|
7
|
+
ARRAY = [].freeze
|
8
|
+
STRING = ''.freeze
|
10
9
|
|
11
|
-
ARY =
|
12
|
-
|
13
|
-
|
14
|
-
STR = ''.freeze
|
15
|
-
STRING = STR
|
10
|
+
ARY = ARRAY
|
11
|
+
STR = STRING
|
16
12
|
end
|
17
13
|
end
|
18
14
|
|
19
|
-
unless defined?(Empty)
|
20
|
-
Empty = Kind::Empty
|
21
|
-
end
|
15
|
+
Empty = Kind::Empty unless defined?(Empty)
|
data/lib/kind/error.rb
CHANGED
@@ -2,12 +2,8 @@
|
|
2
2
|
|
3
3
|
module Kind
|
4
4
|
class Error < TypeError
|
5
|
-
|
6
|
-
|
7
|
-
private_constant :UNDEFINED_OBJECT
|
8
|
-
|
9
|
-
def initialize(arg, object = UNDEFINED_OBJECT)
|
10
|
-
if UNDEFINED_OBJECT == object
|
5
|
+
def initialize(arg, object = UNDEFINED)
|
6
|
+
if UNDEFINED == object
|
11
7
|
# Will be used when the exception was raised with a message. e.g:
|
12
8
|
# raise Kind::Error, "some message"
|
13
9
|
super(arg)
|
data/lib/kind/maybe.rb
CHANGED
@@ -1,124 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'kind/dig'
|
4
|
+
|
3
5
|
module Kind
|
4
6
|
module Maybe
|
5
|
-
|
6
|
-
def self.none?(value)
|
7
|
-
value.nil? || Undefined == value
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.some?(value)
|
11
|
-
!none?(value)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
class Result
|
16
|
-
attr_reader :value
|
17
|
-
|
18
|
-
def initialize(value)
|
19
|
-
@value = value.kind_of?(Result) ? value.value : value
|
20
|
-
end
|
21
|
-
|
22
|
-
def value_or(method_name = Undefined, &block)
|
23
|
-
raise NotImplementedError
|
24
|
-
end
|
25
|
-
|
26
|
-
def none?
|
27
|
-
raise NotImplementedError
|
28
|
-
end
|
29
|
-
|
30
|
-
def some?; !none?; end
|
31
|
-
|
32
|
-
def map(&fn)
|
33
|
-
raise NotImplementedError
|
34
|
-
end
|
35
|
-
|
36
|
-
def try(method_name = Undefined, &block)
|
37
|
-
raise NotImplementedError
|
38
|
-
end
|
39
|
-
end
|
7
|
+
extend self
|
40
8
|
|
41
|
-
|
42
|
-
|
9
|
+
require 'kind/maybe/result'
|
10
|
+
require 'kind/maybe/none'
|
11
|
+
require 'kind/maybe/some'
|
12
|
+
require 'kind/maybe/wrappable'
|
13
|
+
require 'kind/maybe/typed'
|
43
14
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
Undefined != default ? default : block.call
|
48
|
-
end
|
49
|
-
|
50
|
-
def none?; true; end
|
51
|
-
|
52
|
-
def map(&fn)
|
53
|
-
self
|
54
|
-
end
|
55
|
-
|
56
|
-
alias_method :then, :map
|
57
|
-
|
58
|
-
def try(method_name = Undefined, &block)
|
59
|
-
Kind.of.Symbol(method_name) if Undefined != method_name
|
60
|
-
|
61
|
-
nil
|
62
|
-
end
|
63
|
-
|
64
|
-
private_constant :INVALID_DEFAULT_ARG
|
15
|
+
def new(value)
|
16
|
+
(KIND.null?(value) ? None : Some)
|
17
|
+
.new(value)
|
65
18
|
end
|
66
19
|
|
67
|
-
|
68
|
-
NONE_WITH_UNDEFINED_VALUE = None.new(Undefined)
|
69
|
-
|
70
|
-
private_constant :NONE_WITH_NIL_VALUE, :NONE_WITH_UNDEFINED_VALUE
|
71
|
-
|
72
|
-
class Some < Result
|
73
|
-
def value_or(default = Undefined, &block)
|
74
|
-
@value
|
75
|
-
end
|
76
|
-
|
77
|
-
def none?; false; end
|
78
|
-
|
79
|
-
def map(&fn)
|
80
|
-
result = fn.call(@value)
|
20
|
+
alias_method :[], :new
|
81
21
|
|
82
|
-
|
83
|
-
return NONE_WITH_NIL_VALUE if result.nil?
|
84
|
-
return NONE_WITH_UNDEFINED_VALUE if Undefined == result
|
85
|
-
|
86
|
-
Some.new(result)
|
87
|
-
end
|
88
|
-
|
89
|
-
alias_method :then, :map
|
90
|
-
|
91
|
-
def try(method_name = Undefined, *args, &block)
|
92
|
-
fn = Undefined == method_name ? block : Kind.of.Symbol(method_name).to_proc
|
93
|
-
|
94
|
-
result = args.empty? ? fn.call(value) : fn.call(*args.unshift(value))
|
95
|
-
|
96
|
-
return result if Maybe::Value.some?(result)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def self.new(value)
|
101
|
-
result_type = Maybe::Value.none?(value) ? None : Some
|
102
|
-
result_type.new(value)
|
103
|
-
end
|
104
|
-
|
105
|
-
def self.[](value);
|
106
|
-
new(value)
|
107
|
-
end
|
108
|
-
|
109
|
-
def self.none
|
110
|
-
NONE_WITH_NIL_VALUE
|
111
|
-
end
|
112
|
-
|
113
|
-
VALUE_CANT_BE_NONE = "value can't be nil or Kind::Undefined".freeze
|
114
|
-
|
115
|
-
private_constant :VALUE_CANT_BE_NONE
|
116
|
-
|
117
|
-
def self.some(value)
|
118
|
-
return Maybe::Some.new(value) if Value.some?(value)
|
119
|
-
|
120
|
-
raise ArgumentError, VALUE_CANT_BE_NONE
|
121
|
-
end
|
22
|
+
extend Wrappable
|
122
23
|
end
|
123
24
|
|
124
25
|
Optional = Maybe
|
@@ -126,10 +27,18 @@ module Kind
|
|
126
27
|
None = Maybe.none
|
127
28
|
|
128
29
|
def self.None
|
129
|
-
|
30
|
+
None
|
130
31
|
end
|
131
32
|
|
132
33
|
def self.Some(value)
|
133
34
|
Maybe.some(value)
|
134
35
|
end
|
36
|
+
|
37
|
+
def self.Maybe(kind)
|
38
|
+
Maybe::Typed.new(kind)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.Optional(kind)
|
42
|
+
Maybe::Typed.new(kind)
|
43
|
+
end
|
135
44
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kind
|
4
|
+
module Maybe
|
5
|
+
class None < Result
|
6
|
+
INVALID_DEFAULT_ARG = 'the default value must be defined as an argument or block'.freeze
|
7
|
+
|
8
|
+
def value_or(default = UNDEFINED, &block)
|
9
|
+
raise ArgumentError, INVALID_DEFAULT_ARG if UNDEFINED == default && !block
|
10
|
+
|
11
|
+
UNDEFINED != default ? default : block.call
|
12
|
+
end
|
13
|
+
|
14
|
+
def none?; true; end
|
15
|
+
|
16
|
+
def map(&fn)
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
alias_method :map!, :map
|
21
|
+
alias_method :then, :map
|
22
|
+
alias_method :then!, :map
|
23
|
+
alias_method :check, :map
|
24
|
+
|
25
|
+
def try!(method_name = UNDEFINED, *args, &block)
|
26
|
+
Kind::Symbol[method_name] if UNDEFINED != method_name
|
27
|
+
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
alias_method :try, :try!
|
32
|
+
|
33
|
+
def dig(*keys)
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def presence
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
private_constant :INVALID_DEFAULT_ARG
|
42
|
+
end
|
43
|
+
|
44
|
+
NONE_WITH_NIL_VALUE = None.new(nil)
|
45
|
+
NONE_WITH_UNDEFINED_VALUE = None.new(Undefined)
|
46
|
+
|
47
|
+
def none
|
48
|
+
NONE_WITH_NIL_VALUE
|
49
|
+
end
|
50
|
+
|
51
|
+
def __none__(value) # :nodoc:
|
52
|
+
None.new(value)
|
53
|
+
end
|
54
|
+
|
55
|
+
private_constant :NONE_WITH_NIL_VALUE, :NONE_WITH_UNDEFINED_VALUE
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kind
|
4
|
+
module Maybe
|
5
|
+
class Result
|
6
|
+
attr_reader :value
|
7
|
+
|
8
|
+
Value = ->(arg) { arg.kind_of?(::Kind::Maybe::Result) ? arg.value : arg } # :nodoc:
|
9
|
+
|
10
|
+
def initialize(arg)
|
11
|
+
@value = Value.(arg)
|
12
|
+
end
|
13
|
+
|
14
|
+
def value_or(method_name = UNDEFINED, &block)
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
|
18
|
+
def none?
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
21
|
+
|
22
|
+
def some?; !none?; end
|
23
|
+
|
24
|
+
def map(&fn)
|
25
|
+
raise NotImplementedError
|
26
|
+
end
|
27
|
+
|
28
|
+
alias_method :map!, :map
|
29
|
+
alias_method :then, :map
|
30
|
+
alias_method :then!, :map
|
31
|
+
|
32
|
+
def check(&fn)
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
def try(method_name = UNDEFINED, &block)
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
alias_method :try!, :try
|
41
|
+
|
42
|
+
def dig(*keys)
|
43
|
+
raise NotImplementedError
|
44
|
+
end
|
45
|
+
|
46
|
+
def presence
|
47
|
+
raise NotImplementedError
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kind
|
4
|
+
module Maybe
|
5
|
+
class Some < Result
|
6
|
+
def value_or(default = UNDEFINED, &block)
|
7
|
+
@value
|
8
|
+
end
|
9
|
+
|
10
|
+
def none?; false; end
|
11
|
+
|
12
|
+
def map(&fn)
|
13
|
+
map!(&fn)
|
14
|
+
rescue StandardError => exception
|
15
|
+
None.new(exception)
|
16
|
+
end
|
17
|
+
|
18
|
+
alias_method :then, :map
|
19
|
+
|
20
|
+
def check(&fn)
|
21
|
+
result = fn.call(@value)
|
22
|
+
|
23
|
+
!result || KIND.null?(result) ? NONE_WITH_NIL_VALUE : self
|
24
|
+
end
|
25
|
+
|
26
|
+
def map!(&fn)
|
27
|
+
result = fn.call(@value)
|
28
|
+
|
29
|
+
resolve(result)
|
30
|
+
end
|
31
|
+
|
32
|
+
alias_method :then!, :map!
|
33
|
+
|
34
|
+
def try!(method_name = UNDEFINED, *args, &block)
|
35
|
+
return __try_block__(block, args) if block
|
36
|
+
|
37
|
+
return __try_method__(method_name, args) if UNDEFINED != method_name
|
38
|
+
|
39
|
+
raise ArgumentError, 'method name or a block must be present'
|
40
|
+
end
|
41
|
+
|
42
|
+
def try(method_name = UNDEFINED, *args, &block)
|
43
|
+
return __try_block__(block, args) if block
|
44
|
+
|
45
|
+
return __try_method__(method_name, args) if value.respond_to?(method_name)
|
46
|
+
|
47
|
+
NONE_WITH_NIL_VALUE
|
48
|
+
end
|
49
|
+
|
50
|
+
def dig(*keys)
|
51
|
+
resolve(Dig.(value, keys))
|
52
|
+
end
|
53
|
+
|
54
|
+
def presence
|
55
|
+
resolve(Presence.(value))
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def __try_method__(method_name, args)
|
61
|
+
__try_block__(Kind::Symbol[method_name].to_proc, args)
|
62
|
+
end
|
63
|
+
|
64
|
+
def __try_block__(block, args)
|
65
|
+
result = args.empty? ? block.call(value) : block.call(*args.unshift(value))
|
66
|
+
|
67
|
+
resolve(result)
|
68
|
+
end
|
69
|
+
|
70
|
+
def resolve(result)
|
71
|
+
return result if Maybe::None === result
|
72
|
+
return None.new(result) if Exception === result
|
73
|
+
return NONE_WITH_NIL_VALUE if result.nil?
|
74
|
+
return NONE_WITH_UNDEFINED_VALUE if Undefined == result
|
75
|
+
|
76
|
+
Some.new(result)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
VALUE_CANT_BE_NONE = "value can't be nil or Kind::Undefined".freeze
|
81
|
+
|
82
|
+
def some(value)
|
83
|
+
return Maybe::Some.new(value) if !KIND.null?(value)
|
84
|
+
|
85
|
+
raise ArgumentError, VALUE_CANT_BE_NONE
|
86
|
+
end
|
87
|
+
|
88
|
+
private_constant :VALUE_CANT_BE_NONE
|
89
|
+
end
|
90
|
+
end
|