bpmn 0.0.1 → 0.0.3
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 +6 -6
- data/lib/{spot_flow → bpmn}/context.rb +5 -5
- data/lib/bpmn/definitions.rb +53 -0
- data/lib/bpmn/element.rb +42 -0
- data/lib/bpmn/event.rb +193 -0
- data/lib/bpmn/event_definition.rb +133 -0
- data/lib/{spot_flow → bpmn}/execution.rb +8 -8
- data/lib/bpmn/extension_elements.rb +27 -0
- data/lib/bpmn/extensions.rb +75 -0
- data/lib/bpmn/flow.rb +45 -0
- data/lib/bpmn/gateway.rb +83 -0
- data/lib/bpmn/process.rb +177 -0
- data/lib/bpmn/step.rb +56 -0
- data/lib/bpmn/task.rb +126 -0
- data/lib/bpmn/version.rb +5 -0
- data/lib/{spot_flow.rb → bpmn.rb} +19 -8
- metadata +32 -22
- data/lib/spot_flow/bpmn/definitions.rb +0 -55
- data/lib/spot_flow/bpmn/element.rb +0 -44
- data/lib/spot_flow/bpmn/event.rb +0 -195
- data/lib/spot_flow/bpmn/event_definition.rb +0 -135
- data/lib/spot_flow/bpmn/extension_elements.rb +0 -29
- data/lib/spot_flow/bpmn/extensions.rb +0 -77
- data/lib/spot_flow/bpmn/flow.rb +0 -47
- data/lib/spot_flow/bpmn/gateway.rb +0 -85
- data/lib/spot_flow/bpmn/process.rb +0 -179
- data/lib/spot_flow/bpmn/step.rb +0 -58
- data/lib/spot_flow/bpmn/task.rb +0 -128
- data/lib/spot_flow/bpmn.rb +0 -18
- data/lib/spot_flow/version.rb +0 -5
data/lib/bpmn/flow.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BPMN
|
4
|
+
class Flow < Element
|
5
|
+
attr_accessor :source_ref, :target_ref
|
6
|
+
attr_accessor :source, :target
|
7
|
+
|
8
|
+
def initialize(attributes = {})
|
9
|
+
super(attributes.except(:source_ref, :target_ref))
|
10
|
+
|
11
|
+
@source_ref = attributes[:source_ref]
|
12
|
+
@target_ref = attributes[:target_ref]
|
13
|
+
@source = nil
|
14
|
+
@target = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def inspect
|
18
|
+
parts = ["#<#{self.class.name.gsub(/BPMN::/, "")} @id=#{id.inspect}"]
|
19
|
+
parts << "@source_ref=#{source_ref.inspect}" if source_ref
|
20
|
+
parts << "@target_ref=#{target_ref.inspect}" if target_ref
|
21
|
+
parts.join(" ") + ">"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Association < Flow
|
26
|
+
end
|
27
|
+
|
28
|
+
class SequenceFlow < Flow
|
29
|
+
attr_accessor :condition
|
30
|
+
|
31
|
+
def initialize(attributes = {})
|
32
|
+
super(attributes.except(:condition))
|
33
|
+
|
34
|
+
@condition = attributes[:condition_expression]
|
35
|
+
end
|
36
|
+
|
37
|
+
def evaluate(execution)
|
38
|
+
return true unless condition
|
39
|
+
execution.evaluate_condition(condition)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class TextAnnotation < Flow
|
44
|
+
end
|
45
|
+
end
|
data/lib/bpmn/gateway.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BPMN
|
4
|
+
class Gateway < Step
|
5
|
+
|
6
|
+
def execute(execution)
|
7
|
+
if converging?
|
8
|
+
if is_enabled?(execution)
|
9
|
+
return leave(execution)
|
10
|
+
else
|
11
|
+
execution.wait
|
12
|
+
end
|
13
|
+
else
|
14
|
+
return leave(execution)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# Algorithm from https://researcher.watson.ibm.com/researcher/files/zurich-hvo/bpm2010-1.pdf
|
20
|
+
#
|
21
|
+
def is_enabled?(execution)
|
22
|
+
filled = []
|
23
|
+
empty = []
|
24
|
+
|
25
|
+
incoming.each { |flow| execution.tokens_in.include?(flow.id) ? filled.push(flow) : empty.push(flow) }
|
26
|
+
|
27
|
+
# Filled slots don't need to be searched for tokens
|
28
|
+
index = 0
|
29
|
+
while (index < filled.length)
|
30
|
+
current_flow = filled[index]
|
31
|
+
current_flow.source.incoming.each do |incoming_flow|
|
32
|
+
filled.push(incoming_flow) unless filled.include?(incoming_flow) || incoming_flow.target == self
|
33
|
+
end
|
34
|
+
index = index + 1
|
35
|
+
end
|
36
|
+
|
37
|
+
# Empty slots need to be searched for tokens
|
38
|
+
index = 0
|
39
|
+
while (index < empty.length)
|
40
|
+
current_flow = empty[index]
|
41
|
+
current_flow.source.incoming.each do |incoming_flow|
|
42
|
+
empty.push(incoming_flow) unless filled.include?(incoming_flow) || empty.include?(incoming_flow) || incoming_flow.target == self
|
43
|
+
end
|
44
|
+
index = index + 1
|
45
|
+
end
|
46
|
+
|
47
|
+
empty_ids = empty.map { |g| g.id }
|
48
|
+
|
49
|
+
# If there are empty slots with tokens we need to wait
|
50
|
+
return false if (empty_ids & execution.parent.tokens).length > 0
|
51
|
+
return true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class ExclusiveGateway < Gateway
|
56
|
+
# RULE: Only one flow is taken
|
57
|
+
def outgoing_flows(step_execution)
|
58
|
+
flows = super
|
59
|
+
return [flows.first]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class ParallelGateway < Gateway
|
64
|
+
# RULE: All flows are taken
|
65
|
+
end
|
66
|
+
|
67
|
+
class InclusiveGateway < Gateway
|
68
|
+
# RULE: All valid flows are taken
|
69
|
+
# NOTE: Camunda 8 only support diverging but not converging inclusive gateways
|
70
|
+
end
|
71
|
+
|
72
|
+
class EventBasedGateway < Gateway
|
73
|
+
#
|
74
|
+
# RULE: when an event created from an event gateway is caught,
|
75
|
+
# all other waiting events must be canceled.
|
76
|
+
#
|
77
|
+
def cancel_waiting_events(execution)
|
78
|
+
execution.targets.each do |target_execution|
|
79
|
+
target_execution.terminate unless target_execution.ended?
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/bpmn/process.rb
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BPMN
|
4
|
+
class Process < Step
|
5
|
+
attr_accessor :is_executable
|
6
|
+
|
7
|
+
attr_accessor :start_events, :end_events, :intermediate_catch_events, :intermediate_throw_events, :boundary_events
|
8
|
+
attr_accessor :tasks, :user_tasks, :service_tasks, :script_tasks, :send_tasks, :receive_tasks, :manual_tasks, :business_rule_tasks, :call_activities
|
9
|
+
attr_accessor :sub_processes, :ad_hoc_sub_processes
|
10
|
+
attr_accessor :sequence_flows, :message_flows, :associations, :data_associations, :data_inputs, :data_outputs
|
11
|
+
attr_accessor :data_objects, :data_stores, :data_stores_references
|
12
|
+
attr_accessor :gateways, :exclusive_gateways, :parallel_gateways, :inclusive_gateways, :event_based_gateways, :complex_gateways
|
13
|
+
|
14
|
+
attr_accessor :parent
|
15
|
+
|
16
|
+
def initialize(attributes = {})
|
17
|
+
super(attributes.slice(:id, :name, :extension_elements, :incoming, :outgoing, :default))
|
18
|
+
|
19
|
+
@is_executable = attributes[:is_executable] == ("true" || true)
|
20
|
+
|
21
|
+
@start_events = Array.wrap(attributes[:start_event]).map { |se| StartEvent.new(se) }
|
22
|
+
@end_events = Array.wrap(attributes[:end_event]).map { |ee| EndEvent.new(ee) }
|
23
|
+
@intermediate_catch_events = Array.wrap(attributes[:intermediate_catch_event]).map { |ice| IntermediateCatchEvent.new(ice) }
|
24
|
+
@intermediate_throw_events = Array.wrap(attributes[:intermediate_throw_event]).map { |ite| IntermediateThrowEvent.new(ite) }
|
25
|
+
@boundary_events = Array.wrap(attributes[:boundary_event]).map { |be| BoundaryEvent.new(be) }
|
26
|
+
@tasks = Array.wrap(attributes[:task]).map { |t| Task.new(t) }
|
27
|
+
@user_tasks = Array.wrap(attributes[:user_task]).map { |ut| UserTask.new(ut) }
|
28
|
+
@service_tasks = Array.wrap(attributes[:service_task]).map { |st| ServiceTask.new(st) }
|
29
|
+
@script_tasks = Array.wrap(attributes[:script_task]).map { |st| ScriptTask.new(st) }
|
30
|
+
@business_rule_tasks = Array.wrap(attributes[:business_rule_task]).map { |brt| BusinessRuleTask.new(brt) }
|
31
|
+
@call_activities = Array.wrap(attributes[:call_activity]).map { |ca| CallActivity.new(ca) }
|
32
|
+
@sub_processes = Array.wrap(attributes[:sub_process]).map { |sp| SubProcess.new(sp) }
|
33
|
+
@ad_hoc_sub_processes = Array.wrap(attributes[:ad_hoc_sub_processe]).map { |ahsp| AdHocSubProcess.new(ahsp) }
|
34
|
+
@exclusive_gateways = Array.wrap(attributes[:exclusive_gateway]).map { |eg| ExclusiveGateway.new(eg) }
|
35
|
+
@parallel_gateways = Array.wrap(attributes[:parallel_gateway]).map { |pg| ParallelGateway.new(pg) }
|
36
|
+
@inclusive_gateways = Array.wrap(attributes[:inclusive_gateway]).map { |ig| InclusiveGateway.new(ig) }
|
37
|
+
@event_based_gateways = Array.wrap(attributes[:event_based_gateway]).map { |ebg| EventBasedGateway.new(ebg) }
|
38
|
+
@sequence_flows = Array.wrap(attributes[:sequence_flow]).map { |sf| SequenceFlow.new(sf) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def elements
|
42
|
+
@elements ||= {}.tap do |elements|
|
43
|
+
elements.merge!(start_events.index_by(&:id))
|
44
|
+
elements.merge!(end_events.index_by(&:id))
|
45
|
+
elements.merge!(intermediate_catch_events.index_by(&:id))
|
46
|
+
elements.merge!(intermediate_throw_events.index_by(&:id))
|
47
|
+
elements.merge!(boundary_events.index_by(&:id))
|
48
|
+
elements.merge!(tasks.index_by(&:id))
|
49
|
+
elements.merge!(user_tasks.index_by(&:id))
|
50
|
+
elements.merge!(service_tasks.index_by(&:id))
|
51
|
+
elements.merge!(script_tasks.index_by(&:id))
|
52
|
+
elements.merge!(business_rule_tasks.index_by(&:id))
|
53
|
+
elements.merge!(call_activities.index_by(&:id))
|
54
|
+
elements.merge!(sub_processes.index_by(&:id))
|
55
|
+
elements.merge!(ad_hoc_sub_processes.index_by(&:id))
|
56
|
+
elements.merge!(exclusive_gateways.index_by(&:id))
|
57
|
+
elements.merge!(parallel_gateways.index_by(&:id))
|
58
|
+
elements.merge!(inclusive_gateways.index_by(&:id))
|
59
|
+
elements.merge!(event_based_gateways.index_by(&:id))
|
60
|
+
elements.merge!(sequence_flows.index_by(&:id))
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def wire_references(definitions)
|
65
|
+
elements.values.each do |element|
|
66
|
+
if element.is_a?(Step)
|
67
|
+
element.incoming = element.incoming.map { |id| element_by_id(id) }
|
68
|
+
element.outgoing = element.outgoing.map { |id| element_by_id(id) }
|
69
|
+
|
70
|
+
if element.is_a?(Event)
|
71
|
+
|
72
|
+
element.event_definitions.each do |event_definition|
|
73
|
+
if event_definition.is_a?(MessageEventDefinition)
|
74
|
+
event_definition.message = definitions.message_by_id(event_definition.message_ref)
|
75
|
+
elsif event_definition.is_a?(SignalEventDefinition)
|
76
|
+
event_definition.signal = definitions.signal_by_id(event_definition.signal_ref)
|
77
|
+
elsif event_definition.is_a?(ErrorEventDefinition)
|
78
|
+
event_definition.error = definitions.error_by_id(event_definition.error_ref)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
if element.is_a?(BoundaryEvent)
|
83
|
+
host_element = element_by_id(element.attached_to_ref)
|
84
|
+
host_element.attachments << element
|
85
|
+
element.attached_to = host_element
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
if element.is_a?(Gateway)
|
91
|
+
element.default = element_by_id(element.default_ref)
|
92
|
+
end
|
93
|
+
|
94
|
+
if element.is_a?(SubProcess)
|
95
|
+
element.wire_references(definitions)
|
96
|
+
end
|
97
|
+
|
98
|
+
elsif element.is_a?(SequenceFlow)
|
99
|
+
element.source = element_by_id(element.source_ref)
|
100
|
+
element.target = element_by_id(element.target_ref)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Not handled participant process ref
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def element_by_id(id)
|
108
|
+
elements[id]
|
109
|
+
end
|
110
|
+
|
111
|
+
def elements_by_type(type)
|
112
|
+
elements.select { |e| e.class == type }
|
113
|
+
end
|
114
|
+
|
115
|
+
def default_start_event
|
116
|
+
start_events.find { |se| se.event_definitions.empty? }
|
117
|
+
end
|
118
|
+
|
119
|
+
def execute(execution)
|
120
|
+
start_event = execution.start_event_id ? element_by_id(execution.start_event_id) : default_start_event
|
121
|
+
raise ExecutionErrorNew.new("Process must have at least one start event.") if start_event.blank?
|
122
|
+
execution.execute_step(start_event)
|
123
|
+
end
|
124
|
+
|
125
|
+
def inspect
|
126
|
+
parts = ["#<#{self.class.name.gsub(/BPMN::/, "")} @id=#{id.inspect} @name=#{name.inspect} @is_executable=#{is_executable.inspect}"]
|
127
|
+
parts << "@parent=#{parent.inspect}" if parent
|
128
|
+
event_attrs = (start_events + intermediate_catch_events + intermediate_throw_events + boundary_events + end_events).compact
|
129
|
+
parts << "@events=#{event_attrs.inspect}" unless event_attrs.blank?
|
130
|
+
activity_attrs = (tasks + user_tasks + service_tasks + script_tasks + business_rule_tasks + call_activities).compact
|
131
|
+
parts << "@activities=#{activity_attrs.inspect}" unless activity_attrs.blank?
|
132
|
+
gateway_attrs = (exclusive_gateways + parallel_gateways + inclusive_gateways + event_based_gateways).compact
|
133
|
+
parts << "@gateways=#{gateway_attrs.inspect}" unless gateway_attrs.blank?
|
134
|
+
sub_process_attrs = (sub_processes + ad_hoc_sub_processes).compact
|
135
|
+
parts << "@sub_processes=#{sub_process_attrs.inspect}" unless sub_process_attrs.blank?
|
136
|
+
parts << "@sequence_flows=#{sequence_flows.inspect}" unless sequence_flows.blank?
|
137
|
+
parts.join(" ") + ">"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class SubProcess < Process
|
142
|
+
attr_accessor :triggered_by_event
|
143
|
+
|
144
|
+
def initialize(attributes = {})
|
145
|
+
super(attributes.except(:triggered_by_event))
|
146
|
+
|
147
|
+
@is_executable = false
|
148
|
+
@sub_processes = []
|
149
|
+
@triggered_by_event = attributes[:triggered_by_event]
|
150
|
+
end
|
151
|
+
|
152
|
+
def execution_ended(execution)
|
153
|
+
leave(execution)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
class AdHocSubProcess < SubProcess
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
class CallActivity < Activity
|
162
|
+
attr_reader :process_id
|
163
|
+
|
164
|
+
def execute(execution)
|
165
|
+
if extension_elements&.called_element&.process_id&.start_with?("=")
|
166
|
+
@process_id = DMN.evaluate(extension_elements&.called_element&.process_id, variables: execution.variables)
|
167
|
+
else
|
168
|
+
@process_id = extension_elements&.called_element&.process_id
|
169
|
+
end
|
170
|
+
|
171
|
+
execution.wait
|
172
|
+
|
173
|
+
process = execution.context.process_by_id(@process_id) if @process_id
|
174
|
+
execution.execute_step(process.default_start_event) if process
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
data/lib/bpmn/step.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BPMN
|
4
|
+
class Step < Element
|
5
|
+
attr_accessor :incoming, :outgoing, :default, :default_ref
|
6
|
+
|
7
|
+
def initialize(attributes = {})
|
8
|
+
super(attributes.except(:incoming, :outgoing, :default))
|
9
|
+
|
10
|
+
@incoming = Array.wrap(attributes[:incoming]) || []
|
11
|
+
@outgoing = Array.wrap(attributes[:outgoing]) || []
|
12
|
+
@default_ref = attributes[:default]
|
13
|
+
end
|
14
|
+
|
15
|
+
def diverging?
|
16
|
+
outgoing.length > 1
|
17
|
+
end
|
18
|
+
|
19
|
+
def converging?
|
20
|
+
incoming.length > 1
|
21
|
+
end
|
22
|
+
|
23
|
+
def leave(execution)
|
24
|
+
execution.end(false)
|
25
|
+
execution.take_all(outgoing_flows(execution))
|
26
|
+
end
|
27
|
+
|
28
|
+
def outgoing_flows(execution)
|
29
|
+
flows = []
|
30
|
+
outgoing.each do |flow|
|
31
|
+
result = flow.evaluate(execution) unless default&.id == flow.id
|
32
|
+
flows.push flow if result
|
33
|
+
end
|
34
|
+
flows = [default] if flows.empty? && default
|
35
|
+
return flows
|
36
|
+
end
|
37
|
+
|
38
|
+
def input_mappings
|
39
|
+
extension_elements&.io_mapping&.inputs || []
|
40
|
+
end
|
41
|
+
|
42
|
+
def output_mappings
|
43
|
+
extension_elements&.io_mapping&.outputs || []
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Activity < Step
|
48
|
+
attr_accessor :attachments
|
49
|
+
|
50
|
+
def initialize(attributes = {})
|
51
|
+
super(attributes.except(:attachments))
|
52
|
+
|
53
|
+
@attachments = []
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/bpmn/task.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BPMN
|
4
|
+
class Task < Activity
|
5
|
+
|
6
|
+
def is_automated?
|
7
|
+
false
|
8
|
+
end
|
9
|
+
|
10
|
+
def is_manual?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute(execution)
|
15
|
+
execution.wait
|
16
|
+
end
|
17
|
+
|
18
|
+
def signal(execution)
|
19
|
+
leave(execution)
|
20
|
+
end
|
21
|
+
|
22
|
+
def result_to_variables(result)
|
23
|
+
if result_variable
|
24
|
+
return { "#{result_variable}": result }
|
25
|
+
else
|
26
|
+
if result.is_a? Hash
|
27
|
+
result
|
28
|
+
else
|
29
|
+
{}.tap { |h| h[id.underscore] = result }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class UserTask < Task
|
36
|
+
|
37
|
+
def form_key
|
38
|
+
extension_elements&.form_definition&.form_key
|
39
|
+
end
|
40
|
+
|
41
|
+
def assignee
|
42
|
+
extension_elements&.assignment_definition&.assignee
|
43
|
+
end
|
44
|
+
|
45
|
+
def candidate_groups
|
46
|
+
extension_elements&.assignment_definition&.candidate_groups
|
47
|
+
end
|
48
|
+
|
49
|
+
def candidate_users
|
50
|
+
extension_elements&.assignment_definition&.candidate_users
|
51
|
+
end
|
52
|
+
|
53
|
+
def due_date
|
54
|
+
extension_elements&.task_schedule&.due_date
|
55
|
+
end
|
56
|
+
|
57
|
+
def follow_up_date
|
58
|
+
extension_elements&.task_schedule&.follow_up_date
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class ServiceTask < Task
|
63
|
+
attr_accessor :service
|
64
|
+
|
65
|
+
def is_automated?
|
66
|
+
true
|
67
|
+
end
|
68
|
+
|
69
|
+
def is_manual?
|
70
|
+
false
|
71
|
+
end
|
72
|
+
|
73
|
+
def execute(execution)
|
74
|
+
execution.wait
|
75
|
+
end
|
76
|
+
|
77
|
+
def task_type
|
78
|
+
extension_elements&.task_definition&.type
|
79
|
+
end
|
80
|
+
|
81
|
+
def task_retries
|
82
|
+
extension_elements&.task_definition&.retries
|
83
|
+
end
|
84
|
+
|
85
|
+
def headers
|
86
|
+
extension_elements&.task_headers&.headers
|
87
|
+
end
|
88
|
+
|
89
|
+
def run(execution)
|
90
|
+
if defined?(task_type)
|
91
|
+
klass = task_type.constantize
|
92
|
+
klass.new.call(execution.parent.variables, headers || {})
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class ScriptTask < ServiceTask
|
98
|
+
|
99
|
+
def script
|
100
|
+
extension_elements&.script&.expression
|
101
|
+
end
|
102
|
+
|
103
|
+
def result_variable
|
104
|
+
extension_elements&.script&.result_variable
|
105
|
+
end
|
106
|
+
|
107
|
+
def run(execution)
|
108
|
+
DMN.evaluate(script.delete_prefix("="), variables: execution.parent.variables)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class BusinessRuleTask < ServiceTask
|
113
|
+
|
114
|
+
def decision_id
|
115
|
+
extension_elements&.called_decision&.decision_id
|
116
|
+
end
|
117
|
+
|
118
|
+
def result_variable
|
119
|
+
extension_elements&.called_decision&.result_variable
|
120
|
+
end
|
121
|
+
|
122
|
+
def run(execution)
|
123
|
+
DMN.decide(decision_id, definitions: execution.context.dmn_definitions_by_decision_id(decision_id), variables: execution.parent.variables)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
data/lib/bpmn/version.rb
ADDED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "bpmn/version"
|
4
4
|
|
5
5
|
require "active_support"
|
6
6
|
require "active_support/time"
|
@@ -9,13 +9,24 @@ require "active_support/core_ext/object/json"
|
|
9
9
|
require "active_support/configurable"
|
10
10
|
require "active_model"
|
11
11
|
|
12
|
-
require "
|
12
|
+
require "dmn"
|
13
13
|
|
14
|
-
require "
|
15
|
-
require "
|
16
|
-
require "
|
14
|
+
require "bpmn/element"
|
15
|
+
require "bpmn/extensions"
|
16
|
+
require "bpmn/extension_elements"
|
17
|
+
require "bpmn/step"
|
18
|
+
require "bpmn/flow"
|
19
|
+
require "bpmn/event_definition"
|
20
|
+
require "bpmn/event"
|
21
|
+
require "bpmn/gateway"
|
22
|
+
require "bpmn/task"
|
23
|
+
require "bpmn/process"
|
24
|
+
require "bpmn/definitions"
|
17
25
|
|
18
|
-
|
26
|
+
require "bpmn/context"
|
27
|
+
require "bpmn/execution"
|
28
|
+
|
29
|
+
module BPMN
|
19
30
|
include ActiveSupport::Configurable
|
20
31
|
|
21
32
|
#
|
@@ -36,14 +47,14 @@ module SpotFlow
|
|
36
47
|
# Extract processes from a BMPN XML file.
|
37
48
|
#
|
38
49
|
def self.processes_from_xml(xml)
|
39
|
-
|
50
|
+
Definitions.from_xml(xml)&.processes || []
|
40
51
|
end
|
41
52
|
|
42
53
|
#
|
43
54
|
# Extract decisions from a DMN XML file.
|
44
55
|
#
|
45
56
|
def self.decisions_from_xml(xml)
|
46
|
-
definitions =
|
57
|
+
definitions = DMN.definitions_from_xml(xml)
|
47
58
|
definitions.decisions
|
48
59
|
end
|
49
60
|
end
|
metadata
CHANGED
@@ -1,15 +1,28 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bpmn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Connected Bits
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-03-03 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: dmn
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: 0.0.3
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: 0.0.3
|
13
26
|
- !ruby/object:Gem::Dependency
|
14
27
|
name: activemodel
|
15
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -189,29 +202,27 @@ files:
|
|
189
202
|
- MIT-LICENSE
|
190
203
|
- README.md
|
191
204
|
- Rakefile
|
192
|
-
- lib/
|
193
|
-
- lib/
|
194
|
-
- lib/
|
195
|
-
- lib/
|
196
|
-
- lib/
|
197
|
-
- lib/
|
198
|
-
- lib/
|
199
|
-
- lib/
|
200
|
-
- lib/
|
201
|
-
- lib/
|
202
|
-
- lib/
|
203
|
-
- lib/
|
204
|
-
- lib/
|
205
|
-
- lib/
|
206
|
-
- lib/
|
207
|
-
- lib/spot_flow/version.rb
|
205
|
+
- lib/bpmn.rb
|
206
|
+
- lib/bpmn/context.rb
|
207
|
+
- lib/bpmn/definitions.rb
|
208
|
+
- lib/bpmn/element.rb
|
209
|
+
- lib/bpmn/event.rb
|
210
|
+
- lib/bpmn/event_definition.rb
|
211
|
+
- lib/bpmn/execution.rb
|
212
|
+
- lib/bpmn/extension_elements.rb
|
213
|
+
- lib/bpmn/extensions.rb
|
214
|
+
- lib/bpmn/flow.rb
|
215
|
+
- lib/bpmn/gateway.rb
|
216
|
+
- lib/bpmn/process.rb
|
217
|
+
- lib/bpmn/step.rb
|
218
|
+
- lib/bpmn/task.rb
|
219
|
+
- lib/bpmn/version.rb
|
208
220
|
homepage: https://www.connectedbits.com
|
209
221
|
licenses:
|
210
222
|
- MIT
|
211
223
|
metadata:
|
212
224
|
homepage_uri: https://www.connectedbits.com
|
213
225
|
source_code_uri: https://github.com/connectedbits/process_pilot
|
214
|
-
post_install_message:
|
215
226
|
rdoc_options: []
|
216
227
|
require_paths:
|
217
228
|
- lib
|
@@ -226,8 +237,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
226
237
|
- !ruby/object:Gem::Version
|
227
238
|
version: '0'
|
228
239
|
requirements: []
|
229
|
-
rubygems_version: 3.
|
230
|
-
signing_key:
|
240
|
+
rubygems_version: 3.6.5
|
231
241
|
specification_version: 4
|
232
242
|
summary: A BPMN workflow engine in Ruby
|
233
243
|
test_files: []
|
@@ -1,55 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module SpotFlow
|
4
|
-
module Bpmn
|
5
|
-
class Definitions
|
6
|
-
include ActiveModel::Model
|
7
|
-
|
8
|
-
attr_accessor :id, :name, :target_namespace, :exporter, :exporter_version, :execution_platform, :execution_platform_version
|
9
|
-
attr_reader :messages, :signals, :errors, :processes
|
10
|
-
|
11
|
-
def self.from_xml(xml)
|
12
|
-
XmlHasher.configure do |config|
|
13
|
-
config.snakecase = true
|
14
|
-
config.ignore_namespaces = true
|
15
|
-
config.string_keys = false
|
16
|
-
end
|
17
|
-
hash = XmlHasher.parse(xml)
|
18
|
-
Definitions.new(hash[:definitions].except(:bpmn_diagram)).tap do |definitions|
|
19
|
-
definitions.processes.each do |process|
|
20
|
-
process.wire_references(definitions)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def initialize(attributes={})
|
26
|
-
super(attributes.except(:message, :signal, :error, :process))
|
27
|
-
|
28
|
-
@messages = Array.wrap(attributes[:message]).map { |atts| Message.new(atts) }
|
29
|
-
@signals = Array.wrap(attributes[:signal]).map { |atts| Signal.new(atts) }
|
30
|
-
@errors = Array.wrap(attributes[:error]).map { |atts| Error.new(atts) }
|
31
|
-
@processes = Array.wrap(attributes[:process]).map { |atts| Process.new(atts) }
|
32
|
-
end
|
33
|
-
|
34
|
-
def message_by_id(id)
|
35
|
-
messages.find { |message| message.id == id }
|
36
|
-
end
|
37
|
-
|
38
|
-
def signal_by_id(id)
|
39
|
-
signals.find { |signal| signal.id == id }
|
40
|
-
end
|
41
|
-
|
42
|
-
def error_by_id(id)
|
43
|
-
errors.find { |error| error.id == id }
|
44
|
-
end
|
45
|
-
|
46
|
-
def process_by_id(id)
|
47
|
-
processes.find { |process| process.id == id }
|
48
|
-
end
|
49
|
-
|
50
|
-
def inspect
|
51
|
-
"#<Bpmn::Definitions @id=#{id.inspect} @name=#{name.inspect} @messages=#{messages.inspect} @signals=#{signals.inspect} @errors=#{errors.inspect} @processes=#{processes.inspect}>"
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|