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.
- checksums.yaml +7 -0
- data/lib/workflow_template/action/abstract.rb +40 -0
- data/lib/workflow_template/action/description/validated.rb +58 -0
- data/lib/workflow_template/action/description/wrapper.rb +28 -0
- data/lib/workflow_template/action/nested.rb +59 -0
- data/lib/workflow_template/action/simple.rb +28 -0
- data/lib/workflow_template/action/validated.rb +104 -0
- data/lib/workflow_template/action/wrapper.rb +32 -0
- data/lib/workflow_template/action.rb +20 -0
- data/lib/workflow_template/adapters/state/default.rb +72 -0
- data/lib/workflow_template/adapters/state/dry_monads.rb +70 -0
- data/lib/workflow_template/adapters/validation/active_model/builder.rb +18 -0
- data/lib/workflow_template/adapters/validation/active_model/failure.rb +17 -0
- data/lib/workflow_template/adapters/validation/active_model/null_model.rb +24 -0
- data/lib/workflow_template/adapters/validation/active_model/validator.rb +35 -0
- data/lib/workflow_template/adapters/validation/active_model.rb +24 -0
- data/lib/workflow_template/adapters/validation/dry_validation/builder.rb +18 -0
- data/lib/workflow_template/adapters/validation/dry_validation/failure.rb +19 -0
- data/lib/workflow_template/adapters/validation/dry_validation/validator.rb +46 -0
- data/lib/workflow_template/adapters/validation/dry_validation.rb +24 -0
- data/lib/workflow_template/adapters/validation/generic/block.rb +24 -0
- data/lib/workflow_template/adapters/validation/generic/builder.rb +28 -0
- data/lib/workflow_template/adapters/validation/generic/object.rb +30 -0
- data/lib/workflow_template/adapters/validation/generic/validator.rb +25 -0
- data/lib/workflow_template/adapters/validation/generic.rb +24 -0
- data/lib/workflow_template/definer/has_actions/actions.rb +90 -0
- data/lib/workflow_template/definer/has_actions/description.rb +48 -0
- data/lib/workflow_template/definer/has_actions/dsl.rb +64 -0
- data/lib/workflow_template/definer/has_actions/insert.rb +28 -0
- data/lib/workflow_template/definer/has_actions/inside.rb +27 -0
- data/lib/workflow_template/definer/has_actions/nested.rb +65 -0
- data/lib/workflow_template/definer/has_actions/position.rb +83 -0
- data/lib/workflow_template/definer/has_actions/redefine.rb +28 -0
- data/lib/workflow_template/definer/has_actions/root.rb +22 -0
- data/lib/workflow_template/definer/has_actions.rb +45 -0
- data/lib/workflow_template/definer/has_default_state_transition_strategy.rb +34 -0
- data/lib/workflow_template/definer/has_output_normalizer.rb +35 -0
- data/lib/workflow_template/definer/has_state_adapter.rb +30 -0
- data/lib/workflow_template/definer/has_validations/action.rb +20 -0
- data/lib/workflow_template/definer/has_validations/description.rb +38 -0
- data/lib/workflow_template/definer/has_validations/validations.rb +162 -0
- data/lib/workflow_template/definer/has_validations.rb +70 -0
- data/lib/workflow_template/error.rb +99 -0
- data/lib/workflow_template/outcome/block.rb +58 -0
- data/lib/workflow_template/outcome/handler.rb +106 -0
- data/lib/workflow_template/outcome/status.rb +33 -0
- data/lib/workflow_template/outcome/statuses.rb +60 -0
- data/lib/workflow_template/outcome.rb +53 -0
- data/lib/workflow_template/performer.rb +46 -0
- data/lib/workflow_template/state/abstract.rb +33 -0
- data/lib/workflow_template/state/adapter.rb +67 -0
- data/lib/workflow_template/state/class_methods.rb +43 -0
- data/lib/workflow_template/state/final.rb +74 -0
- data/lib/workflow_template/state/intermediate.rb +102 -0
- data/lib/workflow_template/state/meta.rb +35 -0
- data/lib/workflow_template/state/normalizer.rb +34 -0
- data/lib/workflow_template/state/validation.rb +32 -0
- data/lib/workflow_template/state.rb +41 -0
- data/lib/workflow_template/validation/adapter/abstract/builder.rb +28 -0
- data/lib/workflow_template/validation/adapter/abstract/result/failure.rb +25 -0
- data/lib/workflow_template/validation/adapter/abstract/validator.rb +23 -0
- data/lib/workflow_template/validation/adapter.rb +35 -0
- data/lib/workflow_template/validation/builder/error.rb +11 -0
- data/lib/workflow_template/validation/builder/proxy.rb +129 -0
- data/lib/workflow_template/validation/error.rb +16 -0
- data/lib/workflow_template/validation/evaluation/abstract.rb +57 -0
- data/lib/workflow_template/validation/evaluation/boolean.rb +28 -0
- data/lib/workflow_template/validation/evaluation/canonical.rb +21 -0
- data/lib/workflow_template/validation/evaluation.rb +4 -0
- data/lib/workflow_template/validation/result/failure/abstract.rb +15 -0
- data/lib/workflow_template/validation/result/failure.rb +52 -0
- data/lib/workflow_template/validation/result/success.rb +19 -0
- data/lib/workflow_template/validation/validator/result/failure.rb +36 -0
- data/lib/workflow_template/validation/validator/result/success.rb +21 -0
- data/lib/workflow_template/workflow/description.rb +56 -0
- data/lib/workflow_template/workflow.rb +69 -0
- data/lib/workflow_template.rb +11 -0
- metadata +126 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WorkflowTemplate
|
|
4
|
+
module Validation
|
|
5
|
+
module Adapter
|
|
6
|
+
module Abstract
|
|
7
|
+
module Result
|
|
8
|
+
module Failure
|
|
9
|
+
def code
|
|
10
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def message(*)
|
|
14
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def data
|
|
18
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WorkflowTemplate
|
|
4
|
+
module Validation
|
|
5
|
+
module Adapter
|
|
6
|
+
module Abstract
|
|
7
|
+
module Validator
|
|
8
|
+
def initialize
|
|
9
|
+
freeze
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def validate(_data)
|
|
13
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def describe
|
|
17
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'adapter/abstract/builder'
|
|
4
|
+
require_relative 'adapter/abstract/validator'
|
|
5
|
+
require_relative 'adapter/abstract/result/failure'
|
|
6
|
+
require_relative 'validator/result/success'
|
|
7
|
+
|
|
8
|
+
module WorkflowTemplate
|
|
9
|
+
module Validation
|
|
10
|
+
module Adapter
|
|
11
|
+
def self.registry
|
|
12
|
+
@registry ||= {}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.register(name, adapter)
|
|
16
|
+
registry[name.to_sym] = adapter
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.fetch(name)
|
|
20
|
+
name = name.to_sym
|
|
21
|
+
raise Error, "Not a registered validation adapter: #{name}" unless registry.key? name
|
|
22
|
+
|
|
23
|
+
registry[name]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def builder_class
|
|
27
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def register(name)
|
|
31
|
+
Adapter.register(name, self)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'error'
|
|
4
|
+
require_relative '../evaluation'
|
|
5
|
+
|
|
6
|
+
module WorkflowTemplate
|
|
7
|
+
module Validation
|
|
8
|
+
module Builder
|
|
9
|
+
class Proxy
|
|
10
|
+
def self.instance(adapter, collection, name: nil, context: nil)
|
|
11
|
+
new adapter, collection, normalize_name(name), normalize_context(context)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.normalize_name(name)
|
|
15
|
+
return if name.nil?
|
|
16
|
+
|
|
17
|
+
raise Error, 'Empty name' if name.empty?
|
|
18
|
+
|
|
19
|
+
name.to_sym
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.normalize_context(context)
|
|
23
|
+
return if context.nil? || context.empty?
|
|
24
|
+
|
|
25
|
+
raise Error, "Context contains invalid keys: #{context}" if context.any?(&:empty?)
|
|
26
|
+
|
|
27
|
+
context.map(&:to_sym)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private_class_method :new
|
|
31
|
+
|
|
32
|
+
def initialize(adapter, collection, name, context)
|
|
33
|
+
@adapter = adapter
|
|
34
|
+
@collection = collection
|
|
35
|
+
@name = name&.to_sym
|
|
36
|
+
@context = context.freeze
|
|
37
|
+
freeze
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
attr_reader :name, :context
|
|
41
|
+
|
|
42
|
+
def key?
|
|
43
|
+
false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def key
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def named(name)
|
|
51
|
+
raise Error, "Name already defined: #{self.name}" if self.name
|
|
52
|
+
|
|
53
|
+
self.class.instance(adapter, collection, name: name, context: context)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def with_context(*context)
|
|
57
|
+
raise Error, "Context already defined: #{self.context}" if self.context
|
|
58
|
+
|
|
59
|
+
self.class.instance(adapter, collection, name: name, context: context)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def validate(key = nil, **opts, &block)
|
|
63
|
+
proxy = if key
|
|
64
|
+
Keyed.instance(key, adapter, collection, name: name, context: context)
|
|
65
|
+
else
|
|
66
|
+
raise Error, 'Key must be supplied if name is missing' if name.nil?
|
|
67
|
+
raise Error, 'Context is not available if key is not specified' unless context.nil?
|
|
68
|
+
|
|
69
|
+
self
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
adapter.builder_class
|
|
73
|
+
.new(proxy)
|
|
74
|
+
.validate(**opts, &block)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def declare(validation)
|
|
78
|
+
collection.add(evaluation(validation))
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
attr_reader :adapter, :collection
|
|
84
|
+
|
|
85
|
+
def evaluation(validation)
|
|
86
|
+
evaluation_class.new(name, key, context, validation)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def evaluation_class
|
|
90
|
+
case adapter.result_type
|
|
91
|
+
when :boolean then Evaluation::Boolean
|
|
92
|
+
when :canonical then Evaluation::Canonical
|
|
93
|
+
else raise Error, "Unexpected result type: #{adapter.result_type}"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
class Keyed < Proxy
|
|
98
|
+
def self.instance(key, adapter, collection, name: nil, context: nil)
|
|
99
|
+
key = normalize_key(key)
|
|
100
|
+
new key, adapter, collection, normalize_name(name) || key, normalize_context(context)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def self.normalize_key(key)
|
|
104
|
+
return if key.nil?
|
|
105
|
+
|
|
106
|
+
raise Error, 'Empty key' if key.empty?
|
|
107
|
+
|
|
108
|
+
key.to_sym
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def initialize(key, *args)
|
|
112
|
+
@key = key.to_sym
|
|
113
|
+
super(*args)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
attr_reader :key
|
|
117
|
+
|
|
118
|
+
def key?
|
|
119
|
+
true
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def validate(**opts, &block)
|
|
123
|
+
adapter.builder_class.new(self).validate(**opts, &block)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../error'
|
|
4
|
+
|
|
5
|
+
module WorkflowTemplate
|
|
6
|
+
module Validation
|
|
7
|
+
class Error < WorkflowTemplate::Error
|
|
8
|
+
attr_reader :failure
|
|
9
|
+
|
|
10
|
+
def initialize(failure)
|
|
11
|
+
@failure = failure
|
|
12
|
+
super(failure.describe)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../error'
|
|
4
|
+
require_relative '../adapter/abstract/result/failure'
|
|
5
|
+
require_relative '../validator/result/success'
|
|
6
|
+
require_relative '../validator/result/failure'
|
|
7
|
+
require_relative '../result/failure'
|
|
8
|
+
require_relative '../result/success'
|
|
9
|
+
|
|
10
|
+
module WorkflowTemplate
|
|
11
|
+
module Validation
|
|
12
|
+
module Evaluation
|
|
13
|
+
class Abstract
|
|
14
|
+
attr_reader :name, :key, :context, :validator
|
|
15
|
+
|
|
16
|
+
def initialize(name, key, context, validator)
|
|
17
|
+
@name = name.to_sym
|
|
18
|
+
@key = key&.to_sym
|
|
19
|
+
@context = context.freeze
|
|
20
|
+
@validator = validator
|
|
21
|
+
freeze
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def call(data)
|
|
25
|
+
result = run_validator(data)
|
|
26
|
+
evaluate(result)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def evaluate(_result)
|
|
30
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def describe
|
|
34
|
+
validator.describe
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def run_validator(data)
|
|
40
|
+
input = key.nil? ? data : data[key]
|
|
41
|
+
case context
|
|
42
|
+
when nil then validator.call(input)
|
|
43
|
+
else validator.call(input, context: data&.slice(*context))
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_failure(result)
|
|
48
|
+
if result.is_a? Adapter::Abstract::Result::Failure
|
|
49
|
+
Result::Failure.instance(name, key, result)
|
|
50
|
+
else
|
|
51
|
+
to_failure(Validator::Result::Failure.new(validator, data: result))
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'abstract'
|
|
4
|
+
|
|
5
|
+
module WorkflowTemplate
|
|
6
|
+
module Validation
|
|
7
|
+
module Evaluation
|
|
8
|
+
class Boolean < Abstract
|
|
9
|
+
def evaluate(result)
|
|
10
|
+
result, detail = result
|
|
11
|
+
if result
|
|
12
|
+
case detail
|
|
13
|
+
when nil then Result::Success
|
|
14
|
+
when Hash, Array
|
|
15
|
+
raise Fatal::BadValidationReturn, name unless detail.empty?
|
|
16
|
+
|
|
17
|
+
Result::Success
|
|
18
|
+
else
|
|
19
|
+
raise Fatal::BadValidationReturn, name
|
|
20
|
+
end
|
|
21
|
+
else
|
|
22
|
+
to_failure(detail)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'abstract'
|
|
4
|
+
|
|
5
|
+
module WorkflowTemplate
|
|
6
|
+
module Validation
|
|
7
|
+
module Evaluation
|
|
8
|
+
class Canonical < Abstract
|
|
9
|
+
def evaluate(result)
|
|
10
|
+
case result
|
|
11
|
+
when Validator::Result::Success then Result::Success
|
|
12
|
+
when Adapter::Abstract::Result::Failure
|
|
13
|
+
to_failure(result)
|
|
14
|
+
else
|
|
15
|
+
raise Fatal::BadValidationReturn, name
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'failure/abstract'
|
|
4
|
+
require 'forwardable'
|
|
5
|
+
|
|
6
|
+
module WorkflowTemplate
|
|
7
|
+
module Validation
|
|
8
|
+
module Result
|
|
9
|
+
class Failure
|
|
10
|
+
include Abstract
|
|
11
|
+
extend Forwardable
|
|
12
|
+
|
|
13
|
+
attr_reader :name, :key, :failure
|
|
14
|
+
|
|
15
|
+
def_delegator :failure, :data
|
|
16
|
+
|
|
17
|
+
def self.instance(name, key, failure)
|
|
18
|
+
new name, key, failure
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(name, key, failure)
|
|
22
|
+
@name = name.to_sym
|
|
23
|
+
@key = key&.to_sym
|
|
24
|
+
@failure = failure
|
|
25
|
+
freeze
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def code
|
|
29
|
+
failure.code || :"#{name}_failed"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def describe
|
|
33
|
+
"#{code}: #{name} #{failure.describe}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def message(*args, **opts)
|
|
37
|
+
failure.message(*args, **opts)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def eql?(other)
|
|
41
|
+
return false unless other.is_a?(self.class)
|
|
42
|
+
|
|
43
|
+
name == other.name
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def hash
|
|
47
|
+
name.hash
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../adapter/abstract/result/failure'
|
|
4
|
+
|
|
5
|
+
module WorkflowTemplate
|
|
6
|
+
module Validation
|
|
7
|
+
module Validator
|
|
8
|
+
module Result
|
|
9
|
+
class Failure
|
|
10
|
+
include Adapter::Abstract::Result::Failure
|
|
11
|
+
|
|
12
|
+
attr_reader :validator, :code, :data
|
|
13
|
+
|
|
14
|
+
def initialize(validator, code: nil, data: nil)
|
|
15
|
+
@validator = validator
|
|
16
|
+
@code = code&.to_sym || :validation_failed
|
|
17
|
+
@data = data
|
|
18
|
+
freeze
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def success?
|
|
22
|
+
false
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def describe
|
|
26
|
+
validator.describe
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def message(*)
|
|
30
|
+
describe
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WorkflowTemplate
|
|
4
|
+
module Validation
|
|
5
|
+
module Validator
|
|
6
|
+
module Result
|
|
7
|
+
module Success
|
|
8
|
+
def self.success?
|
|
9
|
+
true
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.===(other)
|
|
13
|
+
other.equal?(self)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
freeze
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../error'
|
|
4
|
+
|
|
5
|
+
module WorkflowTemplate
|
|
6
|
+
class Workflow
|
|
7
|
+
class Description
|
|
8
|
+
def self.register(group, key, &block)
|
|
9
|
+
registry[group][key] = block
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.describe(workflow)
|
|
13
|
+
description = new
|
|
14
|
+
registry.each do |group, blocks|
|
|
15
|
+
blocks.each_value do |block|
|
|
16
|
+
result = block.call(workflow)
|
|
17
|
+
next if result.nil?
|
|
18
|
+
|
|
19
|
+
description.add!(result, group)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
description.format
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.registry
|
|
26
|
+
@registry ||= { initialize: {}, perform: {}, finalize: {} }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def initialize
|
|
30
|
+
@initialize = []
|
|
31
|
+
@perform = []
|
|
32
|
+
@finalize = []
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def add!(description, group)
|
|
36
|
+
case group
|
|
37
|
+
when :initialize then @initialize << description
|
|
38
|
+
when :perform then @perform << description
|
|
39
|
+
when :finalize then @finalize << description
|
|
40
|
+
else raise Error, "Unexpected group: #{group}"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def format
|
|
45
|
+
[@initialize, @perform, @finalize].filter_map do |group|
|
|
46
|
+
next if group.empty?
|
|
47
|
+
|
|
48
|
+
formatted = group.map do |element|
|
|
49
|
+
element.is_a?(String) ? element : element.format(level: 0)
|
|
50
|
+
end
|
|
51
|
+
formatted.join("\n")
|
|
52
|
+
end.join("\n\n") << "\n"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'definer/has_actions'
|
|
4
|
+
require_relative 'definer/has_actions/root'
|
|
5
|
+
require_relative 'definer/has_default_state_transition_strategy'
|
|
6
|
+
require_relative 'definer/has_output_normalizer'
|
|
7
|
+
require_relative 'definer/has_state_adapter'
|
|
8
|
+
require_relative 'definer/has_validations'
|
|
9
|
+
require_relative 'definer/has_validations/description'
|
|
10
|
+
require_relative 'outcome'
|
|
11
|
+
|
|
12
|
+
require_relative 'performer'
|
|
13
|
+
require_relative 'workflow/description'
|
|
14
|
+
|
|
15
|
+
module WorkflowTemplate
|
|
16
|
+
class Workflow
|
|
17
|
+
module ModuleMethods
|
|
18
|
+
include Definer::HasActions::Root
|
|
19
|
+
include Definer::HasDefaultStateTransitionStrategy
|
|
20
|
+
include Definer::HasOutputNormalizer
|
|
21
|
+
include Definer::HasStateAdapter
|
|
22
|
+
include Definer::HasValidations
|
|
23
|
+
include Performer
|
|
24
|
+
|
|
25
|
+
def perform(input, receiver, trace: false) # rubocop:disable Metrics/AbcSize
|
|
26
|
+
state_class = State.state_class(state_adapter)
|
|
27
|
+
state = state_class.initial(
|
|
28
|
+
input,
|
|
29
|
+
default_state_transition_strategy,
|
|
30
|
+
validations.input_validations,
|
|
31
|
+
trace: trace
|
|
32
|
+
)
|
|
33
|
+
state = super(state, receiver) if state.continue?
|
|
34
|
+
state = state.normalize_data(output_normalizer)
|
|
35
|
+
state = state.apply_validations(validations.output_validations)
|
|
36
|
+
Outcome.new(state.final)
|
|
37
|
+
rescue Fatal
|
|
38
|
+
raise
|
|
39
|
+
rescue StandardError => e
|
|
40
|
+
raise Fatal, "Unable to initialize state class: #{e}" unless state_class
|
|
41
|
+
raise Fatal, "Unable to initialize state: #{e}" unless state
|
|
42
|
+
|
|
43
|
+
raise Fatal, "Unexpected error: #{e}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def describe
|
|
47
|
+
raise Error, "Can't describe unfrozen workflow" unless frozen?
|
|
48
|
+
|
|
49
|
+
Description.describe(self)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
module ClassMethods
|
|
54
|
+
attr_reader :impl
|
|
55
|
+
|
|
56
|
+
def freeze
|
|
57
|
+
@impl = new.freeze
|
|
58
|
+
super
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
extend ModuleMethods
|
|
63
|
+
extend ClassMethods
|
|
64
|
+
|
|
65
|
+
def perform(inputs, trace: false)
|
|
66
|
+
self.class.perform(inputs, self, trace: trace)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|