kind 5.0.0 → 5.4.1
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 -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
|