bcdd-result 0.11.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
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