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,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WorkflowTemplate
4
+ class Outcome
5
+ class Status
6
+ def initialize(handled: false, default: false)
7
+ @handled = handled
8
+ @default = default
9
+ freeze
10
+ end
11
+
12
+ def handled?
13
+ @handled
14
+ end
15
+
16
+ def handled!
17
+ self.class.new(handled: true, default: default?)
18
+ end
19
+
20
+ def default?
21
+ @default
22
+ end
23
+
24
+ def default!
25
+ self.class.new(handled: handled?, default: true)
26
+ end
27
+
28
+ def inspect
29
+ "handled=#{handled?} default=#{default?}"
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'status'
4
+ require_relative '../error'
5
+
6
+ module WorkflowTemplate
7
+ class Outcome
8
+ class Statuses
9
+ def initialize(ok: Status.new, error: Status.new)
10
+ @statuses = { ok: ok, error: error }.freeze
11
+ freeze
12
+ end
13
+
14
+ def status(status)
15
+ status = status.to_sym
16
+ raise Error, "Unsupported status: '#{status}'" unless @statuses.key? status
17
+
18
+ @statuses[status]
19
+ end
20
+
21
+ def unhandled?(status)
22
+ !status(status).handled?
23
+ end
24
+
25
+ def default_missing?
26
+ @statuses.none? { |_, status| status.default? }
27
+ end
28
+
29
+ def default_missing
30
+ @statuses.reject { |_, status| status.default? }.keys
31
+ end
32
+
33
+ alias [] status
34
+
35
+ def handled!(status)
36
+ updated = status(status).handled!
37
+ self.class.new(**@statuses, status => updated)
38
+ end
39
+
40
+ def on_default_detected(status, detected)
41
+ return self unless detected
42
+
43
+ default!(status)
44
+ end
45
+
46
+ def default!(status)
47
+ updated = status(status).default!
48
+ self.class.new(**@statuses, status => updated)
49
+ end
50
+
51
+ def all_defaults!
52
+ self.class.new(**@statuses.transform_values(&:default!))
53
+ end
54
+
55
+ def inspect
56
+ "#<#{self.class.name} #{@statuses.map { |name, status| "#{name}: #{status.inspect}" }}>"
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require_relative 'outcome/handler'
5
+ require_relative 'outcome/block'
6
+
7
+ module WorkflowTemplate
8
+ class Outcome
9
+ extend Forwardable
10
+
11
+ def initialize(state)
12
+ @handler = Handler.new(state)
13
+ end
14
+
15
+ def_delegator :handler, :retval
16
+ def_delegator :handler, :final_state
17
+
18
+ def_delegator :final_state, :meta
19
+ def_delegator :final_state, :status
20
+ def_delegator :final_state, :unwrap
21
+ def_delegator :final_state, :fetch
22
+ def_delegator :final_state, :slice
23
+ def_delegator :final_state, :to_result
24
+
25
+ def handle(&block)
26
+ binding = block.binding unless block.source_location.nil?
27
+ handler_block = Outcome::Block.new(handler, binding)
28
+
29
+ handler_block.instance_eval(&block)
30
+
31
+ self.handler = handler_block.handler
32
+ ensure_default_handled!
33
+
34
+ freeze
35
+
36
+ handler.retval
37
+ end
38
+
39
+ def data
40
+ final_state.bare_state
41
+ end
42
+
43
+ private
44
+
45
+ def ensure_default_handled!
46
+ return unless handler.default_missing?
47
+
48
+ raise Error, "Default handler missing for statuses: '#{handler.default_missing.join(', ')}'"
49
+ end
50
+
51
+ attr_accessor :handler
52
+ end
53
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'error'
4
+
5
+ module WorkflowTemplate
6
+ module Performer
7
+ def perform(state, receiver)
8
+ raise WorkflowTemplate::Fatal, 'Workflow is not frozen' unless frozen?
9
+
10
+ Performer.perform_wrapper_actions(state, actions, receiver)
11
+ end
12
+
13
+ def self.perform_wrapper_actions(state, actions, receiver)
14
+ if actions.wrapper.empty?
15
+ perform_actions(state, actions, receiver)
16
+ else
17
+ action, container = actions.next_wrapper_action
18
+ perform_wrapper_action(action, container, state, receiver)
19
+ end
20
+ end
21
+
22
+ def self.perform_wrapper_action(action, container, state, receiver)
23
+ inner_state = state
24
+ result = action.perform(state, receiver) do |yielded = {}|
25
+ inner_state = state.process_bare_result(yielded, action, trace: true, validate: false)
26
+ inner_state = perform_wrapper_actions(inner_state, container, receiver)
27
+ inner_state.wrapped_state
28
+ end
29
+ inner_state.process_wrapped_result(result, action, trace: false, validate: true)
30
+ end
31
+
32
+ def self.perform_actions(state, actions, receiver)
33
+ actions.simple.reduce(state) do |intermediate, action|
34
+ intermediate = perform_action(action, intermediate, receiver)
35
+
36
+ break intermediate unless intermediate.continue?
37
+
38
+ intermediate
39
+ end
40
+ end
41
+
42
+ def self.perform_action(action, state, *args)
43
+ action.perform(state, *args)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'meta'
4
+
5
+ module WorkflowTemplate
6
+ module State
7
+ module Abstract
8
+ attr_reader :wrapped_state, :meta
9
+
10
+ def initialize(wrapped_state, meta)
11
+ @wrapped_state = wrapped_state.freeze
12
+ @meta = meta.freeze
13
+ freeze
14
+ end
15
+
16
+ def bare_state
17
+ state_class.unwrap_success(wrapped_state)
18
+ end
19
+
20
+ def error?
21
+ state_class.error?(wrapped_state)
22
+ end
23
+
24
+ def error
25
+ state_class.unwrap_error(wrapped_state)
26
+ end
27
+
28
+ def state_class
29
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../error'
4
+
5
+ module WorkflowTemplate
6
+ module State
7
+ module Adapter
8
+ def self.adapters
9
+ @adapters ||= {}
10
+ end
11
+
12
+ def self.register(name, adapter)
13
+ adapters[name.to_sym] = adapter
14
+ end
15
+
16
+ def self.fetch(name)
17
+ name = name.to_sym
18
+ raise Error, "Not a registered state adapter: #{name}" unless adapters.key? name
19
+
20
+ adapters[name]
21
+ end
22
+
23
+ module Abstract
24
+ module InstanceMethods
25
+ end
26
+
27
+ module ClassMethods
28
+ def canonical?(_wrapped_state)
29
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
30
+ end
31
+
32
+ def error?(_wrapped_state)
33
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
34
+ end
35
+
36
+ def wrap_success(_bare_state)
37
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
38
+ end
39
+
40
+ def unwrap_success(_wrapped_state)
41
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
42
+ end
43
+
44
+ def wrap_error_with_bare_state(_error, _bare_state)
45
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
46
+ end
47
+
48
+ def unwrap_error(_wrapped_state)
49
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
50
+ end
51
+
52
+ def map_unwrappable_state(_wrapped_state)
53
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
54
+ end
55
+
56
+ def flat_map_unwrappable_state(_wrapped_state)
57
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
58
+ end
59
+
60
+ def run_handler!(state, binding, &block)
61
+ raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'validation'
4
+
5
+ module WorkflowTemplate
6
+ module State
7
+ module ClassMethods
8
+ def initial(input, default_strategy, validations, trace: false)
9
+ wrapped_state = wrap_success(input)
10
+ wrapped_state = validate(wrapped_state, validations)
11
+ new wrapped_state, State.normalize_transition_strategy(default_strategy), Meta.instance(trace)
12
+ end
13
+
14
+ def validate(wrapped_state, validations)
15
+ raise Fatal::BadInput, wrapped_state unless canonical? wrapped_state
16
+
17
+ return wrapped_state if !Validation.applicable?(validations) || error?(wrapped_state)
18
+
19
+ flat_map_unwrappable_state(wrapped_state) do |bare_state|
20
+ result = Validation.validate(bare_state, validations)
21
+ result.success? ? wrapped_state : wrap_error_with_bare_state(result, bare_state)
22
+ end
23
+ end
24
+
25
+ def merge_bare_result(bare_state, bare_result, strategy, action_name)
26
+ ensure_canonical_bare_result!(bare_result, action_name)
27
+
28
+ if strategy == :merge
29
+ bare_state.merge(bare_result)
30
+ else
31
+ bare_result
32
+ end
33
+ end
34
+
35
+ def ensure_canonical_bare_result!(bare_result, action_name)
36
+ raise Fatal::BadActionReturn.new(action_name, bare_result) unless bare_result.is_a?(Hash)
37
+
38
+ faulty_keys = bare_result.keys.reject { _1.is_a?(Symbol) }
39
+ raise Fatal::BadReturnKeys.new(action_name, faulty_keys) unless faulty_keys.empty?
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'abstract'
4
+ require_relative 'normalizer'
5
+ require_relative '../validation/result/failure'
6
+ require_relative '../validation/error'
7
+ require_relative '../error'
8
+
9
+ module WorkflowTemplate
10
+ module State
11
+ class Final
12
+ include Abstract
13
+
14
+ attr_reader :wrapped_state, :meta, :state_class
15
+
16
+ def initialize(wrapped_state, meta, state_class)
17
+ @state_class = state_class
18
+ super(wrapped_state, meta)
19
+ end
20
+
21
+ def status
22
+ error? ? :error : :ok
23
+ end
24
+
25
+ def unwrap(slice: nil, fetch: nil)
26
+ raise_error! if error?
27
+ return bare_state if slice.nil? && fetch.nil?
28
+
29
+ normalized_bare_state = Normalizer.instance(slice).normalize(bare_state)
30
+ fetch.nil? ? normalized_bare_state.values : normalized_bare_state.fetch(fetch, nil)
31
+ end
32
+
33
+ def fetch(key)
34
+ unwrap(fetch: key)
35
+ end
36
+
37
+ def slice(*keys)
38
+ unwrap(slice: keys)
39
+ end
40
+
41
+ def to_result(adapter = nil, **opts)
42
+ error? ? rewrap_error(adapter) : rewrap_success(adapter, **opts)
43
+ end
44
+
45
+ def raise_error!
46
+ error = state_class.unwrap_error(wrapped_state)
47
+
48
+ case error
49
+ when Exception then raise error
50
+ when WorkflowTemplate::Validation::Result::Failure then raise WorkflowTemplate::Validation::Error, error
51
+ else raise ExecutionError, error
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def rewrap_error(adapter)
58
+ return wrapped_state if adapter.nil? || target_class(adapter).canonical?(wrapped_state)
59
+
60
+ target_class(adapter).wrap_error_with_bare_state(state_class.unwrap_error(wrapped_state), {})
61
+ end
62
+
63
+ def rewrap_success(adapter, **opts)
64
+ target_class(adapter).wrap_success(unwrap(**opts))
65
+ end
66
+
67
+ def target_class(adapter)
68
+ return state_class if adapter.nil?
69
+
70
+ State.state_class(State::Adapter.fetch(adapter))
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'class_methods'
4
+ require_relative 'final'
5
+
6
+ module WorkflowTemplate
7
+ module State
8
+ module Intermediate
9
+ include Abstract
10
+
11
+ def initialize(wrapped_state, default_strategy, meta)
12
+ @default_strategy = default_strategy
13
+ super(wrapped_state, meta)
14
+ end
15
+
16
+ attr_reader :default_strategy
17
+
18
+ def continue?
19
+ !(error? || halted?)
20
+ end
21
+
22
+ def halted?
23
+ !!bare_state[:halted]
24
+ end
25
+
26
+ def final
27
+ Final.new(wrapped_state, meta, self.class)
28
+ end
29
+
30
+ def apply_validations(validations)
31
+ return self if error?
32
+
33
+ wrapped_state = self.class.validate(self.wrapped_state, validations)
34
+ self.class.send :new, wrapped_state, default_strategy, meta
35
+ end
36
+
37
+ def merge_error(error, action)
38
+ return wrapped_state if error?
39
+
40
+ wrapped_state = self.class.wrap_error_with_bare_state(error, {})
41
+ process_wrapped_result(wrapped_state, action, trace: true, validate: false)
42
+ end
43
+
44
+ def normalize_data(normalizer)
45
+ wrapped_state = normalize(normalizer)
46
+ self.class.send :new, wrapped_state, default_strategy, meta
47
+ end
48
+
49
+ def process_bare_result(bare_result, action, trace:, validate:)
50
+ process_result(action, trace, validate) do |strategy, action_name|
51
+ self.class.map_unwrappable_state(wrapped_state) do |bare_state|
52
+ self.class.merge_bare_result(bare_state, bare_result, strategy, action_name)
53
+ end
54
+ end
55
+ end
56
+
57
+ def process_wrapped_result(wrapped_result, action, trace:, validate:)
58
+ process_result(action, trace, validate) do |strategy, action_name|
59
+ if wrapped_result.nil?
60
+ raise Fatal::UnexpectedNil, action_name if strategy == :update
61
+
62
+ wrapped_state
63
+ else
64
+ raise Fatal::BadActionReturn.new(action_name, wrapped_result) unless self.class.canonical?(wrapped_result)
65
+
66
+ self.class.flat_map_unwrappable_state(wrapped_state) do |bare_state|
67
+ self.class.map_unwrappable_state(wrapped_result) do |bare_result|
68
+ self.class.merge_bare_result(bare_state, bare_result, strategy, action_name)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ def state_class
76
+ self.class
77
+ end
78
+
79
+ private
80
+
81
+ def process_result(action, trace, validate, &block)
82
+ meta = trace ? self.meta.add(action.name) : self.meta
83
+
84
+ strategy = action.state_transition_strategy || default_strategy
85
+ wrapped_result = block.call(strategy, action.name)
86
+ validations = action.validations if validate
87
+ wrapped_state = self.class.validate(wrapped_result, validations)
88
+
89
+ self.class.send :new, wrapped_state, default_strategy, meta
90
+ end
91
+
92
+ def normalize(normalizer)
93
+ self.class.map_unwrappable_state(wrapped_state) do |bare_state|
94
+ normalized = normalizer.normalize(bare_state)
95
+ next normalized unless error?
96
+
97
+ self.class.wrap_error_with_bare_state(self.class.unwrap_error(wrapped_state), normalized)
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WorkflowTemplate
4
+ module State
5
+ class Meta
6
+ module Null
7
+ def self.add(*)
8
+ self
9
+ end
10
+
11
+ def self.trace
12
+ nil
13
+ end
14
+
15
+ freeze
16
+ end
17
+
18
+ attr_reader :trace
19
+
20
+ def self.instance(trace)
21
+ trace ? new : Null
22
+ end
23
+
24
+ private_class_method :new
25
+
26
+ def initialize(trace: [])
27
+ @trace = trace.freeze
28
+ end
29
+
30
+ def add(name)
31
+ self.class.send :new, trace: [*@trace, name]
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WorkflowTemplate
4
+ module State
5
+ class Normalizer
6
+ attr_reader :keys
7
+
8
+ def initialize(keys)
9
+ @keys = keys&.map(&:to_sym).freeze
10
+ freeze
11
+ end
12
+
13
+ def normalize(hash)
14
+ return hash if keys.nil?
15
+
16
+ keys.each_with_object({}) do |key, normalized|
17
+ normalized[key] = hash[key]
18
+ end.freeze
19
+ end
20
+
21
+ private_class_method :new
22
+
23
+ def self.instance(keys = nil)
24
+ return null_object if keys.nil?
25
+
26
+ new keys
27
+ end
28
+
29
+ def self.null_object
30
+ @null_object ||= new(nil)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../validation/result/success'
4
+
5
+ module WorkflowTemplate
6
+ module State
7
+ module Validation
8
+ def self.validate(data, validations)
9
+ case validations
10
+ when Array then validate_multiple(data, validations)
11
+ else validations.call(data)
12
+ end
13
+ end
14
+
15
+ def self.validate_multiple(data, validations)
16
+ validations.reduce(WorkflowTemplate::Validation::Result::Success) do |result, validation|
17
+ break result unless result.success?
18
+
19
+ validation.call(data)
20
+ end
21
+ end
22
+
23
+ def self.applicable?(validations)
24
+ case validations
25
+ when nil then false
26
+ when Array then !validations.empty?
27
+ else true
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'state/intermediate'
4
+
5
+ module WorkflowTemplate
6
+ module State
7
+ STRATEGIES = %i[merge update].freeze
8
+
9
+ def self.normalize_transition_strategy(strategy)
10
+ strategy = strategy.to_sym
11
+ raise Error, "Unimplemented strategy: '#{strategy}'" unless STRATEGIES.include?(strategy)
12
+
13
+ strategy
14
+ end
15
+
16
+ def self.state_class(adapter)
17
+ state_classes[adapter] ||= Class.new do
18
+ include State::Intermediate
19
+ extend State::ClassMethods
20
+
21
+ raise Error, "Invalid adapter: #{adapter}" unless State.valid_adapter?(adapter)
22
+
23
+ include adapter::InstanceMethods
24
+ extend adapter::ClassMethods
25
+
26
+ private_class_method :new
27
+ end
28
+ end
29
+
30
+ def self.valid_adapter?(adapter)
31
+ adapter.constants.include?(:InstanceMethods) &&
32
+ adapter::InstanceMethods.included_modules.include?(Adapter::Abstract::InstanceMethods) &&
33
+ adapter.constants.include?(:ClassMethods) &&
34
+ adapter::ClassMethods.included_modules.include?(Adapter::Abstract::ClassMethods)
35
+ end
36
+
37
+ def self.state_classes
38
+ @state_classes ||= {}
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../builder/error'
4
+ require_relative '../../evaluation'
5
+
6
+ module WorkflowTemplate
7
+ module Validation
8
+ module Adapter
9
+ module Abstract
10
+ class Builder
11
+ Error = Validation::Builder::Error
12
+
13
+ def initialize(proxy)
14
+ @proxy = proxy
15
+ end
16
+
17
+ def validate
18
+ self
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :proxy
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end