bpmn 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -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
@@ -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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BPMN
4
+ VERSION = "0.0.2"
5
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spot_flow/version"
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 "spot_feel"
12
+ require "dmn"
13
13
 
14
- require "spot_flow/bpmn"
15
- require "spot_flow/context"
16
- require "spot_flow/execution"
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
- module SpotFlow
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
- Bpmn::Definitions.from_xml(xml)&.processes || []
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 = SpotFeel.definitions_from_xml(xml)
57
+ definitions = DMN.definitions_from_xml(xml)
47
58
  definitions.decisions
48
59
  end
49
60
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bpmn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Connected Bits
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-12-30 00:00:00.000000000 Z
11
+ date: 2024-12-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -189,22 +189,21 @@ files:
189
189
  - MIT-LICENSE
190
190
  - README.md
191
191
  - Rakefile
192
- - lib/spot_flow.rb
193
- - lib/spot_flow/bpmn.rb
194
- - lib/spot_flow/bpmn/definitions.rb
195
- - lib/spot_flow/bpmn/element.rb
196
- - lib/spot_flow/bpmn/event.rb
197
- - lib/spot_flow/bpmn/event_definition.rb
198
- - lib/spot_flow/bpmn/extension_elements.rb
199
- - lib/spot_flow/bpmn/extensions.rb
200
- - lib/spot_flow/bpmn/flow.rb
201
- - lib/spot_flow/bpmn/gateway.rb
202
- - lib/spot_flow/bpmn/process.rb
203
- - lib/spot_flow/bpmn/step.rb
204
- - lib/spot_flow/bpmn/task.rb
205
- - lib/spot_flow/context.rb
206
- - lib/spot_flow/execution.rb
207
- - lib/spot_flow/version.rb
192
+ - lib/bpmn.rb
193
+ - lib/bpmn/context.rb
194
+ - lib/bpmn/definitions.rb
195
+ - lib/bpmn/element.rb
196
+ - lib/bpmn/event.rb
197
+ - lib/bpmn/event_definition.rb
198
+ - lib/bpmn/execution.rb
199
+ - lib/bpmn/extension_elements.rb
200
+ - lib/bpmn/extensions.rb
201
+ - lib/bpmn/flow.rb
202
+ - lib/bpmn/gateway.rb
203
+ - lib/bpmn/process.rb
204
+ - lib/bpmn/step.rb
205
+ - lib/bpmn/task.rb
206
+ - lib/bpmn/version.rb
208
207
  homepage: https://www.connectedbits.com
209
208
  licenses:
210
209
  - MIT
@@ -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