solid-result 2.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 +7 -0
- data/.rubocop.yml +98 -0
- data/.rubocop_todo.yml +12 -0
- data/CHANGELOG.md +600 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/LICENSE.txt +21 -0
- data/README.md +2691 -0
- data/Rakefile +28 -0
- data/Steepfile +31 -0
- data/examples/multiple_listeners/Rakefile +55 -0
- data/examples/multiple_listeners/app/models/account/member.rb +10 -0
- data/examples/multiple_listeners/app/models/account/owner_creation.rb +62 -0
- data/examples/multiple_listeners/app/models/account.rb +11 -0
- data/examples/multiple_listeners/app/models/user/creation.rb +67 -0
- data/examples/multiple_listeners/app/models/user/token/creation.rb +51 -0
- data/examples/multiple_listeners/app/models/user/token.rb +7 -0
- data/examples/multiple_listeners/app/models/user.rb +15 -0
- data/examples/multiple_listeners/config/boot.rb +16 -0
- data/examples/multiple_listeners/config/initializers/solid_result.rb +9 -0
- data/examples/multiple_listeners/config.rb +27 -0
- data/examples/multiple_listeners/db/setup.rb +60 -0
- data/examples/multiple_listeners/lib/event_logs_listener/stdout.rb +60 -0
- data/examples/multiple_listeners/lib/runtime_breaker.rb +11 -0
- data/examples/multiple_listeners/lib/solid/result/event_logs_record.rb +27 -0
- data/examples/multiple_listeners/lib/solid/result/rollback_on_failure.rb +15 -0
- data/examples/service_objects/Rakefile +36 -0
- data/examples/service_objects/app/models/account/member.rb +10 -0
- data/examples/service_objects/app/models/account.rb +11 -0
- data/examples/service_objects/app/models/user/token.rb +7 -0
- data/examples/service_objects/app/models/user.rb +15 -0
- data/examples/service_objects/app/services/account/owner_creation.rb +47 -0
- data/examples/service_objects/app/services/application_service.rb +79 -0
- data/examples/service_objects/app/services/user/creation.rb +56 -0
- data/examples/service_objects/app/services/user/token/creation.rb +37 -0
- data/examples/service_objects/config/boot.rb +17 -0
- data/examples/service_objects/config/initializers/solid_result.rb +9 -0
- data/examples/service_objects/config.rb +20 -0
- data/examples/service_objects/db/setup.rb +49 -0
- data/examples/single_listener/Rakefile +92 -0
- data/examples/single_listener/app/models/account/member.rb +10 -0
- data/examples/single_listener/app/models/account/owner_creation.rb +62 -0
- data/examples/single_listener/app/models/account.rb +11 -0
- data/examples/single_listener/app/models/user/creation.rb +67 -0
- data/examples/single_listener/app/models/user/token/creation.rb +51 -0
- data/examples/single_listener/app/models/user/token.rb +7 -0
- data/examples/single_listener/app/models/user.rb +15 -0
- data/examples/single_listener/config/boot.rb +16 -0
- data/examples/single_listener/config/initializers/solid_result.rb +9 -0
- data/examples/single_listener/config.rb +23 -0
- data/examples/single_listener/db/setup.rb +49 -0
- data/examples/single_listener/lib/runtime_breaker.rb +11 -0
- data/examples/single_listener/lib/single_event_logs_listener.rb +117 -0
- data/examples/single_listener/lib/solid/result/rollback_on_failure.rb +15 -0
- data/lib/solid/failure.rb +23 -0
- data/lib/solid/output/callable_and_then.rb +40 -0
- data/lib/solid/output/expectations/mixin.rb +31 -0
- data/lib/solid/output/expectations.rb +25 -0
- data/lib/solid/output/failure.rb +9 -0
- data/lib/solid/output/mixin.rb +57 -0
- data/lib/solid/output/success.rb +37 -0
- data/lib/solid/output.rb +115 -0
- data/lib/solid/result/_self.rb +198 -0
- data/lib/solid/result/callable_and_then/caller.rb +49 -0
- data/lib/solid/result/callable_and_then/config.rb +15 -0
- data/lib/solid/result/callable_and_then/error.rb +11 -0
- data/lib/solid/result/callable_and_then.rb +9 -0
- data/lib/solid/result/config/options.rb +27 -0
- data/lib/solid/result/config/switcher.rb +82 -0
- data/lib/solid/result/config/switchers/addons.rb +25 -0
- data/lib/solid/result/config/switchers/constant_aliases.rb +33 -0
- data/lib/solid/result/config/switchers/features.rb +32 -0
- data/lib/solid/result/config/switchers/pattern_matching.rb +20 -0
- data/lib/solid/result/config.rb +64 -0
- data/lib/solid/result/contract/disabled.rb +25 -0
- data/lib/solid/result/contract/error.rb +17 -0
- data/lib/solid/result/contract/evaluator.rb +45 -0
- data/lib/solid/result/contract/for_types.rb +29 -0
- data/lib/solid/result/contract/for_types_and_values.rb +46 -0
- data/lib/solid/result/contract/interface.rb +21 -0
- data/lib/solid/result/contract/type_checker.rb +37 -0
- data/lib/solid/result/contract.rb +33 -0
- data/lib/solid/result/data.rb +33 -0
- data/lib/solid/result/error.rb +59 -0
- data/lib/solid/result/event_logs/config.rb +28 -0
- data/lib/solid/result/event_logs/listener.rb +51 -0
- data/lib/solid/result/event_logs/listeners.rb +87 -0
- data/lib/solid/result/event_logs/tracking/disabled.rb +15 -0
- data/lib/solid/result/event_logs/tracking/enabled.rb +161 -0
- data/lib/solid/result/event_logs/tracking.rb +26 -0
- data/lib/solid/result/event_logs/tree.rb +141 -0
- data/lib/solid/result/event_logs.rb +27 -0
- data/lib/solid/result/expectations/mixin.rb +58 -0
- data/lib/solid/result/expectations.rb +75 -0
- data/lib/solid/result/failure.rb +11 -0
- data/lib/solid/result/handler/allowed_types.rb +45 -0
- data/lib/solid/result/handler.rb +57 -0
- data/lib/solid/result/ignored_types.rb +14 -0
- data/lib/solid/result/mixin.rb +72 -0
- data/lib/solid/result/success.rb +11 -0
- data/lib/solid/result/version.rb +7 -0
- data/lib/solid/result.rb +27 -0
- data/lib/solid/success.rb +23 -0
- data/lib/solid-result.rb +3 -0
- data/sig/solid/failure.rbs +13 -0
- data/sig/solid/output.rbs +175 -0
- data/sig/solid/result/callable_and_then.rbs +60 -0
- data/sig/solid/result/config.rbs +102 -0
- data/sig/solid/result/contract.rbs +120 -0
- data/sig/solid/result/data.rbs +16 -0
- data/sig/solid/result/error.rbs +34 -0
- data/sig/solid/result/event_logs.rbs +189 -0
- data/sig/solid/result/expectations.rbs +71 -0
- data/sig/solid/result/handler.rbs +47 -0
- data/sig/solid/result/ignored_types.rbs +9 -0
- data/sig/solid/result/mixin.rbs +45 -0
- data/sig/solid/result/version.rbs +5 -0
- data/sig/solid/result.rbs +85 -0
- data/sig/solid/success.rbs +13 -0
- metadata +167 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Solid::Result
|
|
4
|
+
module EventLogs
|
|
5
|
+
class Tree
|
|
6
|
+
class Node
|
|
7
|
+
attr_reader :id, :value, :parent, :normalizer, :children
|
|
8
|
+
|
|
9
|
+
def initialize(value, parent:, id:, normalizer:)
|
|
10
|
+
@normalizer = normalizer
|
|
11
|
+
|
|
12
|
+
@id = id
|
|
13
|
+
@value = normalizer.call(id, value)
|
|
14
|
+
@parent = parent
|
|
15
|
+
|
|
16
|
+
@children = []
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def insert(value, id:)
|
|
20
|
+
node = self.class.new(value, parent: self, id: id, normalizer: normalizer)
|
|
21
|
+
|
|
22
|
+
@children << node
|
|
23
|
+
|
|
24
|
+
node
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def root?
|
|
28
|
+
parent.nil?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def leaf?
|
|
32
|
+
children.empty?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def node?
|
|
36
|
+
!leaf?
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def inspect
|
|
40
|
+
"#<#{self.class.name} id=#{id} children.size=#{children.size}>"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
attr_reader :size, :root, :current
|
|
45
|
+
|
|
46
|
+
def initialize(value, normalizer: ->(_id, val) { val })
|
|
47
|
+
@size = 0
|
|
48
|
+
|
|
49
|
+
@root = Node.new(value, parent: nil, id: size, normalizer: normalizer)
|
|
50
|
+
|
|
51
|
+
@current = root
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def root_value
|
|
55
|
+
root.value
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def parent_value
|
|
59
|
+
current.parent&.value || root_value
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def current_value
|
|
63
|
+
current.value
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def insert(value)
|
|
67
|
+
@size += 1
|
|
68
|
+
|
|
69
|
+
current.insert(value, id: size)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def insert!(value)
|
|
73
|
+
move_to! insert(value)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def move_to!(node)
|
|
77
|
+
tap { @current = node }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def move_up!(level = 1)
|
|
81
|
+
tap { level.times { move_to!(current.parent || root) } }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def move_down!(level = 1, index: -1)
|
|
85
|
+
tap { level.times { current.children[index].then { |child| move_to!(child) if child } } }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def move_to_root!
|
|
89
|
+
move_to!(root)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
Ids = ->(node) { [node.id, node.children.map(&Ids)] }
|
|
93
|
+
|
|
94
|
+
def ids
|
|
95
|
+
Ids[root]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def ids_list
|
|
99
|
+
ids.flatten
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
IdsMatrix = ->(tree, row, col, memo, previous) do
|
|
103
|
+
last_row = previous[0]
|
|
104
|
+
|
|
105
|
+
tree.each_with_index do |node, index|
|
|
106
|
+
row = [(index + 1), last_row].max
|
|
107
|
+
|
|
108
|
+
id, leaf = node
|
|
109
|
+
|
|
110
|
+
memo[id] = previous == [row, col] ? [row, col + 1] : [row, col]
|
|
111
|
+
|
|
112
|
+
previous = memo[id]
|
|
113
|
+
|
|
114
|
+
IdsMatrix[leaf, row, col + 1, memo, previous]
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def ids_matrix
|
|
119
|
+
current = [0, 0]
|
|
120
|
+
|
|
121
|
+
memo = { 0 => current }
|
|
122
|
+
|
|
123
|
+
IdsMatrix[ids[1], 1, 1, memo, current]
|
|
124
|
+
|
|
125
|
+
memo
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
IdsLevelParent = ->((id, node), parent = 0, level = 0, memo = {}) do
|
|
129
|
+
memo[id] = [level, parent]
|
|
130
|
+
|
|
131
|
+
node.each { |leaf| IdsLevelParent[leaf, id, level + 1, memo] }
|
|
132
|
+
|
|
133
|
+
memo
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def ids_level_parent
|
|
137
|
+
IdsLevelParent[ids]
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Solid::Result
|
|
4
|
+
module EventLogs
|
|
5
|
+
require_relative 'event_logs/listener'
|
|
6
|
+
require_relative 'event_logs/listeners'
|
|
7
|
+
require_relative 'event_logs/config'
|
|
8
|
+
require_relative 'event_logs/tree'
|
|
9
|
+
require_relative 'event_logs/tracking'
|
|
10
|
+
|
|
11
|
+
THREAD_VAR_NAME = :solid_result_event_logs_tracking
|
|
12
|
+
|
|
13
|
+
EnsureResult = ->(result) do
|
|
14
|
+
return result if result.is_a?(::Solid::Result)
|
|
15
|
+
|
|
16
|
+
raise Error::UnexpectedOutcome.build(outcome: result, origin: :event_logs)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.tracking
|
|
20
|
+
Thread.current[THREAD_VAR_NAME] ||= Tracking.instance
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.event_logs(name: nil, desc: nil, &block)
|
|
25
|
+
EventLogs.tracking.exec(name, desc, &block)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Solid::Result
|
|
4
|
+
module Expectations::Mixin
|
|
5
|
+
module Factory
|
|
6
|
+
def self.module!
|
|
7
|
+
::Module.new do
|
|
8
|
+
def self.included(base); base.const_set(:ResultExpectationsMixin, self); end
|
|
9
|
+
def self.extended(base); base.const_set(:ResultExpectationsMixin, self); end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module Methods
|
|
15
|
+
BASE = <<~RUBY
|
|
16
|
+
def Success(...)
|
|
17
|
+
_Result.Success(...)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def Failure(...)
|
|
21
|
+
_Result.Failure(...)
|
|
22
|
+
end
|
|
23
|
+
RUBY
|
|
24
|
+
|
|
25
|
+
FACTORY = <<~RUBY
|
|
26
|
+
private def _Result
|
|
27
|
+
@_Result ||= Result.with(source: self, terminal: %<terminal>s)
|
|
28
|
+
end
|
|
29
|
+
RUBY
|
|
30
|
+
|
|
31
|
+
def self.to_eval(addons)
|
|
32
|
+
terminal = addons.key?(:continue) ? 'true' : 'nil'
|
|
33
|
+
|
|
34
|
+
"#{BASE}\n#{format(FACTORY, terminal: terminal)}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module Addons
|
|
39
|
+
module Continue
|
|
40
|
+
private def Continue(value)
|
|
41
|
+
_Result.Success(IgnoredTypes::CONTINUE, value)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
module Given
|
|
46
|
+
private def Given(value)
|
|
47
|
+
_Result.Success(IgnoredTypes::GIVEN, value)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
OPTIONS = { continue: Continue, given: Given }.freeze
|
|
52
|
+
|
|
53
|
+
def self.options(config_flags)
|
|
54
|
+
Config::Options.addon(map: config_flags, from: OPTIONS)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Solid::Result
|
|
4
|
+
class Expectations
|
|
5
|
+
require_relative 'expectations/mixin'
|
|
6
|
+
|
|
7
|
+
def self.mixin(**options)
|
|
8
|
+
return mixin!(**options) if Config.instance.feature.enabled?(:expectations)
|
|
9
|
+
|
|
10
|
+
result_factory_without_expectations.mixin(**options.slice(:config))
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.mixin!(success: nil, failure: nil, config: nil)
|
|
14
|
+
addons = mixin_module::Addons.options(config)
|
|
15
|
+
|
|
16
|
+
mod = mixin_module::Factory.module!
|
|
17
|
+
mod.const_set(:Result, new(success: success, failure: failure, config: config).freeze)
|
|
18
|
+
mod.module_eval(mixin_module::Methods.to_eval(addons), __FILE__, __LINE__ + 1)
|
|
19
|
+
mod.send(:include, *addons.values) unless addons.empty?
|
|
20
|
+
mod
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.mixin_module
|
|
24
|
+
Mixin
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.result_factory_without_expectations
|
|
28
|
+
::Solid::Result
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.new(...)
|
|
32
|
+
return result_factory_without_expectations unless Config.instance.feature.enabled?(:expectations)
|
|
33
|
+
|
|
34
|
+
instance = allocate
|
|
35
|
+
instance.send(:initialize, ...)
|
|
36
|
+
instance
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private_class_method :mixin!, :mixin_module, :result_factory_without_expectations
|
|
40
|
+
|
|
41
|
+
def initialize(source: nil, contract: nil, terminal: nil, **options)
|
|
42
|
+
@terminal = terminal
|
|
43
|
+
|
|
44
|
+
@source = source
|
|
45
|
+
|
|
46
|
+
@contract = contract if contract.is_a?(Contract::Evaluator)
|
|
47
|
+
|
|
48
|
+
@contract ||= Contract.new(
|
|
49
|
+
success: options[:success],
|
|
50
|
+
failure: options[:failure],
|
|
51
|
+
config: options[:config]
|
|
52
|
+
).freeze
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def Success(type, value = nil)
|
|
56
|
+
_ResultAs(Success, type, value)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def Failure(type, value = nil)
|
|
60
|
+
_ResultAs(Failure, type, value)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def with(source:, terminal: nil)
|
|
64
|
+
self.class.new(source: source, terminal: terminal, contract: contract)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def _ResultAs(kind_class, type, value)
|
|
70
|
+
kind_class.new(type: type, value: value, source: source, expectations: contract, terminal: terminal)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
attr_reader :source, :terminal, :contract
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Solid::Result
|
|
4
|
+
class Handler
|
|
5
|
+
class AllowedTypes
|
|
6
|
+
attr_reader :unchecked, :type_checker
|
|
7
|
+
|
|
8
|
+
def initialize(type_checker)
|
|
9
|
+
@type_checker = type_checker
|
|
10
|
+
|
|
11
|
+
@expectations = type_checker.expectations
|
|
12
|
+
|
|
13
|
+
@unchecked = @expectations.allowed_types.dup
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def allow?(types)
|
|
17
|
+
check!(types, type_checker.allow?(types))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def allow_success?(types)
|
|
21
|
+
unchecked.subtract(@expectations.success.allowed_types) if types.empty?
|
|
22
|
+
|
|
23
|
+
check!(types, type_checker.allow_success?(types))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def allow_failure?(types)
|
|
27
|
+
unchecked.subtract(@expectations.failure.allowed_types) if types.empty?
|
|
28
|
+
|
|
29
|
+
check!(types, type_checker.allow_failure?(types))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def all_checked?
|
|
33
|
+
unchecked.empty?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def check!(types, checked)
|
|
39
|
+
unchecked.subtract(types) unless all_checked?
|
|
40
|
+
|
|
41
|
+
checked
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Solid::Result
|
|
4
|
+
class Handler
|
|
5
|
+
require_relative 'handler/allowed_types'
|
|
6
|
+
|
|
7
|
+
UNDEFINED = ::Object.new
|
|
8
|
+
|
|
9
|
+
def initialize(result, type_checker:)
|
|
10
|
+
@allowed_types = AllowedTypes.new(type_checker)
|
|
11
|
+
|
|
12
|
+
@outcome = UNDEFINED
|
|
13
|
+
|
|
14
|
+
@result = result
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def [](*types, &block)
|
|
18
|
+
raise Error::MissingTypeArgument if types.empty?
|
|
19
|
+
|
|
20
|
+
self.outcome = block if allowed_types.allow?(types)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def success(*types, &block)
|
|
24
|
+
self.outcome = block if allowed_types.allow_success?(types) && result.success?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def failure(*types, &block)
|
|
28
|
+
self.outcome = block if allowed_types.allow_failure?(types) && result.failure?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def unknown(&block)
|
|
32
|
+
self.outcome = block unless outcome?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
alias type []
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
attr_reader :result, :allowed_types
|
|
40
|
+
|
|
41
|
+
def outcome?
|
|
42
|
+
@outcome != UNDEFINED
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def outcome=(block)
|
|
46
|
+
@outcome = block.call(result.value, result.type) unless outcome?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def outcome
|
|
50
|
+
allowed_types.all_checked? or raise Error::UnhandledTypes.build(types: allowed_types.unchecked)
|
|
51
|
+
|
|
52
|
+
@outcome if outcome?
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private_constant :Handler
|
|
57
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Solid::Result
|
|
4
|
+
module Mixin
|
|
5
|
+
module Factory
|
|
6
|
+
def self.module!
|
|
7
|
+
::Module.new do
|
|
8
|
+
def self.included(base); base.const_set(:ResultMixin, self); end
|
|
9
|
+
def self.extended(base); base.const_set(:ResultMixin, self); end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module Methods
|
|
15
|
+
def Success(type, value = nil)
|
|
16
|
+
_ResultAs(Success, type, value)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def Failure(type, value = nil)
|
|
20
|
+
_ResultAs(Failure, type, value)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private def _ResultAs(kind_class, type, value, terminal: nil)
|
|
24
|
+
kind_class.new(type: type, value: value, source: self, terminal: terminal)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
module Addons
|
|
29
|
+
module Continue
|
|
30
|
+
def Success(type, value = nil)
|
|
31
|
+
_ResultAs(Success, type, value, terminal: true)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private def Continue(value)
|
|
35
|
+
_ResultAs(Success, IgnoredTypes::CONTINUE, value)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
module Given
|
|
40
|
+
private def Given(value)
|
|
41
|
+
_ResultAs(Success, IgnoredTypes::GIVEN, value)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
OPTIONS = { continue: Continue, given: Given }.freeze
|
|
46
|
+
|
|
47
|
+
def self.options(config_flags)
|
|
48
|
+
Config::Options.addon(map: config_flags, from: OPTIONS)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.mixin(config: nil)
|
|
54
|
+
addons = mixin_module::Addons.options(config)
|
|
55
|
+
|
|
56
|
+
mod = mixin_module::Factory.module!
|
|
57
|
+
mod.send(:include, mixin_module::Methods)
|
|
58
|
+
mod.const_set(:Result, result_factory)
|
|
59
|
+
mod.send(:include, *addons.values) unless addons.empty?
|
|
60
|
+
mod
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self.mixin_module
|
|
64
|
+
Mixin
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def self.result_factory
|
|
68
|
+
::Solid::Result
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private_class_method :mixin_module, :result_factory
|
|
72
|
+
end
|
data/lib/solid/result.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
5
|
+
module Solid
|
|
6
|
+
require_relative 'success'
|
|
7
|
+
require_relative 'failure'
|
|
8
|
+
|
|
9
|
+
class Result
|
|
10
|
+
require_relative 'result/version'
|
|
11
|
+
require_relative 'result/error'
|
|
12
|
+
require_relative 'result/ignored_types'
|
|
13
|
+
require_relative 'result/event_logs'
|
|
14
|
+
require_relative 'result/callable_and_then'
|
|
15
|
+
require_relative 'result/data'
|
|
16
|
+
require_relative 'result/handler'
|
|
17
|
+
require_relative 'result/failure'
|
|
18
|
+
require_relative 'result/success'
|
|
19
|
+
require_relative 'result/mixin'
|
|
20
|
+
require_relative 'result/contract'
|
|
21
|
+
require_relative 'result/expectations'
|
|
22
|
+
require_relative 'result/config'
|
|
23
|
+
require_relative 'result/_self'
|
|
24
|
+
|
|
25
|
+
require_relative 'output'
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solid
|
|
4
|
+
module Success
|
|
5
|
+
def success?(type = nil)
|
|
6
|
+
type.nil? || type_checker.allow_success?([type])
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def failure?(_type = nil)
|
|
10
|
+
false
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def value_or
|
|
14
|
+
value
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def kind
|
|
20
|
+
:success
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/solid-result.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Solid
|
|
2
|
+
module Failure
|
|
3
|
+
def success?: (?Symbol type) -> bool
|
|
4
|
+
def failure?: (?Symbol type) -> bool
|
|
5
|
+
def value_or: { (untyped) -> untyped } -> untyped
|
|
6
|
+
def value: -> untyped
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def kind: -> Symbol
|
|
11
|
+
def type_checker: -> Solid::Result::Contract::TypeChecker
|
|
12
|
+
end
|
|
13
|
+
end
|