kind 5.0.0 → 5.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.tool-versions +1 -1
- data/.travis.sh +39 -7
- data/.travis.yml +1 -0
- data/CHANGELOG.md +506 -28
- data/Gemfile +13 -6
- data/README.md +50 -42
- data/kind.gemspec +3 -3
- data/lib/kind.rb +2 -60
- data/lib/kind/__lib__/action_steps.rb +57 -0
- data/lib/kind/__lib__/attributes.rb +66 -0
- data/lib/kind/__lib__/kind.rb +51 -0
- data/lib/kind/__lib__/of.rb +17 -0
- data/lib/kind/__lib__/strict.rb +49 -0
- data/lib/kind/__lib__/undefined.rb +14 -0
- data/lib/kind/action.rb +127 -0
- data/lib/kind/active_model/validation.rb +3 -4
- data/lib/kind/basic.rb +79 -0
- data/lib/kind/basic/error.rb +29 -0
- data/lib/kind/{undefined.rb → basic/undefined.rb} +6 -1
- data/lib/kind/dig.rb +21 -5
- 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 +2 -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 +45 -0
- data/lib/kind/functional.rb +89 -0
- data/lib/kind/functional/action.rb +87 -0
- data/lib/kind/functional/steps.rb +22 -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 +37 -12
- 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 +12 -19
- data/lib/kind/maybe/some.rb +68 -26
- data/lib/kind/maybe/typed.rb +11 -5
- data/lib/kind/maybe/{wrappable.rb → wrapper.rb} +8 -4
- 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 +43 -0
- data/lib/kind/objects/modules.rb +32 -0
- data/lib/kind/{type_checkers → objects/modules}/core/array.rb +3 -1
- data/lib/kind/{type_checkers → objects/modules}/core/class.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/core/comparable.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/core/enumerable.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/core/enumerator.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/core/file.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/core/float.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/core/hash.rb +3 -1
- data/lib/kind/{type_checkers → objects/modules}/core/integer.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/core/io.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/core/method.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/core/module.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/core/numeric.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/core/proc.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/core/queue.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/core/range.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/core/regexp.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/core/string.rb +3 -1
- data/lib/kind/{type_checkers → objects/modules}/core/struct.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/core/symbol.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/core/time.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/custom/boolean.rb +2 -2
- data/lib/kind/{type_checkers → objects/modules}/custom/callable.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/custom/lambda.rb +1 -1
- data/lib/kind/{type_checkers → objects/modules}/stdlib/open_struct.rb +3 -1
- data/lib/kind/{type_checkers → objects/modules}/stdlib/set.rb +3 -1
- data/lib/kind/objects/nil.rb +17 -0
- data/lib/kind/objects/not_nil.rb +9 -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 +4 -2
- data/lib/kind/result.rb +31 -0
- data/lib/kind/result/abstract.rb +53 -0
- data/lib/kind/result/failure.rb +33 -0
- data/lib/kind/result/methods.rb +17 -0
- data/lib/kind/result/monad.rb +74 -0
- data/lib/kind/result/monad/wrapper.rb +19 -0
- data/lib/kind/result/success.rb +53 -0
- data/lib/kind/strict/disabled.rb +34 -0
- data/lib/kind/try.rb +22 -10
- data/lib/kind/validator.rb +111 -0
- data/lib/kind/version.rb +1 -1
- metadata +83 -42
- data/lib/kind/active_model/kind_validator.rb +0 -103
- data/lib/kind/core.rb +0 -8
- data/lib/kind/core/kind.rb +0 -61
- data/lib/kind/core/undefined.rb +0 -7
- data/lib/kind/error.rb +0 -15
- data/lib/kind/maybe/result.rb +0 -51
- data/lib/kind/type_checker.rb +0 -87
- data/lib/kind/type_checkers.rb +0 -30
data/lib/kind/either.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kind/basic'
|
4
|
+
|
5
|
+
module Kind
|
6
|
+
module Either
|
7
|
+
require 'kind/either/monad'
|
8
|
+
require 'kind/either/left'
|
9
|
+
require 'kind/either/right'
|
10
|
+
require 'kind/either/methods'
|
11
|
+
|
12
|
+
extend self
|
13
|
+
|
14
|
+
def new(value)
|
15
|
+
Right[value]
|
16
|
+
end
|
17
|
+
|
18
|
+
alias_method :[], :new
|
19
|
+
|
20
|
+
def self.from
|
21
|
+
result = yield
|
22
|
+
|
23
|
+
Either::Monad === result ? result : Either::Right[result]
|
24
|
+
rescue StandardError => e
|
25
|
+
Either::Left[e]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
extend Either::Methods
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kind
|
4
|
+
class Either::Left < Either::Monad
|
5
|
+
def left?
|
6
|
+
true
|
7
|
+
end
|
8
|
+
|
9
|
+
def value_or(default = UNDEFINED, &block)
|
10
|
+
Error.invalid_default_arg! if UNDEFINED == default && !block
|
11
|
+
|
12
|
+
UNDEFINED != default ? default : block.call(value)
|
13
|
+
end
|
14
|
+
|
15
|
+
def map(&_)
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
alias_method :map!, :map
|
20
|
+
alias_method :then, :map
|
21
|
+
alias_method :then!, :map
|
22
|
+
alias_method :and_then, :map
|
23
|
+
alias_method :and_then!, :map
|
24
|
+
|
25
|
+
def inspect
|
26
|
+
'#<%s value=%p>' % ['Kind::Left', value]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kind
|
4
|
+
module Either::Methods
|
5
|
+
def Left(value)
|
6
|
+
Either::Left[value]
|
7
|
+
end
|
8
|
+
|
9
|
+
def Right(value)
|
10
|
+
Either::Right[value]
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.included(base)
|
14
|
+
base.send(:private, :Left, :Right)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kind
|
4
|
+
class Either::Monad
|
5
|
+
require 'kind/either/monad/wrapper'
|
6
|
+
|
7
|
+
attr_reader :value
|
8
|
+
|
9
|
+
singleton_class.send(:alias_method, :[], :new)
|
10
|
+
|
11
|
+
def initialize(value)
|
12
|
+
@value = value
|
13
|
+
end
|
14
|
+
|
15
|
+
def left?
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
def right?
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
def value_or(_method_name = UNDEFINED, &block)
|
24
|
+
raise NotImplementedError
|
25
|
+
end
|
26
|
+
|
27
|
+
def map(&_)
|
28
|
+
raise NotImplementedError
|
29
|
+
end
|
30
|
+
|
31
|
+
alias_method :map!, :map
|
32
|
+
alias_method :then, :map
|
33
|
+
alias_method :then!, :map
|
34
|
+
alias_method :and_then, :map
|
35
|
+
alias_method :and_then!, :map
|
36
|
+
|
37
|
+
def on
|
38
|
+
monad = Wrapper.new(self)
|
39
|
+
|
40
|
+
yield(monad)
|
41
|
+
|
42
|
+
monad.output
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_right(matcher = UNDEFINED)
|
46
|
+
yield(value) if right? && either?(matcher)
|
47
|
+
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def on_left(matcher = UNDEFINED)
|
52
|
+
yield(value) if left? && either?(matcher)
|
53
|
+
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
def either?(matcher)
|
58
|
+
UNDEFINED == matcher || matcher === value
|
59
|
+
end
|
60
|
+
|
61
|
+
def ===(monad)
|
62
|
+
self.class === monad && self.value === monad.value
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kind
|
4
|
+
require 'kind/monad'
|
5
|
+
|
6
|
+
class Either::Monad::Wrapper < Kind::Monad::Wrapper
|
7
|
+
def left(matcher = UNDEFINED)
|
8
|
+
return if @monad.right? || output?
|
9
|
+
|
10
|
+
@output = yield(@monad.value) if @monad.either?(matcher)
|
11
|
+
end
|
12
|
+
|
13
|
+
def right(matcher = UNDEFINED)
|
14
|
+
return if @monad.left? || output?
|
15
|
+
|
16
|
+
@output = yield(@monad.value) if @monad.either?(matcher)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kind
|
4
|
+
class Either::Right < Either::Monad
|
5
|
+
def right?
|
6
|
+
true
|
7
|
+
end
|
8
|
+
|
9
|
+
def value_or(_default = UNDEFINED, &block)
|
10
|
+
@value
|
11
|
+
end
|
12
|
+
|
13
|
+
def map(&fn)
|
14
|
+
map!(&fn)
|
15
|
+
rescue Kind::Monad::Error => e
|
16
|
+
raise e
|
17
|
+
rescue StandardError => e
|
18
|
+
Either::Left[e]
|
19
|
+
end
|
20
|
+
|
21
|
+
def map!(&fn)
|
22
|
+
monad = fn.call(@value)
|
23
|
+
|
24
|
+
return monad if Either::Monad === monad
|
25
|
+
|
26
|
+
raise Kind::Monad::Error.new('Kind::Right | Kind::Left', monad)
|
27
|
+
end
|
28
|
+
|
29
|
+
alias_method :then, :map
|
30
|
+
alias_method :then!, :map!
|
31
|
+
alias_method :and_then, :map
|
32
|
+
alias_method :and_then!, :map!
|
33
|
+
|
34
|
+
def inspect
|
35
|
+
'#<%s value=%p>' % ['Kind::Right', value]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/kind/empty.rb
CHANGED
data/lib/kind/enum.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
require 'kind/basic'
|
5
|
+
|
6
|
+
module Kind
|
7
|
+
module Enum
|
8
|
+
require 'kind/enum/item'
|
9
|
+
require 'kind/enum/methods'
|
10
|
+
|
11
|
+
extend self
|
12
|
+
|
13
|
+
def values(input)
|
14
|
+
enum_module = ::Module.new
|
15
|
+
|
16
|
+
enum_items =
|
17
|
+
case input
|
18
|
+
when ::Hash then create_from_hash(input)
|
19
|
+
when ::Array then create_from_array(input)
|
20
|
+
else raise ArgumentError, 'use an array or hash to define a Kind::Enum'
|
21
|
+
end
|
22
|
+
|
23
|
+
enum_items.each { |item| enum_module.const_set(item.name, item) }
|
24
|
+
|
25
|
+
enum_map = enum_items.each_with_object({}) do |item, memo|
|
26
|
+
memo[item.to_s] = item
|
27
|
+
memo[item.value] = item
|
28
|
+
memo[item.to_sym] = item
|
29
|
+
end
|
30
|
+
|
31
|
+
enum_module.const_set(:ENUM__MAP, enum_map)
|
32
|
+
enum_module.const_set(:ENUM__HASH, enum_items.map(&:to_ary).to_h.freeze)
|
33
|
+
enum_module.const_set(:ENUM__KEYS, ::Set.new(enum_items.map(&:key)).freeze)
|
34
|
+
enum_module.const_set(:ENUM__VALS, ::Set.new(enum_items.map(&:value)).freeze)
|
35
|
+
enum_module.const_set(:ENUM__REFS, ::Set.new(enum_map.keys))
|
36
|
+
enum_module.const_set(:ENUM__ITEMS, enum_items.freeze)
|
37
|
+
|
38
|
+
enum_module.send(:private_constant, :ENUM__MAP, :ENUM__HASH, :ENUM__KEYS,
|
39
|
+
:ENUM__VALS, :ENUM__REFS, :ENUM__ITEMS)
|
40
|
+
|
41
|
+
enum_module.module_eval(METHODS)
|
42
|
+
|
43
|
+
enum_module.extend(enum_module)
|
44
|
+
enum_module
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def create_from_hash(input)
|
50
|
+
input.map { |key, value| build_item(key, value) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def create_from_array(input)
|
54
|
+
input.map.with_index { |key, index| build_item(key, index) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def build_item(key, value)
|
58
|
+
return Item.new(key, value) if key.respond_to?(:to_sym)
|
59
|
+
|
60
|
+
raise ArgumentError, 'use a string or symbol to define a Kind::Enum item'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kind
|
4
|
+
module Enum
|
5
|
+
class Item
|
6
|
+
Underscore = ->(arg) do
|
7
|
+
str = String(arg).strip
|
8
|
+
str.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
|
9
|
+
str.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
10
|
+
str.tr!("-", "_")
|
11
|
+
str.downcase!
|
12
|
+
str
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :value, :to_s, :name, :to_sym, :inspect
|
16
|
+
|
17
|
+
alias_method :key, :to_s
|
18
|
+
alias_method :to_str, :to_s
|
19
|
+
|
20
|
+
def initialize(key, val)
|
21
|
+
@value = val.frozen? ? val : val.dup.freeze
|
22
|
+
|
23
|
+
@to_s = Kind.respond_to(key, :to_sym).to_s
|
24
|
+
@name = Underscore[key].upcase.freeze
|
25
|
+
@to_sym = key.to_sym
|
26
|
+
@inspect = ('#<Kind::Enum::Item name=%p to_s=%p value=%p>' % [@name, @to_s, @value]).freeze
|
27
|
+
end
|
28
|
+
|
29
|
+
def ==(arg)
|
30
|
+
arg == value || arg == to_s || arg == to_sym
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_ary
|
34
|
+
[key, value]
|
35
|
+
end
|
36
|
+
|
37
|
+
alias_method :===, :==
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kind
|
4
|
+
module Enum
|
5
|
+
METHODS = \
|
6
|
+
<<-RUBY
|
7
|
+
def to_h
|
8
|
+
ENUM__HASH
|
9
|
+
end
|
10
|
+
|
11
|
+
def items
|
12
|
+
ENUM__ITEMS
|
13
|
+
end
|
14
|
+
|
15
|
+
def ===(arg)
|
16
|
+
ENUM__ITEMS.any? { |item| item === arg }
|
17
|
+
end
|
18
|
+
|
19
|
+
def refs
|
20
|
+
ENUM__REFS.to_a
|
21
|
+
end
|
22
|
+
|
23
|
+
def keys
|
24
|
+
ENUM__KEYS.to_a
|
25
|
+
end
|
26
|
+
|
27
|
+
def values
|
28
|
+
ENUM__VALS.to_a
|
29
|
+
end
|
30
|
+
|
31
|
+
def ref?(arg)
|
32
|
+
ENUM__REFS.include?(arg)
|
33
|
+
end
|
34
|
+
|
35
|
+
def key?(arg)
|
36
|
+
arg.respond_to?(:to_sym) ? ref?(arg) && !value?(arg) : false
|
37
|
+
end
|
38
|
+
|
39
|
+
def value?(arg)
|
40
|
+
ENUM__VALS.include?(arg)
|
41
|
+
end
|
42
|
+
|
43
|
+
def [](arg)
|
44
|
+
return arg if ref?(arg)
|
45
|
+
|
46
|
+
raise KeyError, "key or value not found: %p" % [arg]
|
47
|
+
end
|
48
|
+
|
49
|
+
def ref(arg)
|
50
|
+
arg if ref?(arg)
|
51
|
+
end
|
52
|
+
|
53
|
+
def item(arg)
|
54
|
+
ENUM__MAP[arg]
|
55
|
+
end
|
56
|
+
|
57
|
+
def key(arg)
|
58
|
+
item(arg).key if value?(arg)
|
59
|
+
end
|
60
|
+
|
61
|
+
def value_at(arg)
|
62
|
+
item(arg).value if key?(arg)
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.included(base)
|
66
|
+
base.extend(base)
|
67
|
+
end
|
68
|
+
RUBY
|
69
|
+
|
70
|
+
private_constant :METHODS
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kind/basic'
|
4
|
+
|
5
|
+
module Kind
|
6
|
+
module Function
|
7
|
+
module Behavior
|
8
|
+
def self.extended(base)
|
9
|
+
base.send(:alias_method, :[], :call)
|
10
|
+
base.send(:alias_method, :===, :call)
|
11
|
+
base.send(:alias_method, :yield, :call)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.included(_)
|
16
|
+
raise RuntimeError, "The Kind::Function can't be included, it can be only extended."
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.extended(base)
|
20
|
+
base.extend(Kind.of_module(base))
|
21
|
+
end
|
22
|
+
|
23
|
+
def kind_function!
|
24
|
+
return self if Kind.is?(Behavior, self)
|
25
|
+
|
26
|
+
KIND.respond_to!(:call, self).extend(Behavior)
|
27
|
+
|
28
|
+
if method(:call).parameters.empty?
|
29
|
+
raise ArgumentError, "#{self.name}.call must receive at least one argument"
|
30
|
+
end
|
31
|
+
|
32
|
+
self.instance_eval(
|
33
|
+
'def to_proc; @to_proc ||= method(:call).to_proc; end' \
|
34
|
+
"\n" \
|
35
|
+
'def curry; @curry ||= to_proc.curry; end'
|
36
|
+
)
|
37
|
+
|
38
|
+
self.to_proc
|
39
|
+
self.curry
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
private_constant :Behavior
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kind/basic'
|
4
|
+
require 'kind/empty'
|
5
|
+
require 'kind/__lib__/attributes'
|
6
|
+
|
7
|
+
module Kind
|
8
|
+
module Functional
|
9
|
+
def self.extended(_)
|
10
|
+
raise RuntimeError, "The Kind::Functional can't be extended, it can be only included."
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.included(base)
|
14
|
+
Kind.of_class(base).send(:extend, ClassMethods)
|
15
|
+
end
|
16
|
+
|
17
|
+
module Behavior
|
18
|
+
def self.included(base)
|
19
|
+
base.send(:alias_method, :[], :call)
|
20
|
+
base.send(:alias_method, :===, :call)
|
21
|
+
base.send(:alias_method, :yield, :call)
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(arg = Empty::HASH)
|
25
|
+
hash = STRICT.kind_of(::Hash, arg)
|
26
|
+
|
27
|
+
self.class.__dependencies__.each do |name, (kind, default, _visibility)|
|
28
|
+
value_to_assign = ATTRIBUTES.value_to_assign!(kind, default, hash, name)
|
29
|
+
|
30
|
+
instance_variable_set("@#{name}", value_to_assign)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module DependencyInjection
|
36
|
+
def __dependencies__ # :nodoc:
|
37
|
+
@__dependencies__ ||= {}
|
38
|
+
end
|
39
|
+
|
40
|
+
def dependency(name, kind, default: UNDEFINED)
|
41
|
+
__dependencies__[ATTRIBUTES.name!(name)] = ATTRIBUTES.value!(kind, default)
|
42
|
+
|
43
|
+
attr_reader(name)
|
44
|
+
|
45
|
+
private(name)
|
46
|
+
|
47
|
+
name
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module ClassMethods
|
52
|
+
include DependencyInjection
|
53
|
+
|
54
|
+
def kind_functional!
|
55
|
+
return self if Kind.is?(Behavior, self)
|
56
|
+
|
57
|
+
public_methods = self.public_instance_methods - ::Object.new.methods
|
58
|
+
|
59
|
+
unless public_methods.include?(:call)
|
60
|
+
raise Kind::Error.new("expected #{self} to implement `#call`")
|
61
|
+
end
|
62
|
+
|
63
|
+
if public_methods.size > 1
|
64
|
+
raise Kind::Error.new("#{self} can only have `#call` as its public method")
|
65
|
+
end
|
66
|
+
|
67
|
+
if public_instance_method(:call).parameters.empty?
|
68
|
+
raise ArgumentError, "#{self.name}#call must receive at least one argument"
|
69
|
+
end
|
70
|
+
|
71
|
+
self.send(:include, Behavior)
|
72
|
+
|
73
|
+
def self.inherited(_)
|
74
|
+
raise RuntimeError, "#{self.name} is a Kind::Functional and it can't be inherited by anyone"
|
75
|
+
end
|
76
|
+
|
77
|
+
self.class_eval(
|
78
|
+
'def to_proc; @to_proc ||= method(:call).to_proc; end' \
|
79
|
+
"\n" \
|
80
|
+
'def curry; @curry ||= to_proc.curry; end'
|
81
|
+
)
|
82
|
+
|
83
|
+
__dependencies__.freeze
|
84
|
+
|
85
|
+
self
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|