bcdd-result 0.11.0 → 0.13.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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +16 -1
  3. data/CHANGELOG.md +97 -15
  4. data/README.md +508 -95
  5. data/Steepfile +4 -4
  6. data/examples/multiple_listeners/Rakefile +55 -0
  7. data/examples/multiple_listeners/app/models/account/member.rb +10 -0
  8. data/examples/multiple_listeners/app/models/account/owner_creation.rb +62 -0
  9. data/examples/multiple_listeners/app/models/account.rb +11 -0
  10. data/examples/multiple_listeners/app/models/user/creation.rb +67 -0
  11. data/examples/multiple_listeners/app/models/user/token/creation.rb +51 -0
  12. data/examples/multiple_listeners/app/models/user/token.rb +7 -0
  13. data/examples/multiple_listeners/app/models/user.rb +15 -0
  14. data/examples/multiple_listeners/config/boot.rb +16 -0
  15. data/examples/multiple_listeners/config/initializers/bcdd.rb +11 -0
  16. data/examples/multiple_listeners/config.rb +27 -0
  17. data/examples/multiple_listeners/db/setup.rb +61 -0
  18. data/examples/multiple_listeners/lib/bcdd/result/rollback_on_failure.rb +15 -0
  19. data/examples/multiple_listeners/lib/bcdd/result/transitions_record.rb +28 -0
  20. data/examples/multiple_listeners/lib/runtime_breaker.rb +11 -0
  21. data/examples/multiple_listeners/lib/transitions_listener/stdout.rb +54 -0
  22. data/examples/single_listener/Rakefile +92 -0
  23. data/examples/single_listener/app/models/account/member.rb +10 -0
  24. data/examples/single_listener/app/models/account/owner_creation.rb +62 -0
  25. data/examples/single_listener/app/models/account.rb +11 -0
  26. data/examples/single_listener/app/models/user/creation.rb +67 -0
  27. data/examples/single_listener/app/models/user/token/creation.rb +51 -0
  28. data/examples/single_listener/app/models/user/token.rb +7 -0
  29. data/examples/single_listener/app/models/user.rb +15 -0
  30. data/examples/single_listener/config/boot.rb +16 -0
  31. data/examples/single_listener/config/initializers/bcdd.rb +11 -0
  32. data/examples/single_listener/config.rb +23 -0
  33. data/examples/single_listener/db/setup.rb +49 -0
  34. data/examples/single_listener/lib/bcdd/result/rollback_on_failure.rb +15 -0
  35. data/examples/single_listener/lib/runtime_breaker.rb +11 -0
  36. data/examples/single_listener/lib/single_transitions_listener.rb +108 -0
  37. data/lib/bcdd/result/callable_and_then/caller.rb +49 -0
  38. data/lib/bcdd/result/callable_and_then/config.rb +15 -0
  39. data/lib/bcdd/result/callable_and_then/error.rb +11 -0
  40. data/lib/bcdd/result/callable_and_then.rb +9 -0
  41. data/lib/bcdd/result/config/switchers/features.rb +5 -1
  42. data/lib/bcdd/result/config.rb +15 -4
  43. data/lib/bcdd/result/context/callable_and_then.rb +39 -0
  44. data/lib/bcdd/result/context/expectations/mixin.rb +2 -2
  45. data/lib/bcdd/result/context/mixin.rb +3 -3
  46. data/lib/bcdd/result/context/success.rb +29 -7
  47. data/lib/bcdd/result/context.rb +34 -16
  48. data/lib/bcdd/result/contract/for_types.rb +1 -1
  49. data/lib/bcdd/result/contract/for_types_and_values.rb +2 -0
  50. data/lib/bcdd/result/error.rb +20 -11
  51. data/lib/bcdd/result/expectations/mixin.rb +3 -3
  52. data/lib/bcdd/result/expectations.rb +6 -6
  53. data/lib/bcdd/result/ignored_types.rb +14 -0
  54. data/lib/bcdd/result/mixin.rb +3 -3
  55. data/lib/bcdd/result/transitions/config.rb +26 -0
  56. data/lib/bcdd/result/transitions/listener.rb +51 -0
  57. data/lib/bcdd/result/transitions/listeners.rb +87 -0
  58. data/lib/bcdd/result/transitions/tracking/disabled.rb +4 -6
  59. data/lib/bcdd/result/transitions/tracking/enabled.rb +103 -24
  60. data/lib/bcdd/result/transitions/tracking.rb +8 -3
  61. data/lib/bcdd/result/transitions/tree.rb +36 -6
  62. data/lib/bcdd/result/transitions.rb +11 -14
  63. data/lib/bcdd/result/version.rb +1 -1
  64. data/lib/bcdd/result.rb +39 -22
  65. data/sig/bcdd/result/callable_and_then.rbs +60 -0
  66. data/sig/bcdd/result/config.rbs +3 -0
  67. data/sig/bcdd/result/context.rbs +65 -4
  68. data/sig/bcdd/result/error.rbs +9 -6
  69. data/sig/bcdd/result/expectations.rbs +4 -4
  70. data/sig/bcdd/result/ignored_types.rbs +9 -0
  71. data/sig/bcdd/result/transitions.rbs +107 -7
  72. data/sig/bcdd/result.rbs +10 -6
  73. metadata +48 -6
@@ -2,30 +2,36 @@
2
2
 
3
3
  module BCDD::Result::Transitions
4
4
  class Tracking::Enabled
5
- attr_accessor :tree, :records, :root_started_at
5
+ attr_accessor :tree, :records, :root_started_at, :listener
6
6
 
7
- private :tree, :tree=, :records, :records=, :root_started_at, :root_started_at=
7
+ private :tree, :tree=, :records, :records=, :root_started_at, :root_started_at=, :listener, :listener=
8
8
 
9
- def start(name:, desc:)
10
- name_and_desc = [name, desc]
9
+ def exec(name, desc)
10
+ transition_node, scope = start(name, desc)
11
11
 
12
- tree.frozen? ? root_start(name_and_desc) : tree.insert!(name_and_desc)
13
- end
12
+ result = nil
14
13
 
15
- def finish(result:)
16
- node = tree.current
14
+ listener.around_transitions(scope: scope) do
15
+ result = EnsureResult[yield]
16
+ end
17
17
 
18
- tree.move_up!
18
+ tree.move_to_root! if transition_node.root?
19
19
 
20
- return unless node.root?
20
+ finish(result)
21
21
 
22
- duration = (now_in_milliseconds - root_started_at)
22
+ result
23
+ rescue ::Exception => e
24
+ err!(e, transition_node)
25
+ end
23
26
 
24
- metadata = { duration: duration, tree_map: tree.nested_ids }
27
+ def err!(exception, transition_node)
28
+ if transition_node.root?
29
+ listener.before_interruption(exception: exception, transitions: map_transitions)
25
30
 
26
- result.send(:transitions=, version: Tracking::VERSION, records: records, metadata: metadata)
31
+ reset!
32
+ end
27
33
 
28
- reset!
34
+ raise exception
29
35
  end
30
36
 
31
37
  def reset!
@@ -38,43 +44,116 @@ module BCDD::Result::Transitions
38
44
  track(result, time: ::Time.now.getutc)
39
45
  end
40
46
 
41
- def record_and_then(type_arg, arg, subject)
47
+ def record_and_then(type_arg, arg)
48
+ return yield if tree.frozen?
49
+
42
50
  type = type_arg.instance_of?(::Method) ? :method : type_arg
43
51
 
44
- unless tree.frozen?
45
- current_and_then = { type: type, arg: arg, subject: subject }
46
- current_and_then[:method_name] = type_arg.name if type == :method
52
+ current_and_then = { type: type, arg: arg }
53
+ current_and_then[:method_name] = type_arg.name if type == :method
47
54
 
48
- tree.current.value[1] = current_and_then
49
- end
55
+ tree.current.value[1] = current_and_then
56
+
57
+ scope, and_then = tree.current_value
50
58
 
51
- yield
59
+ result = nil
60
+
61
+ listener.around_and_then(scope: scope, and_then: and_then) { result = yield }
62
+
63
+ result
64
+ end
65
+
66
+ def reset_and_then!
67
+ return if tree.frozen?
68
+
69
+ tree.current.value[1] = Tracking::EMPTY_HASH
52
70
  end
53
71
 
54
72
  private
55
73
 
74
+ def start(name, desc)
75
+ name_and_desc = [name, desc]
76
+
77
+ tree.frozen? ? root_start(name_and_desc) : tree.insert!(name_and_desc)
78
+
79
+ scope = tree.current.value[0]
80
+
81
+ listener.on_start(scope: scope)
82
+
83
+ [tree.current, scope]
84
+ end
85
+
86
+ def finish(result)
87
+ node = tree.current
88
+
89
+ tree.move_up!
90
+
91
+ return unless node.root?
92
+
93
+ transitions = map_transitions
94
+
95
+ result.send(:transitions=, transitions)
96
+
97
+ listener.on_finish(transitions: transitions)
98
+
99
+ reset!
100
+ end
101
+
56
102
  TreeNodeValueNormalizer = ->(id, (nam, des)) { [{ id: id, name: nam, desc: des }, Tracking::EMPTY_HASH] }
57
103
 
58
104
  def root_start(name_and_desc)
59
105
  self.root_started_at = now_in_milliseconds
60
106
 
107
+ self.listener = build_listener
108
+
61
109
  self.records = []
62
110
 
63
111
  self.tree = Tree.new(name_and_desc, normalizer: TreeNodeValueNormalizer)
64
112
  end
65
113
 
66
114
  def track(result, time:)
67
- result = result.data.to_h
115
+ record = track_record(result, time)
116
+
117
+ records << record
118
+
119
+ listener.on_record(record: record)
120
+
121
+ record
122
+ end
123
+
124
+ def track_record(result, time)
125
+ result_data = result.data.to_h
126
+ result_data[:source] = result.send(:source)
68
127
 
69
128
  root, = tree.root_value
70
129
  parent, = tree.parent_value
71
130
  current, and_then = tree.current_value
72
131
 
73
- records << { root: root, parent: parent, current: current, result: result, and_then: and_then, time: time }
132
+ { root: root, parent: parent, current: current, result: result_data, and_then: and_then, time: time }
74
133
  end
75
134
 
76
135
  def now_in_milliseconds
77
- Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
136
+ ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond)
137
+ end
138
+
139
+ def map_transitions
140
+ duration = (now_in_milliseconds - root_started_at)
141
+
142
+ trace_id = Config.instance.trace_id.call
143
+
144
+ metadata = { duration: duration, ids_tree: tree.nested_ids, ids_matrix: tree.ids_matrix, trace_id: trace_id }
145
+
146
+ { version: Tracking::VERSION, records: records, metadata: metadata }
147
+ end
148
+
149
+ def build_listener
150
+ Config.instance.listener.new
151
+ rescue ::StandardError => e
152
+ err = "#{e.message} (#{e.class}); Backtrace: #{e.backtrace&.join(', ')}"
153
+
154
+ warn("Fallback to #{Listener::Null} because registered listener raised an exception: #{err}")
155
+
156
+ Listener::Null.new
78
157
  end
79
158
  end
80
159
  end
@@ -6,14 +6,19 @@ class BCDD::Result
6
6
  require_relative 'tracking/enabled'
7
7
  require_relative 'tracking/disabled'
8
8
 
9
+ VERSION = 1
10
+
9
11
  EMPTY_ARRAY = [].freeze
10
12
  EMPTY_HASH = {}.freeze
11
13
  EMPTY_TREE = Tree.new(nil).freeze
12
- VERSION = 1
13
- EMPTY = { version: VERSION, records: EMPTY_ARRAY, metadata: { duration: 0, tree_map: EMPTY_ARRAY } }.freeze
14
+ EMPTY = {
15
+ version: VERSION,
16
+ records: EMPTY_ARRAY,
17
+ metadata: { duration: 0, ids_tree: EMPTY_ARRAY, ids_matrix: EMPTY_HASH, trace_id: nil }.freeze
18
+ }.freeze
14
19
 
15
20
  def self.instance
16
- Config.instance.feature.enabled?(:transitions) ? Tracking::Enabled.new : Tracking::Disabled
21
+ ::BCDD::Result::Config.instance.feature.enabled?(:transitions) ? Tracking::Enabled.new : Tracking::Disabled
17
22
  end
18
23
  end
19
24
  end
@@ -46,9 +46,9 @@ class BCDD::Result
46
46
  def initialize(value, normalizer: ->(_id, val) { val })
47
47
  @size = 0
48
48
 
49
- @root = Node.new(value, parent: nil, id: @size, normalizer: normalizer)
49
+ @root = Node.new(value, parent: nil, id: size, normalizer: normalizer)
50
50
 
51
- @current = @root
51
+ @current = root
52
52
  end
53
53
 
54
54
  def root_value
@@ -70,19 +70,23 @@ class BCDD::Result
70
70
  end
71
71
 
72
72
  def insert!(value)
73
- @current = insert(value)
73
+ move_to! insert(value)
74
+ end
75
+
76
+ def move_to!(node)
77
+ tap { @current = node }
74
78
  end
75
79
 
76
80
  def move_up!(level = 1)
77
- tap { level.times { @current = current.parent || root } }
81
+ tap { level.times { move_to!(current.parent || root) } }
78
82
  end
79
83
 
80
84
  def move_down!(level = 1, index: -1)
81
- tap { level.times { current.children[index].then { |child| @current = child if child } } }
85
+ tap { level.times { current.children[index].then { |child| move_to!(child) if child } } }
82
86
  end
83
87
 
84
88
  def move_to_root!
85
- tap { @current = root }
89
+ move_to!(root)
86
90
  end
87
91
 
88
92
  NestedIds = ->(node) { [node.id, node.children.map(&NestedIds)] }
@@ -90,6 +94,32 @@ class BCDD::Result
90
94
  def nested_ids
91
95
  NestedIds[root]
92
96
  end
97
+
98
+ IdsMatrix = ->(tree, row, col, ids, previous) do
99
+ last_row = previous[0]
100
+
101
+ tree.each_with_index do |node, index|
102
+ row = [(index + 1), last_row].max
103
+
104
+ id, leaf = node
105
+
106
+ ids[id] = previous == [row, col] ? [row, col + 1] : [row, col]
107
+
108
+ previous = ids[id]
109
+
110
+ IdsMatrix[leaf, row, col + 1, ids, previous]
111
+ end
112
+ end
113
+
114
+ def ids_matrix
115
+ current = [0, 0]
116
+
117
+ ids = { 0 => current }
118
+
119
+ IdsMatrix[nested_ids[1], 1, 1, ids, current]
120
+
121
+ ids
122
+ end
93
123
  end
94
124
  end
95
125
  end
@@ -2,29 +2,26 @@
2
2
 
3
3
  class BCDD::Result
4
4
  module Transitions
5
+ require_relative 'transitions/listener'
6
+ require_relative 'transitions/listeners'
7
+ require_relative 'transitions/config'
5
8
  require_relative 'transitions/tree'
6
9
  require_relative 'transitions/tracking'
7
10
 
8
11
  THREAD_VAR_NAME = :bcdd_result_transitions_tracking
9
12
 
13
+ EnsureResult = ->(result) do
14
+ return result if result.is_a?(::BCDD::Result)
15
+
16
+ raise Error::UnexpectedOutcome.build(outcome: result, origin: :transitions)
17
+ end
18
+
10
19
  def self.tracking
11
20
  Thread.current[THREAD_VAR_NAME] ||= Tracking.instance
12
21
  end
13
22
  end
14
23
 
15
- def self.transitions(name: nil, desc: nil)
16
- Transitions.tracking.start(name: name, desc: desc)
17
-
18
- result = yield
19
-
20
- result.is_a?(::BCDD::Result) or raise Error::UnexpectedOutcome.build(outcome: result, origin: :transitions)
21
-
22
- Transitions.tracking.finish(result: result)
23
-
24
- result
25
- rescue ::Exception => e
26
- Transitions.tracking.reset!
27
-
28
- raise e
24
+ def self.transitions(name: nil, desc: nil, &block)
25
+ Transitions.tracking.exec(name, desc, &block)
29
26
  end
30
27
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module BCDD
4
4
  class Result
5
- VERSION = '0.11.0'
5
+ VERSION = '0.13.0'
6
6
  end
7
7
  end
data/lib/bcdd/result.rb CHANGED
@@ -1,8 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'set'
4
+ require 'singleton'
5
+
3
6
  require_relative 'result/version'
4
- require_relative 'result/transitions'
5
7
  require_relative 'result/error'
8
+ require_relative 'result/ignored_types'
9
+ require_relative 'result/transitions'
10
+ require_relative 'result/callable_and_then'
6
11
  require_relative 'result/data'
7
12
  require_relative 'result/handler'
8
13
  require_relative 'result/failure'
@@ -16,9 +21,9 @@ require_relative 'result/config'
16
21
  class BCDD::Result
17
22
  attr_accessor :unknown, :transitions
18
23
 
19
- attr_reader :subject, :data, :type_checker, :terminal
24
+ attr_reader :source, :data, :type_checker, :terminal
20
25
 
21
- protected :subject
26
+ protected :source
22
27
 
23
28
  private :unknown, :unknown=, :type_checker, :transitions=
24
29
 
@@ -26,18 +31,18 @@ class BCDD::Result
26
31
  Config.instance
27
32
  end
28
33
 
29
- def self.configuration
34
+ def self.configuration(freeze: true)
30
35
  yield(config)
31
36
 
32
- config.freeze
37
+ freeze and config.freeze
33
38
  end
34
39
 
35
- def initialize(type:, value:, subject: nil, expectations: nil, terminal: nil)
40
+ def initialize(type:, value:, source: nil, expectations: nil, terminal: nil)
36
41
  data = Data.new(kind, type, value)
37
42
 
38
43
  @type_checker = Contract.evaluate(data, expectations)
39
- @subject = subject
40
- @terminal = terminal || kind == :failure
44
+ @source = source
45
+ @terminal = kind == :failure || (terminal && !IgnoredTypes.include?(type))
41
46
  @data = data
42
47
 
43
48
  self.unknown = true
@@ -88,12 +93,20 @@ class BCDD::Result
88
93
  tap { yield(value, type) if unknown }
89
94
  end
90
95
 
91
- def and_then(method_name = nil, context = nil, &block)
96
+ def and_then(method_name = nil, injected_value = nil, &block)
92
97
  return self if terminal?
93
98
 
94
99
  method_name && block and raise ::ArgumentError, 'method_name and block are mutually exclusive'
95
100
 
96
- method_name ? call_and_then_subject_method(method_name, context) : call_and_then_block(block)
101
+ method_name ? call_and_then_source_method(method_name, injected_value) : call_and_then_block(block)
102
+ end
103
+
104
+ def and_then!(source, injected_value = nil, _call: nil)
105
+ raise Error::CallableAndThenDisabled unless Config.instance.feature.enabled?(:and_then!)
106
+
107
+ return self if terminal?
108
+
109
+ call_and_then_callable!(source, value: value, injected_value: injected_value, method_name: _call)
97
110
  end
98
111
 
99
112
  def handle
@@ -139,27 +152,27 @@ class BCDD::Result
139
152
  block.call(value, type)
140
153
  end
141
154
 
142
- def call_and_then_subject_method(method_name, context_data)
143
- method = subject.method(method_name)
155
+ def call_and_then_source_method(method_name, injected_value)
156
+ method = source.method(method_name)
144
157
 
145
- Transitions.tracking.record_and_then(method, context_data, subject) do
146
- result = call_and_then_subject_method!(method, context_data)
158
+ Transitions.tracking.record_and_then(method, injected_value) do
159
+ result = call_and_then_source_method!(method, injected_value)
147
160
 
148
161
  ensure_result_object(result, origin: :method)
149
162
  end
150
163
  end
151
164
 
152
- def call_and_then_subject_method!(method, context_data)
165
+ def call_and_then_source_method!(method, injected_value)
153
166
  case method.arity
154
- when 0 then subject.send(method.name)
155
- when 1 then subject.send(method.name, value)
156
- when 2 then subject.send(method.name, value, context_data)
157
- else raise Error::InvalidSubjectMethodArity.build(subject: subject, method: method, max_arity: 2)
167
+ when 0 then source.send(method.name)
168
+ when 1 then source.send(method.name, value)
169
+ when 2 then source.send(method.name, value, injected_value)
170
+ else raise Error::InvalidSourceMethodArity.build(source: source, method: method, max_arity: 2)
158
171
  end
159
172
  end
160
173
 
161
174
  def call_and_then_block(block)
162
- Transitions.tracking.record_and_then(:block, nil, subject) do
175
+ Transitions.tracking.record_and_then(:block, nil) do
163
176
  result = call_and_then_block!(block)
164
177
 
165
178
  ensure_result_object(result, origin: :block)
@@ -170,11 +183,15 @@ class BCDD::Result
170
183
  block.call(value)
171
184
  end
172
185
 
186
+ def call_and_then_callable!(source, value:, injected_value:, method_name:)
187
+ CallableAndThen::Caller.call(source, value: value, injected_value: injected_value, method_name: method_name)
188
+ end
189
+
173
190
  def ensure_result_object(result, origin:)
174
191
  raise Error::UnexpectedOutcome.build(outcome: result, origin: origin) unless result.is_a?(::BCDD::Result)
175
192
 
176
- return result if result.subject.equal?(subject)
193
+ return result if result.source.equal?(source)
177
194
 
178
- raise Error::InvalidResultSubject.build(given_result: result, expected_subject: subject)
195
+ raise Error::InvalidResultSource.build(given_result: result, expected_source: source)
179
196
  end
180
197
  end
@@ -0,0 +1,60 @@
1
+ module BCDD::Result::CallableAndThen
2
+ class Config
3
+ attr_accessor default_method_name_to_call: Symbol
4
+
5
+ def initialize: -> void
6
+
7
+ def options: () -> Hash[Symbol, untyped]
8
+ end
9
+
10
+ class Error < BCDD::Result::Error
11
+ end
12
+
13
+ class Error::InvalidArity < Error
14
+ def self.build: (
15
+ source: untyped,
16
+ method: Symbol,
17
+ arity: String
18
+ ) -> Error::InvalidArity
19
+ end
20
+
21
+ class Caller
22
+ def self.call: (
23
+ untyped source,
24
+ value: untyped,
25
+ injected_value: untyped,
26
+ method_name: (Symbol | nil)
27
+ ) -> BCDD::Result
28
+
29
+ private
30
+
31
+ def self.call_proc!: (
32
+ untyped source,
33
+ untyped value,
34
+ untyped injected_value
35
+ ) -> BCDD::Result
36
+
37
+ def self.call_method!: (
38
+ untyped source,
39
+ Method method,
40
+ untyped value,
41
+ untyped injected_value
42
+ ) -> BCDD::Result
43
+
44
+ def self.callable_method: (
45
+ untyped source,
46
+ (Symbol | nil) method_name
47
+ ) -> ::Method
48
+
49
+ def self.ensure_result_object: (
50
+ untyped source,
51
+ untyped value,
52
+ BCDD::Result result
53
+ ) -> BCDD::Result
54
+
55
+ def self.expected_result_object: () -> singleton(BCDD::Result)
56
+
57
+ def self.expected_outcome: () -> String
58
+ end
59
+ end
60
+
@@ -14,6 +14,9 @@ class BCDD::Result::Config
14
14
 
15
15
  def initialize: -> void
16
16
 
17
+ def and_then!: () -> BCDD::Result::CallableAndThen::Config
18
+ def transitions: () -> BCDD::Result::Transitions::Config
19
+
17
20
  def freeze: -> BCDD::Result::Config
18
21
  def options: -> Hash[Symbol, BCDD::Result::Config::Switcher]
19
22
  def to_h: -> Hash[Symbol, Hash[Symbol | String, bool]]
@@ -1,38 +1,99 @@
1
1
  class BCDD::Result::Context < BCDD::Result
2
2
  EXPECTED_OUTCOME: String
3
3
 
4
- SubjectMethodArity: ^(Method) -> Integer
4
+ SourceMethodArity: ^(Method) -> Integer
5
5
 
6
6
  attr_reader acc: Hash[Symbol, untyped]
7
7
 
8
8
  def initialize: (
9
9
  type: Symbol,
10
10
  value: untyped,
11
- ?subject: untyped,
11
+ ?source: untyped,
12
12
  ?expectations: BCDD::Result::Contract::Evaluator,
13
13
  ?terminal: bool
14
14
  ) -> void
15
15
 
16
- def and_then: (?Symbol, **untyped) ?{ (Hash[Symbol, untyped]) -> untyped } -> BCDD::Result::Context
16
+ def and_then: (?Symbol, **untyped) ?{ (Hash[Symbol, untyped]) -> untyped } -> untyped
17
+
18
+ def and_then!: (untyped, **untyped) -> untyped
17
19
 
18
20
  private
19
21
 
20
- def call_and_then_subject_method: (Symbol, Hash[Symbol, untyped]) -> BCDD::Result::Context
22
+ def call_and_then_source_method: (Symbol, Hash[Symbol, untyped]) -> BCDD::Result::Context
23
+
24
+ def call_and_then_callable!: (untyped, value: untyped, injected_value: untyped, method_name: (Symbol | nil)) -> BCDD::Result::Context
25
+
21
26
  def ensure_result_object: (untyped, origin: Symbol) -> BCDD::Result::Context
22
27
 
23
28
  def raise_unexpected_outcome_error: (BCDD::Result::Context | untyped, Symbol) -> void
24
29
  end
25
30
 
31
+ class BCDD::Result::Context
32
+ class Error < BCDD::Result::Error
33
+ class InvalidExposure < BCDD::Result::Context::Error
34
+ end
35
+ end
36
+ end
37
+
26
38
  class BCDD::Result::Context
27
39
  class Success < BCDD::Result::Context
28
40
  include BCDD::Result::Success::Methods
29
41
 
42
+ FetchValues: Proc
43
+
30
44
  def and_expose: (Symbol, Array[Symbol], terminal: bool) -> BCDD::Result::Context::Success
31
45
  end
32
46
 
33
47
  def self.Success: (Symbol, **untyped) -> BCDD::Result::Context::Success
34
48
  end
35
49
 
50
+ module BCDD::Result::Context::CallableAndThen
51
+ class Caller < BCDD::Result::CallableAndThen::Caller
52
+ module KeyArgs
53
+ def self.parameters?: (untyped) -> bool
54
+
55
+ def self.invalid_arity: (untyped, Symbol) -> BCDD::Result::CallableAndThen::Error::InvalidArity
56
+ end
57
+
58
+ def self.call: (
59
+ untyped source,
60
+ value: untyped,
61
+ injected_value: untyped,
62
+ method_name: (Symbol | nil),
63
+ ) -> BCDD::Result::Context
64
+
65
+ private
66
+
67
+ def self.call_proc!: (
68
+ untyped source,
69
+ Hash[Symbol, untyped] value,
70
+ nil injected_value
71
+ ) -> BCDD::Result::Context
72
+
73
+ def self.call_method!: (
74
+ untyped source,
75
+ Method method,
76
+ Hash[Symbol, untyped] value,
77
+ nil injected_value
78
+ ) -> BCDD::Result::Context
79
+
80
+ def self.callable_method: (
81
+ untyped source,
82
+ (Symbol | nil) method_name
83
+ ) -> ::Method
84
+
85
+ def self.ensure_result_object: (
86
+ untyped source,
87
+ untyped value,
88
+ BCDD::Result::Context result
89
+ ) -> BCDD::Result::Context
90
+
91
+ def self.expected_result_object: () -> singleton(BCDD::Result::Context)
92
+
93
+ def self.expected_outcome: () -> String
94
+ end
95
+ end
96
+
36
97
  class BCDD::Result::Context
37
98
  class Failure < BCDD::Result::Context
38
99
  include BCDD::Result::Failure::Methods
@@ -13,19 +13,22 @@ class BCDD::Result
13
13
  -> BCDD::Result::Error::UnexpectedOutcome
14
14
  end
15
15
 
16
- class InvalidResultSubject < BCDD::Result::Error
17
- def self.build: (given_result: BCDD::Result, expected_subject: untyped)
18
- -> BCDD::Result::Error::InvalidResultSubject
16
+ class InvalidResultSource < BCDD::Result::Error
17
+ def self.build: (given_result: BCDD::Result, expected_source: untyped)
18
+ -> BCDD::Result::Error::InvalidResultSource
19
19
  end
20
20
 
21
- class InvalidSubjectMethodArity < BCDD::Result::Error
22
- def self.build: (subject: untyped, method: Method, max_arity: Integer)
23
- -> BCDD::Result::Error::InvalidSubjectMethodArity
21
+ class InvalidSourceMethodArity < BCDD::Result::Error
22
+ def self.build: (source: untyped, method: Method, max_arity: Integer)
23
+ -> BCDD::Result::Error::InvalidSourceMethodArity
24
24
  end
25
25
 
26
26
  class UnhandledTypes < BCDD::Result::Error
27
27
  def self.build: (types: Set[Symbol])
28
28
  -> BCDD::Result::Error::UnhandledTypes
29
29
  end
30
+
31
+ class CallableAndThenDisabled < BCDD::Result::Error
32
+ end
30
33
  end
31
34
  end
@@ -16,14 +16,14 @@ class BCDD::Result::Expectations
16
16
  def self.result_factory_without_expectations: -> singleton(BCDD::Result)
17
17
 
18
18
  def self.new: (
19
- ?subject: untyped,
19
+ ?source: untyped,
20
20
  ?contract: BCDD::Result::Contract::Evaluator,
21
21
  ?terminal: bool,
22
22
  **untyped
23
23
  ) -> (BCDD::Result::Expectations | untyped)
24
24
 
25
25
  def initialize: (
26
- ?subject: untyped,
26
+ ?source: untyped,
27
27
  ?contract: BCDD::Result::Contract::Evaluator,
28
28
  ?terminal: bool,
29
29
  **untyped
@@ -32,13 +32,13 @@ class BCDD::Result::Expectations
32
32
  def Success: (Symbol, ?untyped) -> BCDD::Result::Success
33
33
  def Failure: (Symbol, ?untyped) -> BCDD::Result::Failure
34
34
 
35
- def with: (subject: untyped) -> BCDD::Result::Expectations
35
+ def with: (source: untyped) -> BCDD::Result::Expectations
36
36
 
37
37
  private
38
38
 
39
39
  def _ResultAs: (singleton(BCDD::Result), Symbol, untyped) -> untyped
40
40
 
41
- attr_reader subject: untyped
41
+ attr_reader source: untyped
42
42
  attr_reader contract: BCDD::Result::Contract::Evaluator
43
43
  attr_reader terminal: bool
44
44
  end