spot_flow 0.0.1

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.
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpotFlow
4
+ module Bpmn
5
+ class Extension
6
+ include ActiveModel::Model
7
+ end
8
+ end
9
+
10
+ module Zeebe
11
+ class AssignmentDefinition < Bpmn::Extension
12
+ attr_accessor :assignee, :candidate_groups, :candidate_users
13
+ end
14
+
15
+ class CalledElement < Bpmn::Extension
16
+ attr_accessor :process_id, :propagate_all_child_variables, :propagate_all_parent_variables
17
+
18
+ def initialize(attributes = {})
19
+ super(attributes.except(:propagate_all_child_variables))
20
+
21
+ @propagate_all_parent_variables = true
22
+ @propagate_all_parent_variables = attributes[:propagate_all_parent_variables] == "true" if attributes[:propagate_all_parent_variables].present?
23
+ @propagate_all_child_variables = attributes[:propagate_all_child_variables] == "true"
24
+ end
25
+ end
26
+
27
+ class CalledDecision < Bpmn::Extension
28
+ attr_accessor :decision_id, :result_variable
29
+ end
30
+
31
+ class FormDefinition < Bpmn::Extension
32
+ attr_accessor :form_key
33
+ end
34
+
35
+ class IoMapping < Bpmn::Extension
36
+ attr_reader :inputs, :outputs
37
+
38
+ def initialize(attributes = {})
39
+ super(attributes.except(:input, :output))
40
+
41
+ @inputs = Array.wrap(attributes[:input]).map { |atts| Parameter.new(atts) } if attributes[:input].present?
42
+ @outputs = Array.wrap(attributes[:output]).map { |atts| Parameter.new(atts) } if attributes[:output].present?
43
+ end
44
+ end
45
+
46
+ class Parameter < Bpmn::Extension
47
+ attr_accessor :source, :target
48
+ end
49
+
50
+ class Script < Bpmn::Extension
51
+ attr_accessor :expression, :result_variable
52
+ end
53
+
54
+ class Subscription < Bpmn::Extension
55
+ attr_accessor :correlation_key
56
+ end
57
+
58
+ class TaskDefinition < Bpmn::Extension
59
+ attr_accessor :type, :retries
60
+ end
61
+
62
+ class TaskHeaders < Bpmn::Extension
63
+ attr_accessor :headers
64
+
65
+ def initialize(attributes = {})
66
+ super(attributes.except(:header))
67
+
68
+ @headers = HashWithIndifferentAccess.new
69
+ Array.wrap(attributes[:header]).each { |header| @headers[header[:key]] = header[:value] }
70
+ end
71
+ end
72
+
73
+ class TaskSchedule < Bpmn::Extension
74
+ attr_accessor :due_date, :follow_up_date
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpotFlow
4
+ module Bpmn
5
+ class Flow < Element
6
+ attr_accessor :source_ref, :target_ref
7
+ attr_accessor :source, :target
8
+
9
+ def initialize(attributes = {})
10
+ super(attributes.except(:source_ref, :target_ref))
11
+
12
+ @source_ref = attributes[:source_ref]
13
+ @target_ref = attributes[:target_ref]
14
+ @source = nil
15
+ @target = nil
16
+ end
17
+
18
+ def inspect
19
+ parts = ["#<#{self.class.name.gsub(/SpotFlow::/, "")} @id=#{id.inspect}"]
20
+ parts << "@source_ref=#{source_ref.inspect}" if source_ref
21
+ parts << "@target_ref=#{target_ref.inspect}" if target_ref
22
+ parts.join(" ") + ">"
23
+ end
24
+ end
25
+
26
+ class Association < Flow
27
+ end
28
+
29
+ class SequenceFlow < Flow
30
+ attr_accessor :condition
31
+
32
+ def initialize(attributes = {})
33
+ super(attributes.except(:condition))
34
+
35
+ @condition = attributes[:condition_expression]
36
+ end
37
+
38
+ def evaluate(execution)
39
+ return true unless condition
40
+ execution.evaluate_condition(condition)
41
+ end
42
+ end
43
+
44
+ class TextAnnotation < Flow
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpotFlow
4
+ module Bpmn
5
+ class Gateway < Step
6
+
7
+ def execute(execution)
8
+ if converging?
9
+ if is_enabled?(execution)
10
+ return leave(execution)
11
+ else
12
+ execution.wait
13
+ end
14
+ else
15
+ return leave(execution)
16
+ end
17
+ end
18
+
19
+ #
20
+ # Algorithm from https://researcher.watson.ibm.com/researcher/files/zurich-hvo/bpm2010-1.pdf
21
+ #
22
+ def is_enabled?(execution)
23
+ filled = []
24
+ empty = []
25
+
26
+ incoming.each { |flow| execution.tokens_in.include?(flow.id) ? filled.push(flow) : empty.push(flow) }
27
+
28
+ # Filled slots don't need to be searched for tokens
29
+ index = 0
30
+ while (index < filled.length)
31
+ current_flow = filled[index]
32
+ current_flow.source.incoming.each do |incoming_flow|
33
+ filled.push(incoming_flow) unless filled.include?(incoming_flow) || incoming_flow.target == self
34
+ end
35
+ index = index + 1
36
+ end
37
+
38
+ # Empty slots need to be searched for tokens
39
+ index = 0
40
+ while (index < empty.length)
41
+ current_flow = empty[index]
42
+ current_flow.source.incoming.each do |incoming_flow|
43
+ empty.push(incoming_flow) unless filled.include?(incoming_flow) || empty.include?(incoming_flow) || incoming_flow.target == self
44
+ end
45
+ index = index + 1
46
+ end
47
+
48
+ empty_ids = empty.map { |g| g.id }
49
+
50
+ # If there are empty slots with tokens we need to wait
51
+ return false if (empty_ids & execution.parent.tokens).length > 0
52
+ return true
53
+ end
54
+ end
55
+
56
+ class ExclusiveGateway < Gateway
57
+ # RULE: Only one flow is taken
58
+ def outgoing_flows(step_execution)
59
+ flows = super
60
+ return [flows.first]
61
+ end
62
+ end
63
+
64
+ class ParallelGateway < Gateway
65
+ # RULE: All flows are taken
66
+ end
67
+
68
+ class InclusiveGateway < Gateway
69
+ # RULE: All valid flows are taken
70
+ # NOTE: Camunda 8 only support diverging but not converging inclusive gateways
71
+ end
72
+
73
+ class EventBasedGateway < Gateway
74
+ #
75
+ # RULE: when an event created from an event gateway is caught,
76
+ # all other waiting events must be canceled.
77
+ #
78
+ def cancel_waiting_events(execution)
79
+ execution.targets.each do |target_execution|
80
+ target_execution.terminate unless target_execution.ended?
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpotFlow
4
+ module Bpmn
5
+ class Process < Step
6
+ attr_accessor :is_executable
7
+
8
+ attr_accessor :start_events, :end_events, :intermediate_catch_events, :intermediate_throw_events, :boundary_events
9
+ attr_accessor :tasks, :user_tasks, :service_tasks, :script_tasks, :send_tasks, :receive_tasks, :manual_tasks, :business_rule_tasks, :call_activities
10
+ attr_accessor :sub_processes, :ad_hoc_sub_processes
11
+ attr_accessor :sequence_flows, :message_flows, :associations, :data_associations, :data_inputs, :data_outputs
12
+ attr_accessor :data_objects, :data_stores, :data_stores_references
13
+ attr_accessor :gateways, :exclusive_gateways, :parallel_gateways, :inclusive_gateways, :event_based_gateways, :complex_gateways
14
+
15
+ attr_accessor :parent
16
+
17
+ def initialize(attributes = {})
18
+ super(attributes.slice(:id, :name, :extension_elements, :incoming, :outgoing, :default))
19
+
20
+ @is_executable = attributes[:is_executable] == ("true" || true)
21
+
22
+ @start_events = Array.wrap(attributes[:start_event]).map { |se| StartEvent.new(se) }
23
+ @end_events = Array.wrap(attributes[:end_event]).map { |ee| EndEvent.new(ee) }
24
+ @intermediate_catch_events = Array.wrap(attributes[:intermediate_catch_event]).map { |ice| IntermediateCatchEvent.new(ice) }
25
+ @intermediate_throw_events = Array.wrap(attributes[:intermediate_throw_event]).map { |ite| IntermediateThrowEvent.new(ite) }
26
+ @boundary_events = Array.wrap(attributes[:boundary_event]).map { |be| BoundaryEvent.new(be) }
27
+ @tasks = Array.wrap(attributes[:task]).map { |t| Task.new(t) }
28
+ @user_tasks = Array.wrap(attributes[:user_task]).map { |ut| UserTask.new(ut) }
29
+ @service_tasks = Array.wrap(attributes[:service_task]).map { |st| ServiceTask.new(st) }
30
+ @script_tasks = Array.wrap(attributes[:script_task]).map { |st| ScriptTask.new(st) }
31
+ @business_rule_tasks = Array.wrap(attributes[:business_rule_task]).map { |brt| BusinessRuleTask.new(brt) }
32
+ @call_activities = Array.wrap(attributes[:call_activity]).map { |ca| CallActivity.new(ca) }
33
+ @sub_processes = Array.wrap(attributes[:sub_process]).map { |sp| SubProcess.new(sp) }
34
+ @ad_hoc_sub_processes = Array.wrap(attributes[:ad_hoc_sub_processe]).map { |ahsp| AdHocSubProcess.new(ahsp) }
35
+ @exclusive_gateways = Array.wrap(attributes[:exclusive_gateway]).map { |eg| ExclusiveGateway.new(eg) }
36
+ @parallel_gateways = Array.wrap(attributes[:parallel_gateway]).map { |pg| ParallelGateway.new(pg) }
37
+ @inclusive_gateways = Array.wrap(attributes[:inclusive_gateway]).map { |ig| InclusiveGateway.new(ig) }
38
+ @event_based_gateways = Array.wrap(attributes[:event_based_gateway]).map { |ebg| EventBasedGateway.new(ebg) }
39
+ @sequence_flows = Array.wrap(attributes[:sequence_flow]).map { |sf| SequenceFlow.new(sf) }
40
+ end
41
+
42
+ def elements
43
+ @elements ||= {}.tap do |elements|
44
+ elements.merge!(start_events.index_by(&:id))
45
+ elements.merge!(end_events.index_by(&:id))
46
+ elements.merge!(intermediate_catch_events.index_by(&:id))
47
+ elements.merge!(intermediate_throw_events.index_by(&:id))
48
+ elements.merge!(boundary_events.index_by(&:id))
49
+ elements.merge!(tasks.index_by(&:id))
50
+ elements.merge!(user_tasks.index_by(&:id))
51
+ elements.merge!(service_tasks.index_by(&:id))
52
+ elements.merge!(script_tasks.index_by(&:id))
53
+ elements.merge!(business_rule_tasks.index_by(&:id))
54
+ elements.merge!(call_activities.index_by(&:id))
55
+ elements.merge!(sub_processes.index_by(&:id))
56
+ elements.merge!(ad_hoc_sub_processes.index_by(&:id))
57
+ elements.merge!(exclusive_gateways.index_by(&:id))
58
+ elements.merge!(parallel_gateways.index_by(&:id))
59
+ elements.merge!(inclusive_gateways.index_by(&:id))
60
+ elements.merge!(event_based_gateways.index_by(&:id))
61
+ elements.merge!(sequence_flows.index_by(&:id))
62
+ end
63
+ end
64
+
65
+ def wire_references(definitions)
66
+ elements.values.each do |element|
67
+ if element.is_a?(Step)
68
+ element.incoming = element.incoming.map { |id| element_by_id(id) }
69
+ element.outgoing = element.outgoing.map { |id| element_by_id(id) }
70
+
71
+ if element.is_a?(Event)
72
+
73
+ element.event_definitions.each do |event_definition|
74
+ if event_definition.is_a?(MessageEventDefinition)
75
+ event_definition.message = definitions.message_by_id(event_definition.message_ref)
76
+ elsif event_definition.is_a?(SignalEventDefinition)
77
+ event_definition.signal = definitions.signal_by_id(event_definition.signal_ref)
78
+ elsif event_definition.is_a?(ErrorEventDefinition)
79
+ event_definition.error = definitions.error_by_id(event_definition.error_ref)
80
+ end
81
+ end
82
+
83
+ if element.is_a?(BoundaryEvent)
84
+ host_element = element_by_id(element.attached_to_ref)
85
+ host_element.attachments << element
86
+ element.attached_to = host_element
87
+ end
88
+
89
+ end
90
+
91
+ if element.is_a?(Gateway)
92
+ element.default = element_by_id(element.default_ref)
93
+ end
94
+
95
+ if element.is_a?(SubProcess)
96
+ element.wire_references(definitions)
97
+ end
98
+
99
+ elsif element.is_a?(SequenceFlow)
100
+ element.source = element_by_id(element.source_ref)
101
+ element.target = element_by_id(element.target_ref)
102
+ end
103
+
104
+ # Not handled participant process ref
105
+ end
106
+ end
107
+
108
+ def element_by_id(id)
109
+ elements[id]
110
+ end
111
+
112
+ def elements_by_type(type)
113
+ elements.select { |e| e.class == type }
114
+ end
115
+
116
+ def default_start_event
117
+ start_events.find { |se| se.event_definitions.empty? }
118
+ end
119
+
120
+ def execute(execution)
121
+ start_event = execution.start_event_id ? element_by_id(execution.start_event_id) : default_start_event
122
+ raise ExecutionErrorNew.new("Process must have at least one start event.") if start_event.blank?
123
+ execution.execute_step(start_event)
124
+ end
125
+
126
+ def inspect
127
+ parts = ["#<#{self.class.name.gsub(/SpotFlow::/, "")} @id=#{id.inspect} @name=#{name.inspect} @is_executable=#{is_executable.inspect}"]
128
+ parts << "@parent=#{parent.inspect}" if parent
129
+ event_attrs = (start_events + intermediate_catch_events + intermediate_throw_events + boundary_events + end_events).compact
130
+ parts << "@events=#{event_attrs.inspect}" unless event_attrs.blank?
131
+ activity_attrs = (tasks + user_tasks + service_tasks + script_tasks + business_rule_tasks + call_activities).compact
132
+ parts << "@activities=#{activity_attrs.inspect}" unless activity_attrs.blank?
133
+ gateway_attrs = (exclusive_gateways + parallel_gateways + inclusive_gateways + event_based_gateways).compact
134
+ parts << "@gateways=#{gateway_attrs.inspect}" unless gateway_attrs.blank?
135
+ sub_process_attrs = (sub_processes + ad_hoc_sub_processes).compact
136
+ parts << "@sub_processes=#{sub_process_attrs.inspect}" unless sub_process_attrs.blank?
137
+ parts << "@sequence_flows=#{sequence_flows.inspect}" unless sequence_flows.blank?
138
+ parts.join(" ") + ">"
139
+ end
140
+ end
141
+
142
+ class SubProcess < Process
143
+ attr_accessor :triggered_by_event
144
+
145
+ def initialize(attributes = {})
146
+ super(attributes.except(:triggered_by_event))
147
+
148
+ @is_executable = false
149
+ @sub_processes = []
150
+ @triggered_by_event = attributes[:triggered_by_event]
151
+ end
152
+
153
+ def execution_ended(execution)
154
+ leave(execution)
155
+ end
156
+ end
157
+
158
+ class AdHocSubProcess < SubProcess
159
+
160
+ end
161
+
162
+ class CallActivity < Activity
163
+ attr_reader :process_id
164
+
165
+ def execute(execution)
166
+ if extension_elements&.called_element&.process_id&.start_with?("=")
167
+ @process_id = SpotFeel.evaluate(extension_elements&.called_element&.process_id, variables: execution.variables)
168
+ else
169
+ @process_id = extension_elements&.called_element&.process_id
170
+ end
171
+
172
+ execution.wait
173
+
174
+ process = execution.context.process_by_id(@process_id) if @process_id
175
+ execution.execute_step(process.default_start_event) if process
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpotFlow
4
+ module Bpmn
5
+ class Step < Element
6
+ attr_accessor :incoming, :outgoing, :default, :default_ref
7
+
8
+ def initialize(attributes = {})
9
+ super(attributes.except(:incoming, :outgoing, :default))
10
+
11
+ @incoming = Array.wrap(attributes[:incoming]) || []
12
+ @outgoing = Array.wrap(attributes[:outgoing]) || []
13
+ @default_ref = attributes[:default]
14
+ end
15
+
16
+ def diverging?
17
+ outgoing.length > 1
18
+ end
19
+
20
+ def converging?
21
+ incoming.length > 1
22
+ end
23
+
24
+ def leave(execution)
25
+ execution.end(false)
26
+ execution.take_all(outgoing_flows(execution))
27
+ end
28
+
29
+ def outgoing_flows(execution)
30
+ flows = []
31
+ outgoing.each do |flow|
32
+ result = flow.evaluate(execution) unless default&.id == flow.id
33
+ flows.push flow if result
34
+ end
35
+ flows = [default] if flows.empty? && default
36
+ return flows
37
+ end
38
+
39
+ def input_mappings
40
+ extension_elements&.io_mapping&.inputs || []
41
+ end
42
+
43
+ def output_mappings
44
+ extension_elements&.io_mapping&.outputs || []
45
+ end
46
+ end
47
+
48
+ class Activity < Step
49
+ attr_accessor :attachments
50
+
51
+ def initialize(attributes = {})
52
+ super(attributes.except(:attachments))
53
+
54
+ @attachments = []
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpotFlow
4
+ module Bpmn
5
+ class Task < Activity
6
+
7
+ def is_automated?
8
+ false
9
+ end
10
+
11
+ def is_manual?
12
+ true
13
+ end
14
+
15
+ def execute(execution)
16
+ execution.wait
17
+ end
18
+
19
+ def signal(execution)
20
+ leave(execution)
21
+ end
22
+
23
+ def result_to_variables(result)
24
+ if result_variable
25
+ return { "#{result_variable}": result }
26
+ else
27
+ if result.is_a? Hash
28
+ result
29
+ else
30
+ {}.tap { |h| h[id.underscore] = result }
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ class UserTask < Task
37
+
38
+ def form_key
39
+ extension_elements&.form_definition&.form_key
40
+ end
41
+
42
+ def assignee
43
+ extension_elements&.assignment_definition&.assignee
44
+ end
45
+
46
+ def candidate_groups
47
+ extension_elements&.assignment_definition&.candidate_groups
48
+ end
49
+
50
+ def candidate_users
51
+ extension_elements&.assignment_definition&.candidate_users
52
+ end
53
+
54
+ def due_date
55
+ extension_elements&.task_schedule&.due_date
56
+ end
57
+
58
+ def follow_up_date
59
+ extension_elements&.task_schedule&.follow_up_date
60
+ end
61
+ end
62
+
63
+ class ServiceTask < Task
64
+ attr_accessor :service
65
+
66
+ def is_automated?
67
+ true
68
+ end
69
+
70
+ def is_manual?
71
+ false
72
+ end
73
+
74
+ def execute(execution)
75
+ execution.wait
76
+ end
77
+
78
+ def task_type
79
+ extension_elements&.task_definition&.type
80
+ end
81
+
82
+ def task_retries
83
+ extension_elements&.task_definition&.retries
84
+ end
85
+
86
+ def headers
87
+ extension_elements&.task_headers&.headers
88
+ end
89
+
90
+ def run(execution)
91
+ if defined?(task_type)
92
+ klass = task_type.constantize
93
+ klass.new.call(execution.parent.variables, headers || {})
94
+ end
95
+ end
96
+ end
97
+
98
+ class ScriptTask < ServiceTask
99
+
100
+ def script
101
+ extension_elements&.script&.expression
102
+ end
103
+
104
+ def result_variable
105
+ extension_elements&.script&.result_variable
106
+ end
107
+
108
+ def run(execution)
109
+ SpotFeel.evaluate(script.delete_prefix("="), variables: execution.parent.variables)
110
+ end
111
+ end
112
+
113
+ class BusinessRuleTask < ServiceTask
114
+
115
+ def decision_id
116
+ extension_elements&.called_decision&.decision_id
117
+ end
118
+
119
+ def result_variable
120
+ extension_elements&.called_decision&.result_variable
121
+ end
122
+
123
+ def run(execution)
124
+ SpotFeel.decide(decision_id, definitions: execution.context.dmn_definitions_by_decision_id(decision_id), variables: execution.parent.variables)
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "bpmn/element"
4
+ require_relative "bpmn/extensions"
5
+ require_relative "bpmn/extension_elements"
6
+ require_relative "bpmn/step"
7
+ require_relative "bpmn/flow"
8
+ require_relative "bpmn/event_definition"
9
+ require_relative "bpmn/event"
10
+ require_relative "bpmn/gateway"
11
+ require_relative "bpmn/task"
12
+ require_relative "bpmn/process"
13
+ require_relative "bpmn/definitions"
14
+
15
+ module SpotFlow
16
+ module Bpmn
17
+ end
18
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpotFlow
4
+ class Context
5
+ attr_reader :sources, :processes, :decisions, :services, :executions
6
+ attr_reader :bpmn_definitions, :dmn_definitions
7
+
8
+ def initialize(sources = [], processes:[], decisions:[], services: {})
9
+ @sources = Array.wrap(sources)
10
+ @processes = Array.wrap(processes)
11
+ @bpmn_definitions = []
12
+ @dmn_definitions = []
13
+ @decisions = Array.wrap(decisions)
14
+ @services = HashWithIndifferentAccess.new((SpotFlow.config.services || {}).merge(services))
15
+
16
+ @sources.each do |source|
17
+ if source.include?("http://www.omg.org/spec/DMN/20180521/DC/")
18
+ definitions = SpotFeel.definitions_from_xml(source)
19
+ @dmn_definitions << definitions
20
+ @decisions += definitions.decisions
21
+ else
22
+ @processes += SpotFlow.processes_from_xml(source)
23
+ end
24
+ end
25
+
26
+ @executions = []
27
+ end
28
+
29
+ def start(process_id: nil, start_event_id: nil, variables: {})
30
+ process = process_id ? process_by_id(process_id) : default_process
31
+ raise ExecutionError.new(process_id ? "Process #{process_id} not found." : "No default process found.") if process.blank?
32
+ execution = Execution.start(context: self, process: process, start_event_id: start_event_id, variables: variables)
33
+ executions << execution
34
+ execution
35
+ end
36
+
37
+ def start_with_message(message_name:, variables: {})
38
+ [].tap do |executions|
39
+ processes.map do |process|
40
+ process.start_events.map do |start_event|
41
+ start_event.message_event_definitions.map do |message_event_definition|
42
+ if message_name == message_event_definition.message_name
43
+ Execution.start(context: self, process: process, variables: variables, start_event_id: start_event.id).tap { |execution| executions.push execution }
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ def restore(execution_state)
52
+ Execution.deserialize(execution_state, context: self).tap do |execution|
53
+ executions << execution
54
+ end
55
+ end
56
+
57
+ def notify_listener(*args)
58
+ SpotFlow.config.listener&.call(args)
59
+ end
60
+
61
+ def default_process
62
+ raise "Multiple processes defined, must identify process" if processes.size > 1
63
+ processes.first
64
+ end
65
+
66
+ def process_by_id(id)
67
+ processes.each do |process|
68
+ return process if process.id == id
69
+ process.sub_processes.each do |sub_process|
70
+ return sub_process if sub_process.id == id
71
+ end
72
+ end
73
+ nil
74
+ end
75
+
76
+ def element_by_id(id)
77
+ processes.each do |process|
78
+ element = process.element_by_id(id)
79
+ return element if element
80
+ end
81
+ nil
82
+ end
83
+
84
+ def execution_by_id(id)
85
+ executions.find { |e| e.id == id }
86
+ end
87
+
88
+ def execution_by_step_id(step_id)
89
+ executions.find { |e| e.step.id == step_id }
90
+ end
91
+
92
+ def decision_by_id(id)
93
+ decisions.find { |d| d.id == id }
94
+ end
95
+
96
+ def dmn_definitions_by_decision_id(decision_id)
97
+ dmn_definitions.find { |definitions| definitions.decisions.find { |decision| decision.id == decision_id } }
98
+ end
99
+
100
+ def inspect
101
+ parts = ["#<Context"]
102
+ parts << "@processes=#{processes.inspect}" if processes.present?
103
+ parts << "@decisions=#{decisions.inspect}" if decisions.present?
104
+ parts << "@executions=#{executions.inspect}" if executions.present?
105
+ parts.join(" ") + ">"
106
+ end
107
+ end
108
+ end