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,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../validation/adapter/abstract/validator'
4
+ require_relative '../../../validation/validator/result/success'
5
+ require_relative 'failure'
6
+
7
+ module WorkflowTemplate
8
+ module Adapters
9
+ module Validation
10
+ module DryValidation
11
+ class Validator
12
+ extend WorkflowTemplate::Validation::Adapter::Abstract::Validator
13
+
14
+ def initialize(contract_class)
15
+ @contract_class = contract_class
16
+ end
17
+
18
+ def call(input, context: {})
19
+ return Failure.new(self, code: :input_null) if input.nil?
20
+
21
+ result = contract(context).call(input)
22
+ return WorkflowTemplate::Validation::Validator::Result::Success if result.success?
23
+
24
+ Failure.new(
25
+ self,
26
+ code: :input_invalid,
27
+ data: result
28
+ )
29
+ end
30
+
31
+ def describe
32
+ 'contract to be satisfied'
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :contract_class
38
+
39
+ def contract(context)
40
+ contract_class.new(**context)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../validation/adapter'
4
+ require_relative 'dry_validation/builder'
5
+
6
+ module WorkflowTemplate
7
+ module Adapters
8
+ module Validation
9
+ module DryValidation
10
+ extend WorkflowTemplate::Validation::Adapter
11
+
12
+ def self.builder_class
13
+ Builder
14
+ end
15
+
16
+ def self.result_type
17
+ :canonical
18
+ end
19
+
20
+ register(:dry_validation)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'validator'
4
+
5
+ module WorkflowTemplate
6
+ module Adapters
7
+ module Validation
8
+ module Generic
9
+ class Block < Validator
10
+ def initialize(block)
11
+ @block = block
12
+ super()
13
+ end
14
+
15
+ private
16
+
17
+ def call_foreign(object, **opts)
18
+ @block.call(object, **opts)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../validation/adapter/abstract/builder'
4
+ require_relative 'object'
5
+ require_relative 'block'
6
+
7
+ module WorkflowTemplate
8
+ module Adapters
9
+ module Validation
10
+ module Generic
11
+ class Builder < WorkflowTemplate::Validation::Adapter::Abstract::Builder
12
+ class Error < WorkflowTemplate::Error; end
13
+
14
+ def validate(&block)
15
+ return self unless block
16
+
17
+ proxy.declare(Generic::Block.new(block))
18
+ end
19
+
20
+ def using(implementation)
21
+ validator = Generic::Object.new(implementation)
22
+ proxy.declare(validator)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'validator'
4
+
5
+ module WorkflowTemplate
6
+ module Adapters
7
+ module Validation
8
+ module Generic
9
+ class Object < Validator
10
+ def initialize(object)
11
+ @object = object
12
+ super()
13
+ end
14
+
15
+ def describe
16
+ object.respond_to?(:describe) ? object.describe : super
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :object
22
+
23
+ def call_foreign(value, **opts)
24
+ object.call(value, **opts)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../validation/adapter/abstract/validator'
4
+
5
+ module WorkflowTemplate
6
+ module Adapters
7
+ module Validation
8
+ module Generic
9
+ class Validator
10
+ include WorkflowTemplate::Validation::Adapter::Abstract::Validator
11
+
12
+ def call(object, **opts)
13
+ call_foreign(object, **opts)
14
+ rescue StandardError => e
15
+ raise Fatal::ForeignCodeError, e
16
+ end
17
+
18
+ def describe
19
+ "#{self.class.name.split('::').last.downcase} validation"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../validation/adapter'
4
+ require_relative 'generic/builder'
5
+
6
+ module WorkflowTemplate
7
+ module Adapters
8
+ module Validation
9
+ module Generic
10
+ extend WorkflowTemplate::Validation::Adapter
11
+
12
+ def self.builder_class
13
+ Builder
14
+ end
15
+
16
+ def self.result_type
17
+ :boolean
18
+ end
19
+
20
+ register(:generic)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../error'
4
+
5
+ module WorkflowTemplate
6
+ module Definer
7
+ module HasActions
8
+ module Actions
9
+ class Unprepared
10
+ def self.instance
11
+ new
12
+ end
13
+
14
+ def initialize(simple: [], wrapper: [])
15
+ @unprepared_simple = simple.freeze
16
+ @unprepared_wrapper = wrapper.freeze
17
+ end
18
+
19
+ private_class_method :new
20
+
21
+ def add_simple(action, position)
22
+ @unprepared_simple = [*@unprepared_simple, [action, position]].freeze
23
+ end
24
+
25
+ def add_wrapper(action)
26
+ @unprepared_wrapper = [action, *@unprepared_wrapper].freeze
27
+ end
28
+
29
+ def prepared
30
+ simple = prepare_simple(existing: [])
31
+ wrapper = prepare_wrapper(existing: [])
32
+
33
+ Prepared.new(simple: simple, wrapper: wrapper)
34
+ end
35
+
36
+ protected
37
+
38
+ attr_reader :unprepared_simple, :unprepared_wrapper
39
+
40
+ def prepare_simple(existing:)
41
+ unprepared_simple.reduce(existing) do |existing, (action, position)|
42
+ position.apply_onto(existing, action)
43
+ end
44
+ end
45
+
46
+ def prepare_wrapper(existing:)
47
+ unprepared_wrapper + existing
48
+ end
49
+ end
50
+
51
+ class Prepared
52
+ attr_reader :simple, :wrapper
53
+
54
+ def initialize(simple:, wrapper:)
55
+ @simple = simple.freeze
56
+ @wrapper = wrapper.freeze
57
+ freeze
58
+ end
59
+
60
+ def merge(unprepared)
61
+ simple = unprepared.send(:prepare_simple, existing: self.simple)
62
+ wrapper = unprepared.send(:prepare_wrapper, existing: self.wrapper)
63
+
64
+ Prepared.new(simple: simple, wrapper: wrapper)
65
+ end
66
+
67
+ def map(&block)
68
+ simple_actions = simple.map(&block)
69
+ wrapper_actions = wrapper.map(&block)
70
+ self.class.new simple: simple_actions, wrapper: wrapper_actions
71
+ end
72
+
73
+ def next_wrapper_action
74
+ return [nil, self] if wrapper.empty?
75
+
76
+ action, *rest = wrapper
77
+ container = self.class.new(simple: simple, wrapper: rest)
78
+ [action, container]
79
+ end
80
+
81
+ def inspect
82
+ simple = simple().map(&:name).join(', ')
83
+ wrapper = wrapper().map(&:name).join(', ')
84
+ "#<#{self.class.name} simple=[#{simple}] wrapper=[#{wrapper}]>"
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../workflow/description'
4
+
5
+ module WorkflowTemplate
6
+ module Definer
7
+ module HasActions
8
+ class Description
9
+ Workflow::Description.register(:perform, :actions) do |workflow|
10
+ Description.new(workflow)
11
+ end
12
+
13
+ attr_reader :workflow
14
+
15
+ def initialize(workflow)
16
+ @workflow = workflow
17
+ freeze
18
+ end
19
+
20
+ def format(level:)
21
+ self.class.describe(workflow).flat_map { |action| action.format(level: level) }
22
+ end
23
+
24
+ def self.describe(workflow)
25
+ describe_wrapper_actions(workflow.actions.wrapper, workflow.actions.simple)
26
+ end
27
+
28
+ def self.describe_wrapper_actions(wrapper_actions, simple_actions)
29
+ action, *rest = wrapper_actions
30
+ if action.nil?
31
+ describe_actions(simple_actions)
32
+ else
33
+ [describe_wrapper_action(action, rest, simple_actions)]
34
+ end
35
+ end
36
+
37
+ def self.describe_wrapper_action(action, rest, simple_actions)
38
+ nested = describe_wrapper_actions(rest, simple_actions)
39
+ action.describe(nested)
40
+ end
41
+
42
+ def self.describe_actions(actions)
43
+ actions.map(&:describe)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../action'
4
+ require_relative 'insert'
5
+ require_relative 'inside'
6
+
7
+ module WorkflowTemplate
8
+ module Definer
9
+ module HasActions
10
+ module Dsl
11
+ def apply(action_name, **opts, &block)
12
+ insert_action(action_name, position: :after, **opts, &block)
13
+ end
14
+
15
+ alias and_then apply
16
+
17
+ def append_action(after: nil)
18
+ Insert.new(self, :after, after)
19
+ end
20
+
21
+ def prepend_action(before: nil)
22
+ Insert.new(self, :before, before)
23
+ end
24
+
25
+ def replace_action(name)
26
+ Insert.new(self, :at, name)
27
+ end
28
+
29
+ def inside_action(name, &block)
30
+ position = Inside.new(name, block)
31
+ store_action(nil, position)
32
+ end
33
+
34
+ def wrap_template(action_name, **opts)
35
+ action = Action.instance(action_name, :wrapper, **translate_action_parameters(**opts))
36
+ store_wrapper_action(action)
37
+ end
38
+
39
+ def remove_action(action_name)
40
+ position = Position.instance(:at, action_name)
41
+ store_action(nil, position)
42
+ end
43
+
44
+ protected
45
+
46
+ def insert_action(action_name, position: :after, reference: nil, **opts, &block)
47
+ action = if block.nil?
48
+ Action.instance(action_name, :simple, **translate_action_parameters(**opts), &block)
49
+ else
50
+ wrapper_action = Action.instance(action_name, :wrapper, **translate_action_parameters(**opts))
51
+ Action::Nested::Unprepared.instance(wrapper_action, Nested::Unprepared, &block)
52
+ end
53
+
54
+ position = Position.instance(position, reference)
55
+ store_action(action, position)
56
+ end
57
+
58
+ def translate_action_parameters(state: nil, validate: nil, **opts)
59
+ { state_transition_strategy: state, validates: validate, **opts }
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WorkflowTemplate
4
+ module Definer
5
+ module HasActions
6
+ class Insert
7
+ def initialize(workflow, position, reference)
8
+ @workflow = workflow
9
+ @position = position
10
+ @reference = reference
11
+ end
12
+
13
+ def apply(action_name, **opts, &block)
14
+ @workflow.send(
15
+ :insert_action,
16
+ action_name,
17
+ position: @position,
18
+ reference: @reference,
19
+ **opts,
20
+ &block
21
+ )
22
+ @workflow = nil
23
+ nil
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'position'
4
+
5
+ module WorkflowTemplate
6
+ module Definer
7
+ module HasActions
8
+ class Inside
9
+ attr_reader :reference, :block
10
+
11
+ def initialize(reference, block)
12
+ @reference = reference
13
+ @block = block
14
+ freeze
15
+ end
16
+
17
+ def apply_onto(actions, action)
18
+ raise Error, 'Action expected to be nil' unless action.nil?
19
+
20
+ before, action, after = Position.slice_actions(actions, reference)
21
+ clone = action.duplicate(&block)
22
+ [*before, clone, *after]
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'redefine'
4
+ require_relative 'description'
5
+ require_relative 'dsl'
6
+
7
+ module WorkflowTemplate
8
+ module Definer
9
+ module HasActions
10
+ module Nested
11
+ include HasActions
12
+
13
+ def describe
14
+ raise Error, "Can't describe unfrozen workflow" unless frozen?
15
+
16
+ Description.new(self)
17
+ end
18
+
19
+ module Unprepared
20
+ include Dsl
21
+ include Nested
22
+ include HasActions::Redefine
23
+
24
+ def prepare(*args)
25
+ unprepared = self
26
+
27
+ target = Module.new do
28
+ extend Prepared
29
+
30
+ define_singleton_method :unprepared do
31
+ unprepared
32
+ end
33
+ end
34
+
35
+ actions.wrapper.each do |action|
36
+ target.send :store_wrapper_action, action.prepare(*args)
37
+ end
38
+ actions.simple.each do |action|
39
+ position = Position.instance(:after, nil)
40
+ target.send :store_action, action.prepare(*args), position
41
+ end
42
+ target.freeze
43
+ end
44
+
45
+ def redefine(&block)
46
+ redefine_onto(Unprepared, &block)
47
+ end
48
+ end
49
+
50
+ module Prepared
51
+ include Nested
52
+ include Performer
53
+
54
+ def prepare(*args)
55
+ unprepared.prepare(*args)
56
+ end
57
+
58
+ def redefine(&block)
59
+ unprepared.redefine(&block)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../error'
4
+
5
+ module WorkflowTemplate
6
+ module Definer
7
+ module HasActions
8
+ class Position
9
+ def self.instance(type, reference)
10
+ case type
11
+ when :before
12
+ Position::Before.new(reference)
13
+ when :after
14
+ Position::After.new(reference)
15
+ when :at
16
+ raise Error, 'Reference not expected to be nil' if reference.nil?
17
+
18
+ Position::At.new(reference)
19
+ else
20
+ raise Error, "Unexpected position: #{type}"
21
+ end
22
+ end
23
+
24
+ def self.slice_actions(actions, reference)
25
+ index, *rest = actions.each_index.reduce([]) do |result, index|
26
+ next result unless actions[index].name == reference
27
+
28
+ [*result, index]
29
+ end
30
+ raise Error, "Reference not found: #{reference}" if index.nil?
31
+ raise Error, "Duplicate action name: #{reference}" if rest.length.positive?
32
+
33
+ before = actions[0...index]
34
+ action = actions[index]
35
+ after = actions[(index + 1)..]
36
+ [before, action, after]
37
+ end
38
+
39
+ def initialize(reference)
40
+ @reference = reference&.to_sym
41
+ freeze
42
+ end
43
+
44
+ def apply_onto(actions, action)
45
+ if reference.nil?
46
+ apply_without_reference(actions, action)
47
+ else
48
+ before, reference, after = self.class.slice_actions(actions, self.reference)
49
+ apply_with_reference(before, action, reference, after)
50
+ end
51
+ end
52
+
53
+ attr_reader :reference
54
+
55
+ class Before < Position
56
+ def apply_without_reference(actions, action)
57
+ [action, *actions]
58
+ end
59
+
60
+ def apply_with_reference(before, action, reference, after)
61
+ [*before, action, reference, *after]
62
+ end
63
+ end
64
+
65
+ class After < Position
66
+ def apply_without_reference(actions, action)
67
+ [*actions, action]
68
+ end
69
+
70
+ def apply_with_reference(before, action, reference, after)
71
+ [*before, reference, action, *after]
72
+ end
73
+ end
74
+
75
+ class At < Position
76
+ def apply_with_reference(before, action, _reference, after)
77
+ action.nil? ? [*before, *after] : [*before, action, *after]
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'position'
4
+
5
+ module WorkflowTemplate
6
+ module Definer
7
+ module HasActions
8
+ module Redefine
9
+ def redefine_onto(workflow_module, &block)
10
+ target = Module.new do
11
+ extend workflow_module
12
+ end
13
+
14
+ actions.wrapper.each do |action|
15
+ target.send :store_wrapper_action, action
16
+ end
17
+ actions.simple.each do |action|
18
+ position = Position.instance(:after, nil)
19
+ target.send :store_action, action, position
20
+ end
21
+ target.instance_eval(&block)
22
+
23
+ target.freeze
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'dsl'
4
+
5
+ module WorkflowTemplate
6
+ module Definer
7
+ module HasActions
8
+ module Root
9
+ include HasActions
10
+ include Dsl
11
+
12
+ def prepare_actions
13
+ actions.map { prepare_action(_1) }
14
+ end
15
+
16
+ def prepare_action(action)
17
+ action
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end