kind 3.1.0 → 5.2.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 +67 -12
- data/.travis.yml +7 -5
- data/CHANGELOG.md +1647 -0
- data/Gemfile +22 -7
- data/README.md +920 -486
- data/kind.gemspec +1 -1
- data/lib/kind.rb +2 -314
- data/lib/kind/__lib__/attributes.rb +66 -0
- data/lib/kind/__lib__/kind.rb +71 -0
- data/lib/kind/__lib__/undefined.rb +14 -0
- data/lib/kind/action.rb +92 -0
- data/lib/kind/active_model/validation.rb +3 -4
- data/lib/kind/basic.rb +73 -0
- data/lib/kind/basic/error.rb +29 -0
- data/lib/kind/{undefined.rb → basic/undefined.rb} +8 -1
- data/lib/kind/dig.rb +31 -11
- data/lib/kind/either.rb +30 -0
- data/lib/kind/either/left.rb +29 -0
- data/lib/kind/either/methods.rb +17 -0
- data/lib/kind/either/monad.rb +65 -0
- data/lib/kind/either/monad/wrapper.rb +19 -0
- data/lib/kind/either/right.rb +38 -0
- data/lib/kind/empty.rb +4 -10
- data/lib/kind/empty/constant.rb +7 -0
- data/lib/kind/enum.rb +63 -0
- data/lib/kind/enum/item.rb +40 -0
- data/lib/kind/enum/methods.rb +72 -0
- data/lib/kind/function.rb +47 -0
- data/lib/kind/functional.rb +89 -0
- data/lib/kind/functional/action.rb +89 -0
- data/lib/kind/immutable_attributes.rb +34 -0
- data/lib/kind/immutable_attributes/initializer.rb +70 -0
- data/lib/kind/immutable_attributes/reader.rb +38 -0
- data/lib/kind/maybe.rb +35 -159
- data/lib/kind/maybe/methods.rb +21 -0
- data/lib/kind/maybe/monad.rb +82 -0
- data/lib/kind/maybe/monad/wrapper.rb +19 -0
- data/lib/kind/maybe/none.rb +50 -0
- data/lib/kind/maybe/some.rb +132 -0
- data/lib/kind/maybe/typed.rb +35 -0
- data/lib/kind/maybe/wrapper.rb +37 -0
- data/lib/kind/monad.rb +22 -0
- data/lib/kind/monads.rb +5 -0
- data/lib/kind/objects.rb +17 -0
- data/lib/kind/objects/basic_object.rb +45 -0
- data/lib/kind/objects/modules.rb +32 -0
- data/lib/kind/objects/modules/core/array.rb +19 -0
- data/lib/kind/objects/modules/core/class.rb +13 -0
- data/lib/kind/objects/modules/core/comparable.rb +13 -0
- data/lib/kind/objects/modules/core/enumerable.rb +13 -0
- data/lib/kind/objects/modules/core/enumerator.rb +13 -0
- data/lib/kind/objects/modules/core/file.rb +13 -0
- data/lib/kind/objects/modules/core/float.rb +13 -0
- data/lib/kind/objects/modules/core/hash.rb +19 -0
- data/lib/kind/objects/modules/core/integer.rb +13 -0
- data/lib/kind/objects/modules/core/io.rb +13 -0
- data/lib/kind/objects/modules/core/method.rb +13 -0
- data/lib/kind/objects/modules/core/module.rb +17 -0
- data/lib/kind/objects/modules/core/numeric.rb +13 -0
- data/lib/kind/objects/modules/core/proc.rb +13 -0
- data/lib/kind/objects/modules/core/queue.rb +14 -0
- data/lib/kind/objects/modules/core/range.rb +13 -0
- data/lib/kind/objects/modules/core/regexp.rb +13 -0
- data/lib/kind/objects/modules/core/string.rb +19 -0
- data/lib/kind/objects/modules/core/struct.rb +13 -0
- data/lib/kind/objects/modules/core/symbol.rb +13 -0
- data/lib/kind/objects/modules/core/time.rb +13 -0
- data/lib/kind/objects/modules/custom/boolean.rb +19 -0
- data/lib/kind/objects/modules/custom/callable.rb +19 -0
- data/lib/kind/objects/modules/custom/lambda.rb +19 -0
- data/lib/kind/objects/modules/stdlib/open_struct.rb +15 -0
- data/lib/kind/objects/modules/stdlib/set.rb +19 -0
- data/lib/kind/objects/nil.rb +17 -0
- data/lib/kind/objects/not_nil.rb +13 -0
- data/lib/kind/objects/object.rb +56 -0
- data/lib/kind/objects/respond_to.rb +30 -0
- data/lib/kind/objects/union_type.rb +44 -0
- data/lib/kind/presence.rb +35 -0
- data/lib/kind/result.rb +31 -0
- data/lib/kind/result/abstract.rb +53 -0
- data/lib/kind/result/failure.rb +31 -0
- data/lib/kind/result/methods.rb +17 -0
- data/lib/kind/result/monad.rb +69 -0
- data/lib/kind/result/monad/wrapper.rb +19 -0
- data/lib/kind/result/success.rb +40 -0
- data/lib/kind/try.rb +46 -0
- data/lib/kind/validator.rb +112 -1
- data/lib/kind/version.rb +1 -1
- data/test.sh +4 -4
- metadata +81 -13
- data/lib/kind/active_model/kind_validator.rb +0 -96
- data/lib/kind/checker.rb +0 -15
- data/lib/kind/checker/factory.rb +0 -35
- data/lib/kind/checker/protocol.rb +0 -73
- data/lib/kind/error.rb +0 -19
- data/lib/kind/is.rb +0 -19
- data/lib/kind/of.rb +0 -11
- data/lib/kind/types.rb +0 -115
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Kind
|
|
4
|
+
require 'kind/monad'
|
|
5
|
+
|
|
6
|
+
class Maybe::Monad::Wrapper < Kind::Monad::Wrapper
|
|
7
|
+
def none(matcher = UNDEFINED)
|
|
8
|
+
return if @monad.some? || output?
|
|
9
|
+
|
|
10
|
+
@output = yield(@monad.value) if @monad.maybe?(matcher)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def some(matcher = UNDEFINED)
|
|
14
|
+
return if @monad.none? || output?
|
|
15
|
+
|
|
16
|
+
@output = yield(@monad.value) if @monad.maybe?(matcher)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Kind
|
|
4
|
+
module Maybe
|
|
5
|
+
class None < Monad
|
|
6
|
+
def value_or(default = UNDEFINED, &block)
|
|
7
|
+
Error.invalid_default_arg! if UNDEFINED == default && !block
|
|
8
|
+
|
|
9
|
+
UNDEFINED != default ? default : block.call
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def none?; true; end
|
|
13
|
+
|
|
14
|
+
def map(_method_name = UNDEFINED, &fn)
|
|
15
|
+
self
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
alias_method :map!, :map
|
|
19
|
+
alias_method :then, :map
|
|
20
|
+
alias_method :then!, :map
|
|
21
|
+
alias_method :check, :map
|
|
22
|
+
alias_method :accept, :map
|
|
23
|
+
alias_method :reject, :map
|
|
24
|
+
alias_method :and_then, :map
|
|
25
|
+
alias_method :and_then!, :map!
|
|
26
|
+
|
|
27
|
+
def try!(method_name = UNDEFINED, *args, &block)
|
|
28
|
+
KIND.of!(::Symbol, method_name)if UNDEFINED != method_name
|
|
29
|
+
|
|
30
|
+
self
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
alias_method :try, :try!
|
|
34
|
+
|
|
35
|
+
def dig(*keys)
|
|
36
|
+
self
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def presence
|
|
40
|
+
self
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def inspect
|
|
44
|
+
'#<%s value=%s>' % ['Kind::None', value.inspect]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
NONE_INSTANCE = None.new(nil)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kind/dig'
|
|
4
|
+
require 'kind/presence'
|
|
5
|
+
|
|
6
|
+
module Kind
|
|
7
|
+
module Maybe
|
|
8
|
+
class Some < Monad
|
|
9
|
+
KindSymbol = ->(value) { KIND.of!(::Symbol, value) }
|
|
10
|
+
|
|
11
|
+
VALUE_CANT_BE_NONE = "value can't be nil or Kind::Undefined".freeze
|
|
12
|
+
|
|
13
|
+
def self.[](value)
|
|
14
|
+
return new(value) if !KIND.null?(value)
|
|
15
|
+
|
|
16
|
+
raise ArgumentError, VALUE_CANT_BE_NONE
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def value_or(_default = UNDEFINED, &block)
|
|
20
|
+
@value
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def none?; false; end
|
|
24
|
+
|
|
25
|
+
def map(method_name = UNDEFINED, &fn)
|
|
26
|
+
map!(method_name, &fn)
|
|
27
|
+
rescue StandardError => exception
|
|
28
|
+
None.new(exception)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
alias_method :then, :map
|
|
32
|
+
alias_method :and_then, :map
|
|
33
|
+
|
|
34
|
+
def map!(method_name = UNDEFINED, &fn)
|
|
35
|
+
result = if UNDEFINED != method_name
|
|
36
|
+
return NONE_INSTANCE unless @value.respond_to?(KindSymbol[method_name])
|
|
37
|
+
|
|
38
|
+
@value.public_send(method_name)
|
|
39
|
+
else
|
|
40
|
+
fn.call(@value)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
resolve(result)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
alias_method :then!, :map!
|
|
47
|
+
alias_method :and_then!, :map!
|
|
48
|
+
|
|
49
|
+
def check(method_name = UNDEFINED, &fn)
|
|
50
|
+
result = if UNDEFINED != method_name
|
|
51
|
+
if method_name.kind_of?(::Symbol)
|
|
52
|
+
@value.respond_to?(method_name) ? @value.public_send(method_name) : NONE_INSTANCE
|
|
53
|
+
else
|
|
54
|
+
method_name === @value
|
|
55
|
+
end
|
|
56
|
+
else
|
|
57
|
+
fn.call(@value)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
!result || KIND.null?(result) ? NONE_INSTANCE : self
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
alias_method :accept, :check
|
|
64
|
+
|
|
65
|
+
def reject(method_name = UNDEFINED, &fn)
|
|
66
|
+
result = if UNDEFINED != method_name
|
|
67
|
+
if method_name.kind_of?(::Symbol)
|
|
68
|
+
@value.respond_to?(method_name) ? @value.public_send(method_name) : NONE_INSTANCE
|
|
69
|
+
else
|
|
70
|
+
method_name === @value
|
|
71
|
+
end
|
|
72
|
+
else
|
|
73
|
+
fn.call(@value)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
result || KIND.null?(result) ? NONE_INSTANCE : self
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def try!(method_name = UNDEFINED, *args, &block)
|
|
80
|
+
return __try_block__(block, args) if block
|
|
81
|
+
|
|
82
|
+
return __try_method__(method_name, args) if UNDEFINED != method_name
|
|
83
|
+
|
|
84
|
+
raise ArgumentError, 'method name or a block must be present'
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def try(method_name = UNDEFINED, *args, &block)
|
|
88
|
+
return __try_block__(block, args) if block
|
|
89
|
+
|
|
90
|
+
return __try_method__(method_name, args) if value.respond_to?(method_name)
|
|
91
|
+
|
|
92
|
+
NONE_INSTANCE
|
|
93
|
+
rescue TypeError
|
|
94
|
+
NONE_INSTANCE
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def dig(*keys)
|
|
98
|
+
resolve(Dig.call!(value, keys))
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def presence
|
|
102
|
+
resolve(Presence.(value))
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def inspect
|
|
106
|
+
'#<%s value=%s>' % ['Kind::Some', value.inspect]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
private
|
|
110
|
+
|
|
111
|
+
def __try_method__(method_name, args)
|
|
112
|
+
__try_block__(KindSymbol[method_name].to_proc, args)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def __try_block__(block, args)
|
|
116
|
+
result = args.empty? ? block.call(value) : block.call(*args.unshift(value))
|
|
117
|
+
|
|
118
|
+
resolve(result)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def resolve(result)
|
|
122
|
+
return result if Maybe::None === result
|
|
123
|
+
return NONE_INSTANCE if KIND.null?(result)
|
|
124
|
+
return None.new(result) if ::Exception === result
|
|
125
|
+
|
|
126
|
+
Some.new(result)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private_constant :KindSymbol, :VALUE_CANT_BE_NONE
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Kind
|
|
4
|
+
module Maybe
|
|
5
|
+
class Typed
|
|
6
|
+
include Wrapper
|
|
7
|
+
|
|
8
|
+
singleton_class.send(:alias_method, :[], :new)
|
|
9
|
+
|
|
10
|
+
def initialize(kind)
|
|
11
|
+
@kind = kind
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def new(arg)
|
|
15
|
+
value = Monad::Value[arg]
|
|
16
|
+
|
|
17
|
+
@kind === value ? Maybe::Some[value] : Maybe::NONE_INSTANCE
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
alias_method :[], :new
|
|
21
|
+
|
|
22
|
+
def inspect
|
|
23
|
+
"Kind::Maybe<#{@kind}>"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def __call_before_expose_the_arg_in_a_block(arg)
|
|
29
|
+
value = Monad::Value[arg]
|
|
30
|
+
|
|
31
|
+
@kind === value ? value : Maybe::NONE_INSTANCE
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Kind
|
|
4
|
+
module Maybe
|
|
5
|
+
module Wrapper
|
|
6
|
+
def wrap(arg = UNDEFINED)
|
|
7
|
+
if block_given?
|
|
8
|
+
begin
|
|
9
|
+
return new(yield) if UNDEFINED == arg
|
|
10
|
+
|
|
11
|
+
input = __call_before_expose_the_arg_in_a_block(arg)
|
|
12
|
+
|
|
13
|
+
input.kind_of?(Maybe::None) ? input : new(yield(input))
|
|
14
|
+
rescue StandardError => exception
|
|
15
|
+
Maybe::None.new(exception)
|
|
16
|
+
end
|
|
17
|
+
else
|
|
18
|
+
return new(arg) if UNDEFINED != arg
|
|
19
|
+
|
|
20
|
+
Error.wrong_number_of_args!(given: 0, expected: 1)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def from(&block)
|
|
25
|
+
wrap(&block)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def __call_before_expose_the_arg_in_a_block(input)
|
|
31
|
+
input
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private_constant :Wrapper
|
|
36
|
+
end
|
|
37
|
+
end
|
data/lib/kind/monad.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Kind
|
|
4
|
+
module Monad
|
|
5
|
+
Error = ::Class.new(Kind::Error)
|
|
6
|
+
|
|
7
|
+
class Wrapper
|
|
8
|
+
def initialize(monad)
|
|
9
|
+
@monad = monad
|
|
10
|
+
@output = UNDEFINED
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def output?
|
|
14
|
+
UNDEFINED != @output
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def output
|
|
18
|
+
@output if output?
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
data/lib/kind/monads.rb
ADDED
data/lib/kind/objects.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kind/try'
|
|
4
|
+
require 'kind/maybe'
|
|
5
|
+
|
|
6
|
+
require 'kind/objects/basic_object'
|
|
7
|
+
require 'kind/objects/respond_to'
|
|
8
|
+
require 'kind/objects/union_type'
|
|
9
|
+
require 'kind/objects/nil'
|
|
10
|
+
require 'kind/objects/not_nil'
|
|
11
|
+
require 'kind/objects/object'
|
|
12
|
+
require 'kind/objects/modules'
|
|
13
|
+
|
|
14
|
+
module Kind
|
|
15
|
+
UnionType.send(:include, Maybe::Buildable)
|
|
16
|
+
RespondTo.send(:include, Maybe::Buildable)
|
|
17
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Kind
|
|
4
|
+
module BasicObject
|
|
5
|
+
def [](value, label: nil)
|
|
6
|
+
return value if self === value
|
|
7
|
+
|
|
8
|
+
KIND.error!(name, value, label)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def or_nil(value)
|
|
12
|
+
return value if self === value
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def or_undefined(value)
|
|
16
|
+
or_nil(value) || Undefined
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def or(fallback, value = UNDEFINED)
|
|
20
|
+
return __or_func.(fallback) if UNDEFINED === value
|
|
21
|
+
|
|
22
|
+
self === value ? value : fallback
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def value?(value = UNDEFINED)
|
|
26
|
+
return self === value if UNDEFINED != value
|
|
27
|
+
|
|
28
|
+
@__is_value ||= ->(tc) { ->(arg) { tc === arg } }.(self)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def value(arg, default:)
|
|
32
|
+
KIND.value(self, arg, self[default])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def or_null(value) # :nodoc:
|
|
36
|
+
KIND.null?(value) ? value : self[value]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def __or_func
|
|
42
|
+
@__or_func ||= ->(tc, fb, value) { tc === value ? value : tc.or_null(fb) }.curry[self]
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kind/empty'
|
|
4
|
+
|
|
5
|
+
require 'kind/objects/modules/core/array'
|
|
6
|
+
require 'kind/objects/modules/core/class'
|
|
7
|
+
require 'kind/objects/modules/core/comparable'
|
|
8
|
+
require 'kind/objects/modules/core/enumerable'
|
|
9
|
+
require 'kind/objects/modules/core/enumerator'
|
|
10
|
+
require 'kind/objects/modules/core/file'
|
|
11
|
+
require 'kind/objects/modules/core/float'
|
|
12
|
+
require 'kind/objects/modules/core/hash'
|
|
13
|
+
require 'kind/objects/modules/core/integer'
|
|
14
|
+
require 'kind/objects/modules/core/io'
|
|
15
|
+
require 'kind/objects/modules/core/module'
|
|
16
|
+
require 'kind/objects/modules/core/method'
|
|
17
|
+
require 'kind/objects/modules/core/numeric'
|
|
18
|
+
require 'kind/objects/modules/core/proc'
|
|
19
|
+
require 'kind/objects/modules/core/queue'
|
|
20
|
+
require 'kind/objects/modules/core/range'
|
|
21
|
+
require 'kind/objects/modules/core/regexp'
|
|
22
|
+
require 'kind/objects/modules/core/string'
|
|
23
|
+
require 'kind/objects/modules/core/struct'
|
|
24
|
+
require 'kind/objects/modules/core/symbol'
|
|
25
|
+
require 'kind/objects/modules/core/time'
|
|
26
|
+
|
|
27
|
+
require 'kind/objects/modules/stdlib/open_struct'
|
|
28
|
+
require 'kind/objects/modules/stdlib/set'
|
|
29
|
+
|
|
30
|
+
require 'kind/objects/modules/custom/boolean'
|
|
31
|
+
require 'kind/objects/modules/custom/callable'
|
|
32
|
+
require 'kind/objects/modules/custom/lambda'
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Kind
|
|
4
|
+
module Array
|
|
5
|
+
extend self, ::Kind::Object
|
|
6
|
+
|
|
7
|
+
def kind; ::Array; end
|
|
8
|
+
|
|
9
|
+
def value_or_empty(arg)
|
|
10
|
+
KIND.value(self, arg, Empty::ARRAY)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
alias empty_or value_or_empty
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.Array?(*values)
|
|
17
|
+
KIND.of?(::Array, values)
|
|
18
|
+
end
|
|
19
|
+
end
|