workflow_template 0.0.4

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 (78) hide show
  1. checksums.yaml +7 -0
  2. data/lib/workflow_template/action/abstract.rb +40 -0
  3. data/lib/workflow_template/action/description/validated.rb +58 -0
  4. data/lib/workflow_template/action/description/wrapper.rb +28 -0
  5. data/lib/workflow_template/action/nested.rb +59 -0
  6. data/lib/workflow_template/action/simple.rb +28 -0
  7. data/lib/workflow_template/action/validated.rb +104 -0
  8. data/lib/workflow_template/action/wrapper.rb +32 -0
  9. data/lib/workflow_template/action.rb +20 -0
  10. data/lib/workflow_template/adapters/state/default.rb +72 -0
  11. data/lib/workflow_template/adapters/state/dry_monads.rb +70 -0
  12. data/lib/workflow_template/adapters/validation/active_model/builder.rb +18 -0
  13. data/lib/workflow_template/adapters/validation/active_model/failure.rb +17 -0
  14. data/lib/workflow_template/adapters/validation/active_model/null_model.rb +24 -0
  15. data/lib/workflow_template/adapters/validation/active_model/validator.rb +35 -0
  16. data/lib/workflow_template/adapters/validation/active_model.rb +24 -0
  17. data/lib/workflow_template/adapters/validation/dry_validation/builder.rb +18 -0
  18. data/lib/workflow_template/adapters/validation/dry_validation/failure.rb +19 -0
  19. data/lib/workflow_template/adapters/validation/dry_validation/validator.rb +46 -0
  20. data/lib/workflow_template/adapters/validation/dry_validation.rb +24 -0
  21. data/lib/workflow_template/adapters/validation/generic/block.rb +24 -0
  22. data/lib/workflow_template/adapters/validation/generic/builder.rb +28 -0
  23. data/lib/workflow_template/adapters/validation/generic/object.rb +30 -0
  24. data/lib/workflow_template/adapters/validation/generic/validator.rb +25 -0
  25. data/lib/workflow_template/adapters/validation/generic.rb +24 -0
  26. data/lib/workflow_template/definer/has_actions/actions.rb +90 -0
  27. data/lib/workflow_template/definer/has_actions/description.rb +48 -0
  28. data/lib/workflow_template/definer/has_actions/dsl.rb +64 -0
  29. data/lib/workflow_template/definer/has_actions/insert.rb +28 -0
  30. data/lib/workflow_template/definer/has_actions/inside.rb +27 -0
  31. data/lib/workflow_template/definer/has_actions/nested.rb +65 -0
  32. data/lib/workflow_template/definer/has_actions/position.rb +83 -0
  33. data/lib/workflow_template/definer/has_actions/redefine.rb +28 -0
  34. data/lib/workflow_template/definer/has_actions/root.rb +22 -0
  35. data/lib/workflow_template/definer/has_actions.rb +45 -0
  36. data/lib/workflow_template/definer/has_default_state_transition_strategy.rb +34 -0
  37. data/lib/workflow_template/definer/has_output_normalizer.rb +35 -0
  38. data/lib/workflow_template/definer/has_state_adapter.rb +30 -0
  39. data/lib/workflow_template/definer/has_validations/action.rb +20 -0
  40. data/lib/workflow_template/definer/has_validations/description.rb +38 -0
  41. data/lib/workflow_template/definer/has_validations/validations.rb +162 -0
  42. data/lib/workflow_template/definer/has_validations.rb +70 -0
  43. data/lib/workflow_template/error.rb +99 -0
  44. data/lib/workflow_template/outcome/block.rb +58 -0
  45. data/lib/workflow_template/outcome/handler.rb +106 -0
  46. data/lib/workflow_template/outcome/status.rb +33 -0
  47. data/lib/workflow_template/outcome/statuses.rb +60 -0
  48. data/lib/workflow_template/outcome.rb +53 -0
  49. data/lib/workflow_template/performer.rb +46 -0
  50. data/lib/workflow_template/state/abstract.rb +33 -0
  51. data/lib/workflow_template/state/adapter.rb +67 -0
  52. data/lib/workflow_template/state/class_methods.rb +43 -0
  53. data/lib/workflow_template/state/final.rb +74 -0
  54. data/lib/workflow_template/state/intermediate.rb +102 -0
  55. data/lib/workflow_template/state/meta.rb +35 -0
  56. data/lib/workflow_template/state/normalizer.rb +34 -0
  57. data/lib/workflow_template/state/validation.rb +32 -0
  58. data/lib/workflow_template/state.rb +41 -0
  59. data/lib/workflow_template/validation/adapter/abstract/builder.rb +28 -0
  60. data/lib/workflow_template/validation/adapter/abstract/result/failure.rb +25 -0
  61. data/lib/workflow_template/validation/adapter/abstract/validator.rb +23 -0
  62. data/lib/workflow_template/validation/adapter.rb +35 -0
  63. data/lib/workflow_template/validation/builder/error.rb +11 -0
  64. data/lib/workflow_template/validation/builder/proxy.rb +129 -0
  65. data/lib/workflow_template/validation/error.rb +16 -0
  66. data/lib/workflow_template/validation/evaluation/abstract.rb +57 -0
  67. data/lib/workflow_template/validation/evaluation/boolean.rb +28 -0
  68. data/lib/workflow_template/validation/evaluation/canonical.rb +21 -0
  69. data/lib/workflow_template/validation/evaluation.rb +4 -0
  70. data/lib/workflow_template/validation/result/failure/abstract.rb +15 -0
  71. data/lib/workflow_template/validation/result/failure.rb +52 -0
  72. data/lib/workflow_template/validation/result/success.rb +19 -0
  73. data/lib/workflow_template/validation/validator/result/failure.rb +36 -0
  74. data/lib/workflow_template/validation/validator/result/success.rb +21 -0
  75. data/lib/workflow_template/workflow/description.rb +56 -0
  76. data/lib/workflow_template/workflow.rb +69 -0
  77. data/lib/workflow_template.rb +11 -0
  78. metadata +126 -0
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../performer'
4
+ require_relative 'has_actions/actions'
5
+ require_relative 'has_actions/nested'
6
+
7
+ module WorkflowTemplate
8
+ module Definer
9
+ module HasActions
10
+ def actions
11
+ return @actions if frozen?
12
+
13
+ @own_actions ||= Actions::Unprepared.instance
14
+ return @own_actions.prepared unless defined?(superclass) && superclass.respond_to?(:actions)
15
+
16
+ superclass.actions.merge(@own_actions)
17
+ end
18
+
19
+ def freeze
20
+ unless frozen?
21
+ @actions = prepare_actions
22
+ @own_actions = nil
23
+ end
24
+
25
+ super
26
+ end
27
+
28
+ protected
29
+
30
+ def prepare_actions
31
+ actions
32
+ end
33
+
34
+ def store_action(action, position)
35
+ @own_actions ||= Actions::Unprepared.instance
36
+ @own_actions.add_simple(action, position)
37
+ end
38
+
39
+ def store_wrapper_action(action)
40
+ @own_actions ||= Actions::Unprepared.instance
41
+ @own_actions.add_wrapper(action)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../state'
4
+ require_relative '../workflow/description'
5
+
6
+ module WorkflowTemplate
7
+ module Definer
8
+ module HasDefaultStateTransitionStrategy
9
+ Workflow::Description.register(:initialize, :state) do |workflow|
10
+ "state: #{workflow.default_state_transition_strategy}"
11
+ end
12
+
13
+ def default_state_transition_strategy(*args)
14
+ case args.length
15
+ when 0
16
+ return @default_state_transition_strategy if defined? @default_state_transition_strategy
17
+ if defined?(superclass) && superclass.respond_to?(:default_state_transition_strategy)
18
+ return superclass.default_state_transition_strategy
19
+ end
20
+
21
+ :merge
22
+ when 1 then @default_state_transition_strategy = State.normalize_transition_strategy(args[0])
23
+ else raise Error, "Unexpected arguments for state transition strategy: '#{args}'"
24
+ end
25
+ end
26
+
27
+ def freeze
28
+ @default_state_transition_strategy = default_state_transition_strategy unless frozen?
29
+
30
+ super
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../state'
4
+ require_relative '../workflow/description'
5
+
6
+ module WorkflowTemplate
7
+ module Definer
8
+ module HasOutputNormalizer
9
+ Workflow::Description.register(:finalize, :output_normalizer) do |workflow|
10
+ next if workflow.send(:output_normalizer).keys.nil?
11
+
12
+ "normalizes output: #{workflow.send(:output_normalizer).keys.join(', ')}"
13
+ end
14
+
15
+ def normalize_output(*keys)
16
+ @output_normalizer = State::Normalizer.instance(keys)
17
+ end
18
+
19
+ def freeze
20
+ @output_normalizer = output_normalizer unless frozen?
21
+
22
+ super
23
+ end
24
+
25
+ protected
26
+
27
+ def output_normalizer
28
+ return @output_normalizer if defined? @output_normalizer
29
+ return State::Normalizer.instance unless defined?(superclass) && superclass.respond_to?(:output_normalizer)
30
+
31
+ superclass.output_normalizer
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../adapters/state/default'
4
+ require_relative '../workflow/description'
5
+
6
+ module WorkflowTemplate
7
+ module Definer
8
+ module HasStateAdapter
9
+ def state_adapter(*args)
10
+ case args.length
11
+ when 0
12
+ return @state_adapter if defined? @state_adapter
13
+ if defined?(superclass) && superclass.respond_to?(:state_adapter)
14
+ return superclass.state_adapter
15
+ end
16
+
17
+ State::Adapter.fetch(:default)
18
+ when 1 then @state_adapter = State::Adapter.fetch(args[0])
19
+ else raise Error, "Unexpected arguments for state adapter: '#{args}'"
20
+ end
21
+ end
22
+
23
+ def freeze
24
+ @state_adapter = state_adapter unless frozen?
25
+
26
+ super
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WorkflowTemplate
4
+ module Definer
5
+ module HasValidations
6
+ class Action
7
+ def initialize(action, validations)
8
+ @action = action
9
+ @validations = validations
10
+ end
11
+
12
+ attr_reader :validations
13
+
14
+ private
15
+
16
+ attr_reader :action
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../workflow/description'
4
+
5
+ module WorkflowTemplate
6
+ module Definer
7
+ module HasValidations
8
+ module Description
9
+ Workflow::Description.register(:initialize, :input_validations) do |workflow|
10
+ next if void?(workflow.send(:validations).input)
11
+
12
+ "validates input: #{describe_entry(workflow.send(:validations).input)}"
13
+ end
14
+
15
+ Workflow::Description.register(:finalize, :output_validations) do |workflow|
16
+ next if void?(workflow.send(:validations).output)
17
+
18
+ "validates output: #{describe_entry(workflow.send(:validations).output)}"
19
+ end
20
+
21
+ def self.describe_entry(entry)
22
+ case entry
23
+ when Array then "{ #{entry.join(', ')} }"
24
+ else entry
25
+ end
26
+ end
27
+
28
+ def self.void?(entry)
29
+ case entry
30
+ when nil then true
31
+ when Symbol then false
32
+ else entry.empty?
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+ require 'forwardable'
5
+ require_relative '../../error'
6
+ require_relative '../../validation/evaluation/abstract'
7
+
8
+ module WorkflowTemplate
9
+ module Definer
10
+ module HasValidations
11
+ module Validations
12
+ def self.merge_entries(current_entry, new_entry)
13
+ new_entry = normalize_entry(new_entry)
14
+ return new_entry if current_entry.nil?
15
+ return current_entry if new_entry.nil?
16
+
17
+ entry_to_set(current_entry) | entry_to_set(new_entry)
18
+ end
19
+
20
+ def self.entry_to_set(entry)
21
+ case entry
22
+ when Set then entry
23
+ when Array then entry.to_set
24
+ else Set.new([entry])
25
+ end
26
+ end
27
+
28
+ def self.normalize_entry(entry)
29
+ case entry
30
+ when nil then nil
31
+ when Array, Set then entry.to_a.to_set(&:to_sym)
32
+ else entry.to_sym
33
+ end
34
+ end
35
+
36
+ class Unprepared
37
+ def self.instance
38
+ new
39
+ end
40
+
41
+ attr_reader :declarations
42
+
43
+ def initialize(declarations: {}, input_set: nil, output_set: nil)
44
+ @declarations = declarations.freeze
45
+ @input_set = Validations.normalize_entry(input_set)
46
+ @output_set = Validations.normalize_entry(output_set)
47
+ end
48
+
49
+ private_class_method :new
50
+
51
+ def add(declaration)
52
+ raise Error, "Declaration name taken: '#{declaration.name}'" if @declarations.key? declaration.name
53
+ raise Error, "Validation '#{declaration.name}' not properly declared" unless valid_declaration?(declaration)
54
+
55
+ @declarations = declarations.merge(declaration.name => declaration).freeze
56
+ declaration
57
+ end
58
+
59
+ def validate_input(entry)
60
+ @input_set = Validations.merge_entries(@input_set, entry)
61
+ end
62
+
63
+ def validate_output(entry)
64
+ @output_set = Validations.merge_entries(@output_set, entry)
65
+ end
66
+
67
+ def prepared
68
+ Prepared.instance declarations: declarations, input: input_set, output: output_set
69
+ end
70
+
71
+ def freeze
72
+ raise Error, "Freeze is unimplemented for #{self.class.name}, call #prepared instead"
73
+ end
74
+
75
+ protected
76
+
77
+ attr_reader :input_set, :output_set
78
+
79
+ def valid_declaration?(declaration)
80
+ declaration.is_a?(Validation::Evaluation::Abstract)
81
+ end
82
+ end
83
+
84
+ class Prepared
85
+ attr_reader :declarations, :input, :output
86
+
87
+ def self.instance(declarations:, input:, output:)
88
+ new(declarations, normalize_entry(input), normalize_entry(output))
89
+ end
90
+
91
+ def self.normalize_entry(entry)
92
+ case entry
93
+ when nil then nil
94
+ when Array, Set then entry.to_a.map(&:to_sym)
95
+ else entry.to_sym
96
+ end
97
+ end
98
+
99
+ private_class_method :new
100
+
101
+ def initialize(declarations, input, output)
102
+ @declarations = declarations.freeze
103
+ @input = input.freeze
104
+ @output = output.freeze
105
+ freeze
106
+ end
107
+
108
+ def merge(other)
109
+ merged_validations = declarations.merge other.declarations
110
+ merged_input = Validations.merge_entries(input, other.send(:input_set))
111
+ merged_output = Validations.merge_entries(output, other.send(:output_set))
112
+
113
+ Prepared.instance(
114
+ declarations: merged_validations,
115
+ input: merged_input,
116
+ output: merged_output
117
+ )
118
+ end
119
+
120
+ def resolve_validations(option)
121
+ case option
122
+ when nil then nil
123
+ when Array then option.map { |element| resolve_validation(element) }.freeze
124
+ else resolve_validation(option).freeze
125
+ end
126
+ end
127
+
128
+ def resolve_validation(name)
129
+ name = name.to_sym
130
+ raise Error, "Validation not declared: #{name}" unless declarations.key? name
131
+
132
+ declarations[name]
133
+ end
134
+
135
+ def resolved
136
+ Resolved.new(self)
137
+ end
138
+ end
139
+
140
+ class Resolved
141
+ extend Forwardable
142
+
143
+ def_delegators :@prepared, :declarations
144
+ def_delegators :@prepared, :input
145
+ def_delegators :@prepared, :output
146
+
147
+ def_delegators :@prepared, :merge
148
+ def_delegators :@prepared, :resolve_validations
149
+
150
+ def initialize(prepared)
151
+ @prepared = prepared
152
+ @input_validations = prepared.resolve_validations(prepared.input)
153
+ @output_validations = prepared.resolve_validations(prepared.output)
154
+ freeze
155
+ end
156
+
157
+ attr_reader :input_validations, :output_validations
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../validation/adapter'
4
+ require_relative '../validation/builder/proxy'
5
+ require_relative '../adapters/validation/generic'
6
+ require_relative 'has_validations/validations'
7
+
8
+ module WorkflowTemplate
9
+ module Definer
10
+ module HasValidations
11
+ def default_validation_adapter(*args)
12
+ case args.length
13
+ when 0
14
+ return @default_validation_adapter if defined? @default_validation_adapter
15
+ if defined?(superclass) && superclass.respond_to?(:default_validation_adapter)
16
+ return superclass.default_validation_adapter
17
+ end
18
+
19
+ :default
20
+ when 1 then @default_validation_adapter = args[0]
21
+ else raise Error, "Unexpected arguments for default validation adapter: '#{args}'"
22
+ end
23
+ end
24
+
25
+ def declare_validation(name = nil, using: default_validation_adapter)
26
+ adapter = Validation::Adapter.fetch(using)
27
+
28
+ Validation::Builder::Proxy
29
+ .instance(adapter, own_validations, name: name)
30
+ end
31
+
32
+ def validate_input(keys)
33
+ own_validations.validate_input(keys)
34
+ end
35
+
36
+ def validate_output(keys)
37
+ own_validations.validate_output(keys)
38
+ end
39
+
40
+ def freeze
41
+ unless frozen?
42
+ @default_validation_adapter = default_validation_adapter
43
+ @validations = validations.resolved
44
+ @own_validations = nil
45
+ end
46
+
47
+ super
48
+ end
49
+
50
+ protected
51
+
52
+ def prepare_action(action)
53
+ super.prepare(validations)
54
+ end
55
+
56
+ def validations
57
+ return @validations if defined? @validations
58
+ return own_validations.prepared unless defined?(superclass) && superclass.respond_to?(:validations, true)
59
+
60
+ superclass.validations.merge(own_validations)
61
+ end
62
+
63
+ private
64
+
65
+ def own_validations
66
+ @own_validations ||= Validations::Unprepared.instance
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WorkflowTemplate
4
+ class Error < RuntimeError; end
5
+
6
+ class Fatal < Error
7
+ class ArgumentError < Fatal
8
+ def initialize(action_name, message)
9
+ full_message = "Argument mismatch in '#{action_name}': #{message}"
10
+
11
+ super(full_message)
12
+ end
13
+
14
+ def self.depth(argument_error, method)
15
+ path, = method.source_location
16
+ label = method.name.to_s
17
+ _, index = argument_error.backtrace_locations.each_with_index.find do |location, _index|
18
+ location.label == label && location.path == path
19
+ end
20
+
21
+ index
22
+ end
23
+ end
24
+
25
+ class InconsistentState < Fatal
26
+ end
27
+
28
+ class BadActionReturn < Fatal
29
+ def initialize(action_name, value)
30
+ full_message = <<~ERR.squish
31
+ Invalid return from '#{action_name}' action, nil or Hash expected,
32
+ got '#{value.inspect}' (#{value.class.name})
33
+ ERR
34
+
35
+ super(full_message)
36
+ end
37
+ end
38
+
39
+ class BadReturnKeys < Fatal
40
+ def initialize(action_name, keys)
41
+ keys = keys.map { |key| "'#{key.inspect}' (#{key.class.name})" }
42
+ full_message = "Invalid return from '#{action_name}' action, symbolic keys expected, got #{keys.join(', ')}"
43
+ super(full_message)
44
+ end
45
+ end
46
+
47
+ class BadValidationReturn < Fatal
48
+ def initialize(action_name)
49
+ full_message = "Invalid return from '#{action_name}' validation"
50
+ super(full_message)
51
+ end
52
+ end
53
+
54
+ class BadInput < Fatal
55
+ def initialize(value)
56
+ full_message = "Invalid input, Hash expected, got '#{value.inspect}' (#{value.class.name})"
57
+ super(full_message)
58
+ end
59
+ end
60
+
61
+ class Detailed < Fatal
62
+ def initialize(action_name, input, message)
63
+ full_message = "Fatal error in '#{action_name}': #{message}, input: #{input.inspect}"
64
+ super(full_message)
65
+ end
66
+ end
67
+
68
+ class ForeignCodeError < Fatal
69
+ attr_reader :foreign_error
70
+
71
+ def initialize(foreign_error)
72
+ @foreign_error = foreign_error
73
+ full_message = "Foreign error #{foreign_error.class.name}: #{foreign_error.message}"
74
+ super(full_message)
75
+ end
76
+ end
77
+
78
+ class UnexpectedNil < Fatal
79
+ def initialize(action_name)
80
+ full_message = "Nil return from '#{action_name}', nil is not allowed for 'update' state transition strategy"
81
+ super(full_message)
82
+ end
83
+ end
84
+
85
+ class Unimplemented < Fatal
86
+ def initialize(action_name)
87
+ super("Unimplemented method: '#{action_name}'")
88
+ end
89
+ end
90
+ end
91
+
92
+ class ExecutionError < Error
93
+ def initialize(original_error)
94
+ @original_error = original_error
95
+ super(original_error.to_s)
96
+ freeze
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../validation/result/failure'
4
+
5
+ module WorkflowTemplate
6
+ class Outcome
7
+ class Block
8
+ attr_reader :handler
9
+
10
+ def initialize(handler, binding)
11
+ @handler = handler
12
+ @binding = binding
13
+ end
14
+
15
+ def ok(&block)
16
+ handle_status(:ok, &block)
17
+ end
18
+
19
+ def invalid(&block)
20
+ handle_status(:error, matcher: Validation::Result::Failure, &block)
21
+ end
22
+
23
+ def error(match = nil, &block)
24
+ handle_status(:error, matcher: match, &block)
25
+ end
26
+
27
+ def otherwise(&block)
28
+ self.handler = handler.otherwise(binding: binding, &block)
29
+ end
30
+
31
+ def otherwise_unwrap(slice: nil, fetch: nil)
32
+ self.handler = handler.otherwise_unwrap(slice: slice, fetch: fetch)
33
+ end
34
+
35
+ def otherwise_raise(error: nil)
36
+ handler = handler()
37
+ self.handler = handler.otherwise(binding: binding) do |_|
38
+ error ||= Error.new("Unhandled workflow status: '#{handler.final_state.status}'")
39
+ raise error
40
+ end
41
+ end
42
+
43
+ def delegate_to(proc)
44
+ instance_eval(&proc)
45
+ end
46
+
47
+ private
48
+
49
+ attr_reader :binding
50
+ attr_writer :handler
51
+
52
+ def handle_status(status, **opts, &block)
53
+ self.handler = handler.handle_status(status, binding: binding, **opts, &block)
54
+ self
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require_relative 'statuses'
5
+ require_relative '../validation/error'
6
+
7
+ module WorkflowTemplate
8
+ class Outcome
9
+ class Handler
10
+ extend Forwardable
11
+
12
+ attr_reader :final_state, :retval
13
+
14
+ def_delegator :@statuses, :unhandled?
15
+ def_delegator :@statuses, :default_missing?
16
+ def_delegator :@statuses, :default_missing
17
+
18
+ def initialize(final_state, statuses: Outcome::Statuses.new, retval: nil)
19
+ @final_state = final_state
20
+ @statuses = statuses
21
+ @retval = retval
22
+ freeze
23
+ end
24
+
25
+ def handle_status(status, binding: nil, **opts, &block) # rubocop:disable Metrics/AbcSize
26
+ status = status.to_sym
27
+ raise WorkflowTemplate::Error, "Handler for '#{status}' unexpected" if @statuses[status].default?
28
+
29
+ statuses = @statuses.on_default_detected(status, self.class.default?(status, **opts))
30
+ if unhandled?(final_state.status) && self.class.run_handler?(status, final_state, **opts)
31
+ self.class.run_handler(status, statuses, final_state, binding: binding, &block)
32
+ else
33
+ Handler.new(final_state, statuses: statuses, retval: retval)
34
+ end
35
+ end
36
+
37
+ def otherwise(binding: nil, &block)
38
+ statuses = @statuses.all_defaults!
39
+ if unhandled?(final_state.status)
40
+ self.class.run_handler(final_state.status, statuses, final_state, binding: binding, &block)
41
+ else
42
+ Handler.new(final_state, statuses: statuses, retval: retval)
43
+ end
44
+ end
45
+
46
+ def otherwise_unwrap(slice: nil, fetch: nil)
47
+ statuses = @statuses.all_defaults!
48
+ if unhandled?(final_state.status)
49
+ Handler.new(
50
+ final_state,
51
+ statuses: statuses.handled!(final_state.status),
52
+ retval: final_state.unwrap(slice: slice, fetch: fetch)
53
+ )
54
+ else
55
+ Handler.new(final_state, statuses: statuses, retval: retval)
56
+ end
57
+ end
58
+
59
+ def self.run_handler(status, statuses, final_state, binding: nil, &block)
60
+ statuses = statuses.handled!(status)
61
+ retval = final_state.state_class.run_handler!(final_state, binding, &block)
62
+ Handler.new(final_state, statuses: statuses, retval: retval)
63
+ end
64
+
65
+ def self.default?(status, **opts)
66
+ handler_strategy(status).default?(**opts)
67
+ end
68
+
69
+ def self.run_handler?(status, final_state, **opts)
70
+ return false unless status == final_state.status
71
+
72
+ handler_strategy(status).run_handler?(final_state, **opts)
73
+ end
74
+
75
+ def self.handler_strategy(status)
76
+ case status
77
+ when :ok then Ok
78
+ when :error then Error
79
+ else raise WorkflowTemplate::Error, "Unexpected status '#{status}'"
80
+ end
81
+ end
82
+
83
+ module Ok
84
+ def self.default?
85
+ true
86
+ end
87
+
88
+ def self.run_handler?(_final_state)
89
+ true
90
+ end
91
+ end
92
+
93
+ module Error
94
+ def self.default?(matcher: nil)
95
+ matcher.nil?
96
+ end
97
+
98
+ def self.run_handler?(final_state, matcher: nil)
99
+ # rubocop:disable Style/CaseEquality
100
+ matcher.nil? || matcher === final_state.error
101
+ # rubocop:enable Style/CaseEquality
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end