flow_core 0.0.3 → 0.0.5
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 +4 -4
- data/README.md +7 -0
- data/app/models/flow_core/arc.rb +8 -0
- data/app/models/flow_core/arc_guard.rb +27 -2
- data/app/models/flow_core/branch.rb +48 -0
- data/app/models/flow_core/instance.rb +10 -1
- data/app/models/flow_core/pipeline.rb +58 -0
- data/app/models/flow_core/place.rb +2 -1
- data/app/models/flow_core/step.rb +337 -0
- data/app/models/flow_core/steps/end.rb +32 -0
- data/app/models/flow_core/steps/exclusive_choice.rb +72 -0
- data/app/models/flow_core/steps/parallel_split.rb +42 -0
- data/app/models/flow_core/steps/redirection.rb +39 -0
- data/app/models/flow_core/steps/task.rb +25 -0
- data/app/models/flow_core/task.rb +21 -8
- data/app/models/flow_core/transition.rb +44 -6
- data/app/models/flow_core/transition_callback.rb +19 -2
- data/app/models/flow_core/transition_trigger.rb +19 -2
- data/app/models/flow_core/workflow.rb +11 -4
- data/db/migrate/{20200130200532_create_flow_core_tables.rb → 20200130200532_create_workflow_tables.rb} +32 -29
- data/db/migrate/20200130200533_create_pipeline_tables.rb +64 -0
- data/lib/flow_core.rb +3 -4
- data/lib/flow_core/definition/net.rb +6 -4
- data/lib/flow_core/definition/transition.rb +37 -25
- data/lib/flow_core/engine.rb +5 -0
- data/lib/flow_core/locale/en.yml +9 -4
- data/lib/flow_core/task_executable.rb +4 -0
- data/lib/flow_core/version.rb +1 -1
- data/lib/flow_core/workflow_callbacks.rb +2 -0
- metadata +41 -4
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FlowCore::Steps
|
4
|
+
class End < FlowCore::Step
|
5
|
+
def deploy_to_workflow!(workflow, input_place_or_transition)
|
6
|
+
target_place = workflow.end_place || workflow.create_end_place!
|
7
|
+
|
8
|
+
if input_place_or_transition.is_a? FlowCore::Transition
|
9
|
+
target_place.input_transitions << input_place_or_transition
|
10
|
+
else
|
11
|
+
input_place_or_transition.input_arcs.update place: target_place
|
12
|
+
input_place_or_transition.reload.destroy!
|
13
|
+
end
|
14
|
+
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def creatable?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def redirection_step?
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
def redirection_configurable?
|
28
|
+
false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FlowCore::Steps
|
4
|
+
class ExclusiveChoice < FlowCore::Step
|
5
|
+
after_create :auto_create_fallback_branch
|
6
|
+
|
7
|
+
def deploy_to_workflow!(workflow, input_transition)
|
8
|
+
return input_transition if branches.empty?
|
9
|
+
|
10
|
+
input_place = find_or_create_input_place(workflow, input_transition)
|
11
|
+
exclusive_choice_transition =
|
12
|
+
if transition_trigger
|
13
|
+
t = input_place.output_transitions.create! workflow: workflow,
|
14
|
+
name: name,
|
15
|
+
output_token_create_strategy: :match_one_or_fallback,
|
16
|
+
generated_by_step_id: id
|
17
|
+
copy_transition_trigger_to t
|
18
|
+
copy_transition_callbacks_to t
|
19
|
+
t
|
20
|
+
else
|
21
|
+
input_place.output_transitions.create! workflow: workflow,
|
22
|
+
name: name,
|
23
|
+
output_token_create_strategy: :match_one_or_fallback,
|
24
|
+
auto_finish_strategy: "synchronously",
|
25
|
+
generated_by_step_id: id
|
26
|
+
end
|
27
|
+
|
28
|
+
simple_merge_place = workflow.places.create! workflow: workflow
|
29
|
+
|
30
|
+
deploy_branches_to(workflow, exclusive_choice_transition, simple_merge_place)
|
31
|
+
|
32
|
+
simple_merge_place
|
33
|
+
end
|
34
|
+
|
35
|
+
def transition_trigger_attachable?
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
39
|
+
def branch_arc_guard_attachable?
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
def fallback_branch_required?
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
class << self
|
48
|
+
def creatable?
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def multi_branch_step?
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def transition_callback_attachable?
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
def branch_configurable?
|
61
|
+
true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def auto_create_fallback_branch
|
68
|
+
branches.create! name: I18n.t("flow_core.pipeline.fallback_branch_name"),
|
69
|
+
fallback_branch: true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FlowCore::Steps
|
4
|
+
class ParallelSplit < FlowCore::Step
|
5
|
+
def deploy_to_workflow!(workflow, input_transition)
|
6
|
+
return input_transition if branches.empty?
|
7
|
+
|
8
|
+
input_place = find_or_create_input_place(workflow, input_transition)
|
9
|
+
parallel_split_transition =
|
10
|
+
input_place.output_transitions.create! workflow: workflow,
|
11
|
+
name: name,
|
12
|
+
auto_finish_strategy: "synchronously",
|
13
|
+
generated_by_step_id: id
|
14
|
+
synchronization_transition =
|
15
|
+
workflow.transitions.create! workflow: workflow,
|
16
|
+
name: I18n.t("flow_core.pipeline.synchronization_transition_name"),
|
17
|
+
auto_finish_strategy: "synchronously"
|
18
|
+
|
19
|
+
deploy_branches_to(workflow, parallel_split_transition, synchronization_transition)
|
20
|
+
|
21
|
+
synchronization_transition
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def creatable?
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
def multi_branch_step?
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def branch_configurable?
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
def barrier_step?
|
38
|
+
true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FlowCore::Steps
|
4
|
+
class Redirection < FlowCore::Step
|
5
|
+
def deploy_to_workflow!(workflow, input_place_or_transition)
|
6
|
+
target_transition = workflow.transitions.find_by! generated_by_step_id: redirect_to_step_id
|
7
|
+
target_place = target_transition.input_places.first
|
8
|
+
|
9
|
+
if input_place_or_transition.is_a? FlowCore::Transition
|
10
|
+
target_place.input_transitions << input_place_or_transition
|
11
|
+
else
|
12
|
+
input_place_or_transition.input_arcs.update place: target_place
|
13
|
+
input_place_or_transition.reload.destroy!
|
14
|
+
end
|
15
|
+
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def creatable?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
def redirection_step?
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def redirection_configurable?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def update_verified
|
36
|
+
self.verified = valid? && redirect_to_step_id.present?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FlowCore::Steps
|
4
|
+
class Task < FlowCore::Step
|
5
|
+
def deploy_to_workflow!(workflow, input_place_or_transition)
|
6
|
+
input_place = find_or_create_input_place(workflow, input_place_or_transition)
|
7
|
+
|
8
|
+
transition = input_place.output_transitions.create! workflow: workflow, name: name, generated_by_step_id: id
|
9
|
+
copy_transition_trigger_to transition
|
10
|
+
copy_transition_callbacks_to transition
|
11
|
+
|
12
|
+
transition
|
13
|
+
end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def creatable?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def transition_trigger_required?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -80,15 +80,20 @@ module FlowCore
|
|
80
80
|
def enable
|
81
81
|
return false unless can_enable?
|
82
82
|
|
83
|
-
|
84
|
-
|
85
|
-
|
83
|
+
status =
|
84
|
+
with_transaction_returning_status do
|
85
|
+
input_free_tokens.each(&:lock!)
|
86
|
+
update! stage: :enabled, enabled_at: Time.zone.now
|
86
87
|
|
87
|
-
|
88
|
-
|
88
|
+
transition.on_task_enable(self)
|
89
|
+
workflow.on_instance_task_enable(self)
|
89
90
|
|
90
|
-
|
91
|
-
|
91
|
+
true
|
92
|
+
end
|
93
|
+
|
94
|
+
try_auto_finish if status
|
95
|
+
|
96
|
+
status
|
92
97
|
end
|
93
98
|
|
94
99
|
def finish
|
@@ -170,7 +175,7 @@ module FlowCore
|
|
170
175
|
return if output_token_created
|
171
176
|
|
172
177
|
with_transaction_returning_status do
|
173
|
-
transition.
|
178
|
+
transition.create_output_tokens_for(self)
|
174
179
|
update! output_token_created: true
|
175
180
|
|
176
181
|
true
|
@@ -220,5 +225,13 @@ module FlowCore
|
|
220
225
|
def same_origin_tasks
|
221
226
|
instance.tasks.where(created_by_token: created_by_token)
|
222
227
|
end
|
228
|
+
|
229
|
+
def try_auto_finish
|
230
|
+
return unless can_finish?
|
231
|
+
|
232
|
+
if transition.auto_finish_strategy == "synchronously"
|
233
|
+
finish
|
234
|
+
end
|
235
|
+
end
|
223
236
|
end
|
224
237
|
end
|
@@ -6,19 +6,36 @@ module FlowCore
|
|
6
6
|
|
7
7
|
FORBIDDEN_ATTRIBUTES = %i[workflow_id created_at updated_at].freeze
|
8
8
|
|
9
|
+
belongs_to :generated_by,
|
10
|
+
class_name: "FlowCore::Step", foreign_key: :generated_by_step_id,
|
11
|
+
inverse_of: :generated_transitions, optional: true
|
12
|
+
|
9
13
|
belongs_to :workflow, class_name: "FlowCore::Workflow"
|
10
14
|
|
11
15
|
# NOTE: Place - out -> Transition - in -> Place
|
12
16
|
has_many :input_arcs, -> { where(direction: :in) },
|
13
|
-
class_name: "FlowCore::Arc", inverse_of: :transition, dependent: :
|
17
|
+
class_name: "FlowCore::Arc", inverse_of: :transition, dependent: :destroy
|
14
18
|
has_many :output_arcs, -> { where(direction: :out) },
|
15
19
|
class_name: "FlowCore::Arc", inverse_of: :transition, dependent: :destroy
|
16
20
|
|
17
21
|
has_many :input_places, through: :input_arcs, class_name: "FlowCore::Place", source: :place
|
22
|
+
has_many :output_places, through: :output_arcs, class_name: "FlowCore::Place", source: :place
|
18
23
|
|
19
24
|
has_one :trigger, class_name: "FlowCore::TransitionTrigger", dependent: :delete
|
20
25
|
has_many :callbacks, class_name: "FlowCore::TransitionCallback", dependent: :delete_all
|
21
26
|
|
27
|
+
enum output_token_create_strategy: {
|
28
|
+
petri_net: 0,
|
29
|
+
match_one_or_fallback: 1
|
30
|
+
}, _suffix: :strategy
|
31
|
+
|
32
|
+
enum auto_finish_strategy: {
|
33
|
+
disabled: 0,
|
34
|
+
synchronously: 1
|
35
|
+
}, _prefix: :auto_finish
|
36
|
+
|
37
|
+
accepts_nested_attributes_for :trigger
|
38
|
+
|
22
39
|
before_destroy :prevent_destroy
|
23
40
|
after_create :reset_workflow_verification
|
24
41
|
after_destroy :reset_workflow_verification
|
@@ -57,7 +74,7 @@ module FlowCore
|
|
57
74
|
end
|
58
75
|
end
|
59
76
|
|
60
|
-
def
|
77
|
+
def create_output_tokens_for(task)
|
61
78
|
instance = task.instance
|
62
79
|
arcs = output_arcs.includes(:place, :guards).to_a
|
63
80
|
|
@@ -68,12 +85,18 @@ module FlowCore
|
|
68
85
|
return
|
69
86
|
end
|
70
87
|
|
71
|
-
|
88
|
+
unless end_arc.fallback_arc?
|
89
|
+
arcs.delete(end_arc)
|
90
|
+
end
|
72
91
|
end
|
73
92
|
|
74
|
-
candidate_arcs =
|
75
|
-
|
76
|
-
|
93
|
+
candidate_arcs =
|
94
|
+
case output_token_create_strategy
|
95
|
+
when "match_one_or_fallback"
|
96
|
+
find_output_arcs_with_match_one_or_fallback_strategy(arcs, task)
|
97
|
+
else
|
98
|
+
find_output_arcs_with_petri_net_strategy(arcs, task)
|
99
|
+
end
|
77
100
|
|
78
101
|
if candidate_arcs.empty?
|
79
102
|
# TODO: find a better way
|
@@ -127,6 +150,21 @@ module FlowCore
|
|
127
150
|
|
128
151
|
private
|
129
152
|
|
153
|
+
def find_output_arcs_with_petri_net_strategy(arcs, task)
|
154
|
+
arcs.select do |arc|
|
155
|
+
arc.guards.empty? || arc.guards.map { |guard| guard.permit? task }.reduce(&:&)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def find_output_arcs_with_match_one_or_fallback_strategy(arcs, task)
|
160
|
+
fallback_arc = arcs.find(&:fallback_arc?)
|
161
|
+
candidate_arcs = arcs.select do |arc|
|
162
|
+
!arc.fallback_arc? && (arc.guards.empty? || arc.guards.map { |guard| guard.permit? task }.reduce(&:&))
|
163
|
+
end
|
164
|
+
|
165
|
+
[candidate_arcs.first || fallback_arc]
|
166
|
+
end
|
167
|
+
|
130
168
|
def reset_workflow_verification
|
131
169
|
workflow.reset_workflow_verification!
|
132
170
|
end
|
@@ -4,11 +4,28 @@ module FlowCore
|
|
4
4
|
class TransitionCallback < FlowCore::ApplicationRecord
|
5
5
|
self.table_name = "flow_core_transition_callbacks"
|
6
6
|
|
7
|
-
belongs_to :workflow, class_name: "FlowCore::Workflow"
|
8
|
-
belongs_to :transition, class_name: "FlowCore::Transition"
|
7
|
+
belongs_to :workflow, class_name: "FlowCore::Workflow", optional: true
|
8
|
+
belongs_to :transition, class_name: "FlowCore::Transition", optional: true
|
9
|
+
|
10
|
+
belongs_to :pipeline, class_name: "FlowCore::Pipeline", optional: true
|
11
|
+
belongs_to :step, class_name: "FlowCore::Step", optional: true
|
12
|
+
|
13
|
+
validates :transition,
|
14
|
+
presence: true,
|
15
|
+
if: ->(r) { r.workflow }
|
16
|
+
validates :step,
|
17
|
+
presence: true,
|
18
|
+
if: ->(r) { r.pipeline }
|
19
|
+
validates :workflow,
|
20
|
+
presence: true,
|
21
|
+
if: ->(r) { !r.pipeline }
|
22
|
+
validates :pipeline,
|
23
|
+
presence: true,
|
24
|
+
if: ->(r) { !r.workflow }
|
9
25
|
|
10
26
|
before_validation do
|
11
27
|
self.workflow ||= transition&.workflow
|
28
|
+
self.pipeline ||= step&.pipeline
|
12
29
|
end
|
13
30
|
|
14
31
|
def configurable?
|
@@ -4,11 +4,28 @@ module FlowCore
|
|
4
4
|
class TransitionTrigger < FlowCore::ApplicationRecord
|
5
5
|
self.table_name = "flow_core_transition_triggers"
|
6
6
|
|
7
|
-
belongs_to :workflow, class_name: "FlowCore::Workflow"
|
8
|
-
belongs_to :transition, class_name: "FlowCore::Transition"
|
7
|
+
belongs_to :workflow, class_name: "FlowCore::Workflow", optional: true
|
8
|
+
belongs_to :transition, class_name: "FlowCore::Transition", optional: true
|
9
|
+
|
10
|
+
belongs_to :pipeline, class_name: "FlowCore::Pipeline", optional: true
|
11
|
+
belongs_to :step, class_name: "FlowCore::Step", optional: true
|
12
|
+
|
13
|
+
validates :transition,
|
14
|
+
presence: true,
|
15
|
+
if: ->(r) { r.workflow }
|
16
|
+
validates :step,
|
17
|
+
presence: true,
|
18
|
+
if: ->(r) { r.pipeline }
|
19
|
+
validates :workflow,
|
20
|
+
presence: true,
|
21
|
+
if: ->(r) { !r.pipeline }
|
22
|
+
validates :pipeline,
|
23
|
+
presence: true,
|
24
|
+
if: ->(r) { !r.workflow }
|
9
25
|
|
10
26
|
before_validation do
|
11
27
|
self.workflow ||= transition&.workflow
|
28
|
+
self.pipeline ||= step&.pipeline
|
12
29
|
end
|
13
30
|
|
14
31
|
def configurable?
|
@@ -6,6 +6,10 @@ module FlowCore
|
|
6
6
|
|
7
7
|
FORBIDDEN_ATTRIBUTES = %i[verified verified_at created_at updated_at].freeze
|
8
8
|
|
9
|
+
belongs_to :generated_by,
|
10
|
+
class_name: "FlowCore::Pipeline", foreign_key: :generated_by_pipeline_id,
|
11
|
+
inverse_of: :generated_workflows, optional: true
|
12
|
+
|
9
13
|
has_many :instances, class_name: "FlowCore::Instance", dependent: :destroy
|
10
14
|
|
11
15
|
has_many :arcs, class_name: "FlowCore::Arc", dependent: :destroy
|
@@ -53,10 +57,10 @@ module FlowCore
|
|
53
57
|
violations.clear
|
54
58
|
|
55
59
|
unless start_place
|
56
|
-
violations.add(
|
60
|
+
violations.add(self, :no_start_place)
|
57
61
|
end
|
58
62
|
unless end_place
|
59
|
-
violations.add(
|
63
|
+
violations.add(self, :no_end_place)
|
60
64
|
end
|
61
65
|
|
62
66
|
return false unless start_place && end_place
|
@@ -69,7 +73,7 @@ module FlowCore
|
|
69
73
|
end_place_code = "P_#{end_place.id}"
|
70
74
|
|
71
75
|
unless rgl.path?(start_place_code, end_place_code)
|
72
|
-
violations.add
|
76
|
+
violations.add end_place, :unreachable
|
73
77
|
end
|
74
78
|
|
75
79
|
places.find_each do |p|
|
@@ -119,15 +123,18 @@ module FlowCore
|
|
119
123
|
|
120
124
|
def verify!
|
121
125
|
update! verified: verify?, verified_at: Time.zone.now
|
122
|
-
|
126
|
+
verified
|
123
127
|
end
|
124
128
|
|
125
129
|
def reset_workflow_verification!
|
130
|
+
return unless verified?
|
131
|
+
|
126
132
|
update! verified: false, verified_at: nil
|
127
133
|
end
|
128
134
|
|
129
135
|
def fork
|
130
136
|
new_workflow = dup
|
137
|
+
|
131
138
|
transaction do
|
132
139
|
yield new_workflow if block_given?
|
133
140
|
new_workflow.save!
|