rails_workflow 0.3.9 → 0.4.0

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 (161) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +2 -0
  3. data/app/concerns/rails_workflow/operation_status.rb +2 -0
  4. data/app/concerns/rails_workflow/operation_templates/assignments.rb +3 -1
  5. data/app/concerns/rails_workflow/operation_templates/dependencies.rb +18 -18
  6. data/app/concerns/rails_workflow/operations/assignments.rb +7 -5
  7. data/app/concerns/rails_workflow/operations/dependencies.rb +2 -0
  8. data/app/concerns/rails_workflow/status.rb +11 -15
  9. data/app/concerns/rails_workflow/user/assignment.rb +2 -0
  10. data/app/concerns/rails_workflow/uuid.rb +2 -0
  11. data/app/controllers/rails_workflow/application_controller.rb +2 -0
  12. data/app/controllers/rails_workflow/errors_controller.rb +2 -0
  13. data/app/controllers/rails_workflow/operation_templates_controller.rb +3 -1
  14. data/app/controllers/rails_workflow/operations_controller.rb +3 -1
  15. data/app/controllers/rails_workflow/process_templates_controller.rb +10 -8
  16. data/app/controllers/rails_workflow/processes_controller.rb +10 -8
  17. data/app/decorators/rails_workflow/context_decorator.rb +2 -0
  18. data/app/decorators/rails_workflow/decorator.rb +2 -0
  19. data/app/decorators/rails_workflow/operation_decorator.rb +2 -0
  20. data/app/decorators/rails_workflow/operation_helper_decorator.rb +2 -0
  21. data/app/decorators/rails_workflow/operation_template_decorator.rb +2 -0
  22. data/app/decorators/rails_workflow/paginating_decorator.rb +2 -0
  23. data/app/decorators/rails_workflow/process_decorator.rb +2 -0
  24. data/app/decorators/rails_workflow/process_template_decorator.rb +2 -0
  25. data/app/decorators/rails_workflow/status_decorator.rb +2 -0
  26. data/app/helpers/rails_workflow/application_helper.rb +2 -0
  27. data/app/jobs/rails_workflow/operation_execution_job.rb +11 -1
  28. data/app/models/rails_workflow/context.rb +3 -1
  29. data/app/models/rails_workflow/error.rb +23 -50
  30. data/app/models/rails_workflow/operation.rb +39 -3
  31. data/app/models/rails_workflow/operation_template.rb +2 -2
  32. data/app/models/rails_workflow/process.rb +34 -5
  33. data/app/models/rails_workflow/process_template.rb +12 -8
  34. data/app/models/rails_workflow/user_by_group_operation.rb +2 -0
  35. data/app/models/rails_workflow/user_by_role_operation.rb +2 -0
  36. data/app/models/rails_workflow/user_operation.rb +2 -0
  37. data/app/serializers/rails_workflow/operation_template_serializer.rb +2 -0
  38. data/app/serializers/rails_workflow/process_template_serializer.rb +2 -0
  39. data/app/services/rails_workflow/default_importer_preprocessor.rb +2 -0
  40. data/app/services/rails_workflow/process_importer.rb +2 -0
  41. data/app/views/rails_workflow/processes/index.html.slim +5 -5
  42. data/config/initializers/rails_workflow.rb +3 -3
  43. data/config/routes.rb +4 -2
  44. data/db/migrate/20150630174700_create_workflow_processes.rb +86 -84
  45. data/lib/generators/rails_workflow/install/install_generator.rb +2 -0
  46. data/lib/generators/rails_workflow/install/templates/create_workflow_processes.rb +86 -84
  47. data/lib/rails_workflow/config.rb +65 -24
  48. data/lib/rails_workflow/db/mysql.rb +3 -1
  49. data/lib/rails_workflow/db/pg.rb +3 -1
  50. data/lib/rails_workflow/dependency_resolver.rb +74 -0
  51. data/lib/rails_workflow/engine.rb +3 -1
  52. data/lib/rails_workflow/error_builder.rb +57 -0
  53. data/lib/rails_workflow/error_resolver.rb +72 -0
  54. data/lib/rails_workflow/operation_builder.rb +95 -0
  55. data/lib/rails_workflow/operation_runner.rb +113 -0
  56. data/lib/rails_workflow/process_builder.rb +57 -0
  57. data/lib/rails_workflow/process_manager.rb +66 -0
  58. data/lib/rails_workflow/process_runner.rb +72 -0
  59. data/lib/rails_workflow/version.rb +3 -1
  60. data/lib/rails_workflow.rb +2 -0
  61. data/lib/tasks/rails_workflow_tasks.rake +1 -0
  62. data/spec/concerns/status_spec.rb +25 -0
  63. data/spec/controllers/rails_workflow/operation_templates_controller_spec.rb +2 -0
  64. data/spec/controllers/rails_workflow/operations_controller_spec.rb +2 -0
  65. data/spec/controllers/rails_workflow/process_templates_controller_spec.rb +2 -0
  66. data/spec/controllers/rails_workflow/processes_controller_spec.rb +3 -1
  67. data/spec/dummy/Rakefile +2 -0
  68. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  69. data/spec/dummy/app/controllers/leads_controller.rb +2 -0
  70. data/spec/dummy/app/controllers/sales_contacts_controller.rb +2 -0
  71. data/spec/dummy/app/decorators/lead_decorator.rb +2 -0
  72. data/spec/dummy/app/decorators/sales_contact_decorator.rb +2 -0
  73. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  74. data/spec/dummy/app/helpers/leads_helper.rb +2 -0
  75. data/spec/dummy/app/helpers/sales_contacts_helper.rb +2 -0
  76. data/spec/dummy/app/models/bad_operation.rb +2 -0
  77. data/spec/dummy/app/models/bad_operation_template.rb +2 -0
  78. data/spec/dummy/app/models/lead.rb +2 -0
  79. data/spec/dummy/app/models/sales_contact.rb +2 -0
  80. data/spec/dummy/app/models/user.rb +2 -0
  81. data/spec/dummy/bin/bundle +2 -0
  82. data/spec/dummy/bin/rails +2 -0
  83. data/spec/dummy/bin/rake +2 -0
  84. data/spec/dummy/config/application.rb +2 -0
  85. data/spec/dummy/config/boot.rb +2 -0
  86. data/spec/dummy/config/database.yml.semaphore +2 -2
  87. data/spec/dummy/config/environment.rb +2 -0
  88. data/spec/dummy/config/environments/development.rb +2 -0
  89. data/spec/dummy/config/environments/production.rb +2 -0
  90. data/spec/dummy/config/environments/test.rb +2 -0
  91. data/spec/dummy/config/initializers/assets.rb +2 -0
  92. data/spec/dummy/config/initializers/backtrace_silencers.rb +1 -0
  93. data/spec/dummy/config/initializers/cookies_serializer.rb +2 -0
  94. data/spec/dummy/config/initializers/devise.rb +2 -0
  95. data/spec/dummy/config/initializers/filter_parameter_logging.rb +2 -0
  96. data/spec/dummy/config/initializers/inflections.rb +1 -0
  97. data/spec/dummy/config/initializers/mime_types.rb +1 -0
  98. data/spec/dummy/config/initializers/session_store.rb +2 -0
  99. data/spec/dummy/config/initializers/workflow.rb +2 -0
  100. data/spec/dummy/config/initializers/wrap_parameters.rb +2 -0
  101. data/spec/dummy/config/routes.rb +2 -0
  102. data/spec/dummy/config.ru +2 -0
  103. data/spec/dummy/db/migrate/20150127171613_devise_create_users.rb +2 -0
  104. data/spec/dummy/db/migrate/20150130042852_create_sales_contacts.rb +2 -0
  105. data/spec/dummy/db/migrate/20150130043008_create_leads.rb +2 -0
  106. data/spec/dummy/log/development.log +110 -0
  107. data/spec/dummy/log/test.log +0 -0
  108. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/7O/7O5OddMZvWFGpj71BCdc0vA55rhj4YPoL0XjBV4_OYk.cache +1 -0
  109. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Cy/CyixQwp8QvorgpRadtYf6_zY35nZZp0JmrhkDN16XPM.cache +1 -0
  110. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/FW/FWR5Q7EP17cTcY35R1MscF6MX2HYiH5I2ZII89YLYxc.cache +1 -0
  111. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/HQ/HQuQO1k4kOyLSDXJEc8p-XR2agdkPF7mIFyh2noiNrU.cache +1 -0
  112. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/N4/N4e3G5QNTts2OVqkL0kxoP1xnAdDWnumpnH1m6WAsuY.cache +0 -0
  113. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Rs/RsIG_ias53xeb4WHWRodcMcDmVyz1QvLQNy4gV2iBSo.cache +1 -0
  114. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/WI/WI2XYMfwCaPWcjvvouKVltl9oKqKZL-BHePPLhC-rzw.cache +1 -0
  115. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Yc/YcbQRZWzddLfawePxNTEXcFFiS7asAyrujZzciT99l0.cache +1 -0
  116. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ai/aihcJWCIJGiHDpamul8bzy4jMsxnhewX6XWtkdsQ9dc.cache +1 -0
  117. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/fR/fRVQN4mU7z3MVJD9u1LMq8idpOH8o2tFh_YouMYGAM8.cache +1 -0
  118. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/gU/gUFNMb7SgxFyz5ZZcGfp-VYRVIw711HUeiWonMzYrcU.cache +1 -0
  119. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/h8/h86FNb4MxO5dq40P8KcCgGwZ_uqd8nn_O2fMWGDApW8.cache +1 -0
  120. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/q7/q7cZDJXZFXTklMF8JAQSMr-MBxOKc7q1vZAPPR5RBf4.cache +2 -0
  121. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/s9/s98GbJDqEe4PG3moRH6h-ukIZmQz9Frq4LO_Z9vCBLM.cache +1 -0
  122. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/xX/xXCd205lXu7u_BuyBL0kfZwkBGJLvocHDItBfU6tMm4.cache +2 -0
  123. data/spec/factories/context.rb +2 -0
  124. data/spec/factories/operation_templates.rb +2 -0
  125. data/spec/factories/operations.rb +3 -1
  126. data/spec/factories/process_templates.rb +2 -0
  127. data/spec/factories/processes.rb +3 -1
  128. data/spec/factories/user.rb +2 -0
  129. data/spec/factories/workflow_errors.rb +2 -0
  130. data/spec/features/operations_spec.rb +2 -0
  131. data/spec/features/process_template_spec.rb +2 -0
  132. data/spec/lib/error_builder_spec.rb +137 -0
  133. data/spec/lib/error_resolver_spec.rb +182 -0
  134. data/spec/lib/operation_builder_spec.rb +69 -0
  135. data/spec/lib/process_builder_spec.rb +19 -0
  136. data/spec/{core/rails_workflow → lib}/process_manager_spec.rb +17 -27
  137. data/spec/models/rails_workflow/context_spec.rb +6 -3
  138. data/spec/models/rails_workflow/error_spec.rb +20 -147
  139. data/spec/models/rails_workflow/operation_spec.rb +64 -97
  140. data/spec/models/rails_workflow/operation_template_spec.rb +24 -11
  141. data/spec/models/rails_workflow/process_spec.rb +5 -2
  142. data/spec/models/rails_workflow/process_template_spec.rb +2 -36
  143. data/spec/rails_helper.rb +2 -0
  144. data/spec/serializers/process_template_serializer_spec.rb +2 -0
  145. data/spec/services/process_importer_spec.rb +2 -0
  146. data/spec/spec_helper.rb +2 -0
  147. data/spec/support/controller_macros.rb +2 -0
  148. data/spec/support/pages/operations.rb +2 -0
  149. data/spec/support/rails_workflow/custom_operation.rb +2 -0
  150. data/spec/support/rails_workflow/custom_operation_template.rb +2 -0
  151. data/spec/{core → support}/rails_workflow/prepare_template.rb +3 -1
  152. data/spec/support/workflow_helper.rb +2 -0
  153. metadata +54 -28
  154. data/app/concerns/rails_workflow/operation_templates/default_builder.rb +0 -56
  155. data/app/concerns/rails_workflow/operations/default_runner.rb +0 -121
  156. data/app/concerns/rails_workflow/process_templates/default_builder.rb +0 -52
  157. data/app/concerns/rails_workflow/processes/default_runner.rb +0 -64
  158. data/app/concerns/rails_workflow/processes/dependency_resolver.rb +0 -54
  159. data/app/concerns/rails_workflow/reset_cache.rb +0 -13
  160. data/app/jobs/rails_workflow/operation_error_job.rb +0 -40
  161. data/app/managers/rails_workflow/process_manager.rb +0 -40
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailsWorkflow
2
4
  class Engine < ::Rails::Engine
3
5
  isolate_namespace RailsWorkflow
@@ -17,7 +19,7 @@ module RailsWorkflow
17
19
  end
18
20
 
19
21
  initializer 'rails_workflow.assets.precompile' do |app|
20
- app.config.assets.precompile += %w(application.css application.js)
22
+ app.config.assets.precompile += %w[application.css application.js]
21
23
  end
22
24
  end
23
25
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsWorkflow
4
+ # Default error builder. Can be changed in configuration.
5
+ # Manages errors building
6
+ class ErrorBuilder
7
+ attr_accessor :exception, :context
8
+
9
+ def self.handle(exception, context)
10
+ new(exception, context).handle
11
+ end
12
+
13
+ def initialize(exception, context)
14
+ @exception = exception
15
+ @context = context
16
+ end
17
+
18
+ def handle
19
+ create_error(context)
20
+ process_parent(target)
21
+ end
22
+
23
+ private
24
+
25
+ def create_error(context)
26
+ error = RailsWorkflow::Error.create(
27
+ parent: target,
28
+ message: exception.message.first(250),
29
+ stack_trace: exception.backtrace.join("<br/>\n")
30
+ )
31
+
32
+ error.create_context(data: context)
33
+ end
34
+
35
+ # Changing custom process or operation classes to default classes.
36
+ # If we store error with a custom class and somebody will delete
37
+ # or rename this class - we will not be able to load error.
38
+ def target
39
+ @target ||= begin
40
+ parent = context[:parent]
41
+ if parent.is_a? RailsWorkflow::Operation
42
+ parent.becomes(RailsWorkflow::Operation)
43
+ elsif parent.is_a? RailsWorkflow::Process
44
+ parent.becomes(RailsWorkflow::Process)
45
+ end
46
+ end
47
+ end
48
+
49
+ def process_parent(subject)
50
+ return if subject.nil?
51
+
52
+ subject.status = Status::ERROR
53
+ subject.save
54
+ process_parent(subject.parent) if subject.parent.present?
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsWorkflow
4
+ # Default error resolver. Can be changed in configuration.
5
+ # Manages errors processing
6
+ class ErrorResolver
7
+ attr_accessor :error
8
+ delegate :update_attribute, :target, :operation, :process,
9
+ :data, :can_restart_process?, to: :error
10
+
11
+ def self.retry(error)
12
+ new(error).retry
13
+ end
14
+
15
+ def initialize(error)
16
+ @error = error
17
+ end
18
+
19
+ def retry
20
+ update_attribute(:resolved, true)
21
+ fix_status(error.parent)
22
+ prepared_target.send(data[:method], *data[:args])
23
+ try_restart_process unless target == 'process_manager'
24
+ end
25
+
26
+ def fix_status(subject)
27
+ subject.status = Status::IN_PROGRESS
28
+ subject.save
29
+ fix_status(subject.parent) if subject.parent.present?
30
+ end
31
+
32
+ def prepared_target
33
+ return operation_runner if target == 'operation_runner'
34
+ return operation_builder if target == 'operation_builder'
35
+ return dependency_resolver if target == 'dependency_resolver'
36
+ return process_manager if target == 'process_manager'
37
+ target
38
+ end
39
+
40
+ def try_restart_process
41
+ return unless process.present?
42
+ process.update_attribute(:status, Status::IN_PROGRESS)
43
+
44
+ process.reload
45
+ process_runner.start if can_restart_process?
46
+ end
47
+
48
+ def config
49
+ RailsWorkflow.config
50
+ end
51
+
52
+ def process_manager
53
+ config.process_manager.new(process)
54
+ end
55
+
56
+ def operation_runner
57
+ config.operation_runner.new(operation)
58
+ end
59
+
60
+ def operation_builder
61
+ config.operation_builder.new(*data[:args]).tap { data[:args] = nil }
62
+ end
63
+
64
+ def dependency_resolver
65
+ config.dependency_resolver.new(process)
66
+ end
67
+
68
+ def process_runner
69
+ config.process_runner.new(process)
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsWorkflow
4
+ # Operation builder which creates new operaitons including
5
+ # their context, child processes etc... s
6
+ class OperationBuilder
7
+ attr_accessor :process, :template, :completed_dependencies
8
+ delegate :attributes, :operation_class, :child_process, to: :template
9
+
10
+ def initialize(process, template, completed_dependencies = [])
11
+ @process = process
12
+ @template = template
13
+ @completed_dependencies = completed_dependencies
14
+ end
15
+
16
+ def create_operation
17
+ create_operation!.tap { |operation| process.operations << operation }
18
+ rescue => exception
19
+ handle_exception(exception)
20
+ end
21
+
22
+ def create_operation!
23
+ operation = operation_class.create(prepared_attributes) do |op|
24
+ op.context = build_context(op, completed_dependencies)
25
+ # Can add OperationTemplate#after_operation_create callback
26
+ template.after_operation_create(op) if template.respond_to?(:after_operation_create)
27
+ end
28
+
29
+ build_child_process(operation)
30
+ operation
31
+ end
32
+
33
+ def prepared_dependencies
34
+ completed_dependencies.map do |dep|
35
+ {
36
+ operation_id: dep.id,
37
+ status: dep.status
38
+ }
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def prepared_attributes
45
+ attributes.with_indifferent_access
46
+ .slice(:title, :async, :is_background)
47
+ .merge(template: template,
48
+ process: process,
49
+ status: Operation::NOT_STARTED,
50
+ manager: process.manager,
51
+ dependencies: prepared_dependencies)
52
+ end
53
+
54
+ def build_child_process(operation)
55
+ return unless child_process.present?
56
+
57
+ # TODO: replace with Process Builder or replace with
58
+ # config process manager
59
+ operation.child_process = RailsWorkflow::ProcessManager
60
+ .create_process(
61
+ child_process.id,
62
+ operation.context.data
63
+ )
64
+ end
65
+
66
+ def prepare_context_data(dependencies)
67
+ dependencies.first.try(:context).try(:data)
68
+ end
69
+
70
+ # TODO: move to ContextBuilder
71
+ def build_context(operation, dependencies)
72
+ RailsWorkflow::Context.new(
73
+ parent: operation,
74
+ data: prepare_context_data(dependencies) || operation.process.data
75
+ )
76
+ end
77
+
78
+ def handle_exception(exception)
79
+ error_builder.handle(
80
+ exception,
81
+ parent: process, target: :operation_builder, method: :create_operation,
82
+ args: [process, template, completed_dependencies]
83
+ )
84
+ end
85
+
86
+ def error_builder
87
+ config.error_builder
88
+ end
89
+
90
+ # TODO: move config
91
+ def config
92
+ RailsWorkflow.config
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsWorkflow
4
+ # Workflow::OperationRunner responsible for operation execution
5
+ class OperationRunner
6
+ attr_accessor :operation
7
+ delegate :can_start?, :completed?, :completable?, :update_attribute,
8
+ :update_attributes, :is_background, :child_process, :context,
9
+ to: :operation
10
+
11
+ def self.start(operations)
12
+ operations.each do |operation|
13
+ new(operation).start
14
+ end
15
+ end
16
+
17
+ def initialize(operation)
18
+ @operation = operation
19
+ end
20
+
21
+ def start
22
+ can_start? ? starting : waiting
23
+ rescue => exception
24
+ error_builder.handle(
25
+ exception,
26
+ parent: operation, target: :operation_runner, method: :start
27
+ )
28
+ end
29
+
30
+ def starting
31
+ update_attribute(:status, Status::IN_PROGRESS)
32
+
33
+ if is_background && config.activejob_enabled
34
+ OperationExecutionJob.perform_later(operation.id)
35
+ else
36
+ OperationExecutionJob.perform_now(operation.id)
37
+ end
38
+ end
39
+
40
+ def waiting
41
+ update_attribute(:status, Status::WAITING)
42
+ start_waiting if respond_to? :start_waiting
43
+ rescue => exception
44
+ error_builder.handle(
45
+ exception,
46
+ parent: operation,
47
+ target: :operation_runner,
48
+ method: :waiting
49
+ )
50
+ end
51
+
52
+ def execute_in_transaction
53
+ with_transaction do
54
+ child_process_runner.start if child_process.present?
55
+ operation.execute if operation.respond_to?(:execute)
56
+ complete
57
+ end
58
+ rescue => exception
59
+ handle_exception(exception)
60
+ end
61
+
62
+ def complete(to_status = Status::DONE)
63
+ return unless completable?
64
+
65
+ context&.save
66
+ update_attributes(
67
+ status: to_status,
68
+ completed_at: Time.zone.now
69
+ )
70
+ process_runner.operation_completed(operation)
71
+ end
72
+
73
+ def cancel
74
+ complete Status::CANCELED
75
+ end
76
+
77
+ def skip
78
+ complete Status::SKIPPED
79
+ end
80
+
81
+ private
82
+
83
+ def error_builder
84
+ config.error_builder
85
+ end
86
+
87
+ def handle_exception(exception)
88
+ error_builder.handle(
89
+ exception,
90
+ parent: operation, target: :operation_runner,
91
+ method: :execute_in_transaction
92
+ )
93
+ end
94
+
95
+ def with_transaction
96
+ operation.class.transaction(requires_new: true) do
97
+ yield
98
+ end
99
+ end
100
+
101
+ def child_process_runner
102
+ @child_process_runner ||= config.process_runner.new(child_process)
103
+ end
104
+
105
+ def process_runner
106
+ @process_runner ||= config.process_runner.new(operation.process)
107
+ end
108
+
109
+ def config
110
+ RailsWorkflow.config
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsWorkflow
4
+ # = DefaultBuilder
5
+ #
6
+ # Process Builder is used to build new process. All process building logic
7
+ # should be gathered here. It defines how process is build using template
8
+ # (for example it can used to gather some additional information from
9
+ # system - for example some information from existing processes or it
10
+ # can handle hierarchical processes logic for parent / child processes).
11
+ class ProcessBuilder
12
+ attr_accessor :template, :context
13
+
14
+ delegate :process_class, :title, :independent_operations, to: :template
15
+
16
+ def initialize(template, context)
17
+ @template = template
18
+ @context = context
19
+ end
20
+
21
+ def create_process!
22
+ process = process_class.create(template: template)
23
+
24
+ process.update_attributes(title: title, status: Process::NOT_STARTED)
25
+ process.create_context(data: context, parent: process)
26
+
27
+ build_independent_operations process
28
+ process
29
+ end
30
+
31
+ # Independent operations is template operations that have no
32
+ # dependencies from any other operations
33
+ def build_independent_operations(process)
34
+ independent_operations.each do |operation_template|
35
+ build_operation process, operation_template
36
+ end
37
+ end
38
+
39
+ def build_operation(process, template, completed_dependencies = [])
40
+ operation_builder.new(
41
+ process, template, completed_dependencies
42
+ ).create_operation
43
+ end
44
+
45
+ def error_builder
46
+ config.error_builder
47
+ end
48
+
49
+ def config
50
+ RailsWorkflow.config
51
+ end
52
+
53
+ def operation_builder
54
+ config.operation_builder
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsWorkflow
4
+ # ProcessManager should be used to build and start processes.
5
+ # It is top level hierarchy class that also can be used
6
+ # to build enhancements. For example they can be used to implement
7
+ # processes communications.
8
+ class ProcessManager
9
+ attr_accessor :process, :template, :context
10
+ # delegate :template, :operation_exception, to: :process
11
+ delegate :complete, to: :process, prefix: true
12
+
13
+ class << self
14
+ def create_process(template_id, context)
15
+ new(template_id: template_id, context: context).create_process
16
+ end
17
+ end
18
+
19
+ def initialize(process = nil, template_id: nil, context: nil)
20
+ @process = process
21
+ @template = ProcessTemplate.find(template_id) if template_id
22
+ @context = context
23
+ end
24
+
25
+ def create_process
26
+ self.process = process_builder.new(template, context).create_process!
27
+ end
28
+
29
+ def self.start_process(template_id, context)
30
+ process = create_process template_id, context
31
+ new(process).start_process
32
+ process
33
+ end
34
+
35
+ def start_process
36
+ process_runner.start
37
+ rescue => exception
38
+ error_builder.handle(
39
+ exception,
40
+ parent: process,
41
+ target: :process_manager,
42
+ method: :start_process
43
+ )
44
+ end
45
+
46
+ def complete_process
47
+ process_runner.complete
48
+ end
49
+
50
+ def error_builder
51
+ config.error_builder
52
+ end
53
+
54
+ def process_builder
55
+ config.process_builder
56
+ end
57
+
58
+ def process_runner
59
+ @process_runner ||= config.process_runner.new(process)
60
+ end
61
+
62
+ def config
63
+ RailsWorkflow.config
64
+ end
65
+ end
66
+ end