trailblazer-developer 0.0.21 → 0.0.24
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/.github/workflows/ci.yml +17 -0
- data/CHANGES.md +13 -0
- data/Gemfile +4 -6
- data/README.md +8 -5
- data/lib/trailblazer/developer/render/linear.rb +4 -4
- data/lib/trailblazer/developer/trace/focusable.rb +0 -2
- data/lib/trailblazer/developer/trace.rb +9 -3
- data/lib/trailblazer/developer/version.rb +1 -1
- data/lib/trailblazer/developer.rb +0 -3
- data/trailblazer-developer.gemspec +2 -4
- metadata +9 -40
- data/.travis.yml +0 -9
- data/lib/trailblazer/developer/client.rb +0 -95
- data/lib/trailblazer/developer/generate.rb +0 -98
- data/lib/trailblazer/diagram/bpmn.rb +0 -338
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23a6eb4a9f753e270064c377f75c08045ffcfffe1ee4643c82b52a59df0e38c6
|
4
|
+
data.tar.gz: 48f8e2b81baff35b7545deb747fa50e09dea67ea4eca1c55af5566a04edf917d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ec36ea0cfe3b6bafe10ab4029651dfa13db4557bad3fd121fae6e1d28629551f1d747793ad93f95a20a7eabc918254e929ce53eaac7337eb9a3d5db255961da
|
7
|
+
data.tar.gz: 3586db8c45fe02f4ccb99199690916e1ac6f208afe0a9f2a2d12bf5dcccccaae2b20bc65da32ef830bf39661276899fc94e4d5250879c7e02af3a2658be96e7d
|
@@ -0,0 +1,17 @@
|
|
1
|
+
name: CI
|
2
|
+
on: [push, pull_request]
|
3
|
+
jobs:
|
4
|
+
test:
|
5
|
+
strategy:
|
6
|
+
fail-fast: false
|
7
|
+
matrix:
|
8
|
+
# Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0'
|
9
|
+
ruby: [2.5, 2.6, 2.7, '3.0', head, jruby]
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
steps:
|
12
|
+
- uses: actions/checkout@v2
|
13
|
+
- uses: ruby/setup-ruby@v1
|
14
|
+
with:
|
15
|
+
ruby-version: ${{ matrix.ruby }}
|
16
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
17
|
+
- run: bundle exec rake
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
# 0.0.24
|
2
|
+
|
3
|
+
* Use `trailblazer-activity-dsl-linear` >= 1.0.
|
4
|
+
|
5
|
+
# 0.0.23
|
6
|
+
|
7
|
+
* Remove `representable` dependency.
|
8
|
+
* Moved all editor-specific code to the `pro` gem (`client.rb` and `generate.rb`).
|
9
|
+
|
10
|
+
# 0.0.22
|
11
|
+
|
12
|
+
* Upgrade trb-activity, trb-activity-dsl-linear and representable versions.
|
13
|
+
|
1
14
|
# 0.0.21
|
2
15
|
|
3
16
|
* Ruby 3.0 support. :sunset:
|
data/Gemfile
CHANGED
@@ -4,9 +4,7 @@ source "https://rubygems.org"
|
|
4
4
|
gemspec
|
5
5
|
|
6
6
|
# gem "trailblazer-activity", ">= 0.7.1"
|
7
|
-
gem "trailblazer-activity", path: "../trailblazer-activity"
|
8
|
-
gem "trailblazer-
|
9
|
-
gem "trailblazer-activity-dsl-linear",
|
10
|
-
|
11
|
-
gem "faraday"
|
12
|
-
gem "multi_json"
|
7
|
+
# gem "trailblazer-activity", path: "../trailblazer-activity"
|
8
|
+
# gem "trailblazer-activity-dsl-linear", path: "../trailblazer-activity-dsl-linear"
|
9
|
+
gem "trailblazer-activity-dsl-linear", github: "trailblazer/trailblazer-activity-dsl-linear"
|
10
|
+
gem "trailblazer-activity", github: "trailblazer/trailblazer-activity"
|
data/README.md
CHANGED
@@ -10,8 +10,11 @@ Find the complete documentation on the project website: [https://trailblazer.to/
|
|
10
10
|
|
11
11
|
The `developer` gem provides the following neat tools.
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
## `wtf?` mode™
|
14
|
+
|
15
|
+
Run an activity and see its flow on the command line. Or - run a _broken_ activity and trace where an exception happened.
|
16
|
+
|
17
|
+

|
18
|
+
|
19
|
+
|
20
|
+
## Quick rendering of activities, including their taskWraps.
|
@@ -6,15 +6,15 @@ module Trailblazer
|
|
6
6
|
# NOTE: this is absolutely to be considered as prototyping and acts more like a test helper ATM as
|
7
7
|
|
8
8
|
module Developer
|
9
|
-
def self.railway(*args)
|
10
|
-
Render::Linear.(*args)
|
9
|
+
def self.railway(*args, **kws)
|
10
|
+
Render::Linear.(*args, **kws)
|
11
11
|
end
|
12
12
|
|
13
13
|
module Render
|
14
14
|
module Linear
|
15
15
|
module_function
|
16
16
|
|
17
|
-
def call(operation,
|
17
|
+
def call(operation, style: :line)
|
18
18
|
graph = Activity::Introspect::Graph(operation)
|
19
19
|
|
20
20
|
rows = graph.collect do |node, i|
|
@@ -27,7 +27,7 @@ module Trailblazer
|
|
27
27
|
|
28
28
|
rows = rows[1..-1] # remove start
|
29
29
|
|
30
|
-
return inspect_line(rows) if
|
30
|
+
return inspect_line(rows) if style == :line
|
31
31
|
|
32
32
|
return inspect_rows(rows)
|
33
33
|
end
|
@@ -43,9 +43,15 @@ module Trailblazer::Developer
|
|
43
43
|
#
|
44
44
|
# @private
|
45
45
|
def merge_plan
|
46
|
-
Activity::TaskWrap::
|
47
|
-
|
48
|
-
|
46
|
+
Activity::TaskWrap::Extension.new(
|
47
|
+
{
|
48
|
+
insert: [Activity::Adds::Insert.method(:Prepend), "task_wrap.call_task"],
|
49
|
+
row: Activity::TaskWrap::Pipeline.Row("task_wrap.capture_args", Trace.method(:capture_args))
|
50
|
+
},
|
51
|
+
{
|
52
|
+
insert: [Activity::Adds::Insert.method(:Append)], # append to the very end of tW.
|
53
|
+
row: Activity::TaskWrap::Pipeline.Row("task_wrap.capture_return", Trace.method(:capture_return))
|
54
|
+
},
|
49
55
|
)
|
50
56
|
end
|
51
57
|
|
@@ -12,8 +12,5 @@ require "trailblazer/developer/trace"
|
|
12
12
|
require "trailblazer/developer/trace/present"
|
13
13
|
require "trailblazer/developer/trace/focusable"
|
14
14
|
require "trailblazer/developer/trace/inspector"
|
15
|
-
require "trailblazer/developer/generate"
|
16
15
|
require "trailblazer/developer/render/circuit"
|
17
16
|
require "trailblazer/developer/render/linear"
|
18
|
-
|
19
|
-
# require "trailblazer/developer/client"
|
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.email = ["apotonick@gmail.com"]
|
10
10
|
|
11
11
|
spec.summary = "Developer tools for Trailblazer."
|
12
|
-
spec.description = "Developer tools for Trailblazer: debugger,
|
12
|
+
spec.description = "Developer tools for Trailblazer: debugger, activity visualizer and tracing."
|
13
13
|
spec.homepage = "http://trailblazer.to"
|
14
14
|
spec.license = "LGPL-3.0"
|
15
15
|
|
@@ -23,8 +23,6 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.add_development_dependency "minitest-line"
|
24
24
|
spec.add_development_dependency "rake"
|
25
25
|
|
26
|
-
spec.add_dependency "trailblazer-activity", ">= 0.
|
27
|
-
spec.add_dependency "trailblazer-activity-dsl-linear"
|
28
|
-
spec.add_dependency "representable"
|
26
|
+
spec.add_dependency "trailblazer-activity-dsl-linear", ">= 1.0.0", "< 1.1.0"
|
29
27
|
spec.add_dependency "hirb"
|
30
28
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trailblazer-developer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.24
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-07-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -67,53 +67,25 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name: trailblazer-activity
|
70
|
+
name: trailblazer-activity-dsl-linear
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: 0.11.0
|
76
|
-
- - "<"
|
77
74
|
- !ruby/object:Gem::Version
|
78
75
|
version: 1.0.0
|
79
|
-
type: :runtime
|
80
|
-
prerelease: false
|
81
|
-
version_requirements: !ruby/object:Gem::Requirement
|
82
|
-
requirements:
|
83
|
-
- - ">="
|
84
|
-
- !ruby/object:Gem::Version
|
85
|
-
version: 0.11.0
|
86
76
|
- - "<"
|
87
77
|
- !ruby/object:Gem::Version
|
88
|
-
version: 1.
|
89
|
-
- !ruby/object:Gem::Dependency
|
90
|
-
name: trailblazer-activity-dsl-linear
|
91
|
-
requirement: !ruby/object:Gem::Requirement
|
92
|
-
requirements:
|
93
|
-
- - ">="
|
94
|
-
- !ruby/object:Gem::Version
|
95
|
-
version: '0'
|
78
|
+
version: 1.1.0
|
96
79
|
type: :runtime
|
97
80
|
prerelease: false
|
98
81
|
version_requirements: !ruby/object:Gem::Requirement
|
99
82
|
requirements:
|
100
83
|
- - ">="
|
101
84
|
- !ruby/object:Gem::Version
|
102
|
-
version:
|
103
|
-
-
|
104
|
-
name: representable
|
105
|
-
requirement: !ruby/object:Gem::Requirement
|
106
|
-
requirements:
|
107
|
-
- - ">="
|
108
|
-
- !ruby/object:Gem::Version
|
109
|
-
version: '0'
|
110
|
-
type: :runtime
|
111
|
-
prerelease: false
|
112
|
-
version_requirements: !ruby/object:Gem::Requirement
|
113
|
-
requirements:
|
114
|
-
- - ">="
|
85
|
+
version: 1.0.0
|
86
|
+
- - "<"
|
115
87
|
- !ruby/object:Gem::Version
|
116
|
-
version:
|
88
|
+
version: 1.1.0
|
117
89
|
- !ruby/object:Gem::Dependency
|
118
90
|
name: hirb
|
119
91
|
requirement: !ruby/object:Gem::Requirement
|
@@ -128,15 +100,15 @@ dependencies:
|
|
128
100
|
- - ">="
|
129
101
|
- !ruby/object:Gem::Version
|
130
102
|
version: '0'
|
131
|
-
description: 'Developer tools for Trailblazer: debugger,
|
103
|
+
description: 'Developer tools for Trailblazer: debugger, activity visualizer and tracing.'
|
132
104
|
email:
|
133
105
|
- apotonick@gmail.com
|
134
106
|
executables: []
|
135
107
|
extensions: []
|
136
108
|
extra_rdoc_files: []
|
137
109
|
files:
|
110
|
+
- ".github/workflows/ci.yml"
|
138
111
|
- ".gitignore"
|
139
|
-
- ".travis.yml"
|
140
112
|
- CHANGES.md
|
141
113
|
- Gemfile
|
142
114
|
- LICENSE
|
@@ -146,8 +118,6 @@ files:
|
|
146
118
|
- bin/setup
|
147
119
|
- gems.local.rb
|
148
120
|
- lib/trailblazer/developer.rb
|
149
|
-
- lib/trailblazer/developer/client.rb
|
150
|
-
- lib/trailblazer/developer/generate.rb
|
151
121
|
- lib/trailblazer/developer/render/circuit.rb
|
152
122
|
- lib/trailblazer/developer/render/linear.rb
|
153
123
|
- lib/trailblazer/developer/trace.rb
|
@@ -157,7 +127,6 @@ files:
|
|
157
127
|
- lib/trailblazer/developer/version.rb
|
158
128
|
- lib/trailblazer/developer/wtf.rb
|
159
129
|
- lib/trailblazer/developer/wtf/renderer.rb
|
160
|
-
- lib/trailblazer/diagram/bpmn.rb
|
161
130
|
- trailblazer-developer.gemspec
|
162
131
|
homepage: http://trailblazer.to
|
163
132
|
licenses:
|
data/.travis.yml
DELETED
@@ -1,95 +0,0 @@
|
|
1
|
-
require "faraday"
|
2
|
-
require "base64"
|
3
|
-
require "json"
|
4
|
-
require "representable/json"
|
5
|
-
|
6
|
-
module Trailblazer::Developer
|
7
|
-
module Client
|
8
|
-
Diagram = Struct.new(:id, :body)
|
9
|
-
|
10
|
-
class Diagram::Representer < Representable::Decorator
|
11
|
-
include Representable::JSON
|
12
|
-
property :id
|
13
|
-
property :body, as: :diagram
|
14
|
-
end
|
15
|
-
|
16
|
-
module_function
|
17
|
-
|
18
|
-
def Diagram(id, body)
|
19
|
-
Diagram.new(id, body).freeze
|
20
|
-
end
|
21
|
-
|
22
|
-
def import(id:, query:"", **options)
|
23
|
-
token = retrieve_token(**options)
|
24
|
-
export_diagram(id: id, token: token, query: query, **options)
|
25
|
-
end
|
26
|
-
|
27
|
-
def retrieve_token(email:, api_key:, url: "/signin", **options)
|
28
|
-
body = JSON.generate({email: email, api_key: api_key})
|
29
|
-
|
30
|
-
response = request(token: nil, method: :get, url: url, body: body, **options)
|
31
|
-
return false unless response.status == 200
|
32
|
-
|
33
|
-
# token = CGI::Cookie.parse(response.headers["set-cookie"])["token"][0]
|
34
|
-
JSON.parse(response.body)["token"]
|
35
|
-
end
|
36
|
-
|
37
|
-
def export_diagram(id:, query:, **options)
|
38
|
-
response = request(body: nil, url: "/api/v1/diagrams/#{id}/export#{query}", method: :get, **options)
|
39
|
-
|
40
|
-
# parse_response(response)
|
41
|
-
response.body
|
42
|
-
end
|
43
|
-
|
44
|
-
def duplicate(id:, **options)
|
45
|
-
token = retrieve_token(**options)
|
46
|
-
|
47
|
-
response = request(body: nil, token: token, url: "/api/v1/diagrams/#{id}/duplicate", method: :get, **options)
|
48
|
-
parse_response(response)
|
49
|
-
end
|
50
|
-
|
51
|
-
# DISCUSS: do we need that?
|
52
|
-
def new_diagram(token:, **options)
|
53
|
-
response = request(body: nil, url: "/api/v1/diagrams/new", method: :get, token: token, **options)
|
54
|
-
|
55
|
-
# TODO: use Dry::Struct
|
56
|
-
# TODO: handle unauthorized/errors
|
57
|
-
parse_response(response)
|
58
|
-
end
|
59
|
-
|
60
|
-
def request(host:, url:, method:, token:, body:, **)
|
61
|
-
conn = Faraday.new(url: host)
|
62
|
-
|
63
|
-
conn.send(method) do |req|
|
64
|
-
req.url url
|
65
|
-
req.headers["Content-Type"] = "application/json"
|
66
|
-
req.body = body
|
67
|
-
req.headers["Authorization"] = token
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def parse_response(response)
|
72
|
-
diagram = Diagram.new
|
73
|
-
Diagram::Representer.new(diagram).from_json(response.body) # a parsed hash would be cooler?
|
74
|
-
|
75
|
-
diagram
|
76
|
-
end
|
77
|
-
|
78
|
-
# TODO: remove me!
|
79
|
-
def self.push(operation:, name:)
|
80
|
-
xml = Trailblazer::Diagram::BPMN.to_xml(operation["__activity__"], operation["__sequence__"].map(&:id))
|
81
|
-
token = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJpZCI6MywidXNlcm5hbWUiOiJhcG90b25pY2siLCJlbWFpbCI6Im5pY2tAdHJhaWxibGF6ZXIudG8ifQ." # rubocop:disable Metrics/LineLength
|
82
|
-
conn = Faraday.new(url: "https://api.trb.to")
|
83
|
-
response = conn.post do |req|
|
84
|
-
req.url "/dev/v1/import"
|
85
|
-
req.headers["Content-Type"] = "application/json"
|
86
|
-
req.headers["Authorization"] = token
|
87
|
-
require "base64"
|
88
|
-
|
89
|
-
req.body = %({ "name": "#{name}", "xml":"#{Base64.strict_encode64(xml)}" })
|
90
|
-
end
|
91
|
-
|
92
|
-
puts response.status.inspect
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
@@ -1,98 +0,0 @@
|
|
1
|
-
require "representable/hash"
|
2
|
-
require "trailblazer/activity/dsl/linear" # Railway.
|
3
|
-
|
4
|
-
module Trailblazer
|
5
|
-
module Developer
|
6
|
-
# Computes an {Intermediate} data structure from a TRB-editor.js file.
|
7
|
-
module Generate
|
8
|
-
module_function
|
9
|
-
|
10
|
-
Element = Struct.new(:id, :type, :linksTo, :data, :label, :parent)
|
11
|
-
Arrow = Struct.new(:target, :label, :message, :target_lane)
|
12
|
-
|
13
|
-
module Representer
|
14
|
-
class Activity < Representable::Decorator
|
15
|
-
include Representable::Hash
|
16
|
-
|
17
|
-
collection :elements, class: Element do
|
18
|
-
property :id
|
19
|
-
property :type
|
20
|
-
collection :linksTo, class: Arrow, default: ::Declarative::Variables::Append([]) do
|
21
|
-
property :target
|
22
|
-
property :label
|
23
|
-
property :message
|
24
|
-
property :target_lane
|
25
|
-
end
|
26
|
-
property :data, default: {}
|
27
|
-
|
28
|
-
property :label
|
29
|
-
property :parent # TODO: remove?
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def call(hash)
|
35
|
-
_, (ctx, _) = Activity::TaskWrap.invoke(Pipeline, [{hash: hash}, {}])
|
36
|
-
ctx[:intermediate]
|
37
|
-
end
|
38
|
-
|
39
|
-
def transform_from_hash(ctx, hash:, parser: Representer::Activity, **)
|
40
|
-
ctx[:elements] = parser.new(OpenStruct.new).from_hash(hash).elements
|
41
|
-
end
|
42
|
-
|
43
|
-
def find_start_events(ctx, elements:, **)
|
44
|
-
ctx[:start_events] = elements.find_all { |el| el.type == "Event" }
|
45
|
-
end
|
46
|
-
|
47
|
-
def compute_intermediate(ctx, elements:, start_events:, **)
|
48
|
-
end_events = elements.find_all { |el| el.type == "EndEventTerminate" } # DISCUSS: is it really called TERMINATE?
|
49
|
-
|
50
|
-
inter = Activity::Schema::Intermediate
|
51
|
-
|
52
|
-
wiring = elements.collect { |el|
|
53
|
-
data = data_for(el)
|
54
|
-
|
55
|
-
[inter.TaskRef(el.id, data), el.linksTo.collect { |arrow| inter.Out(semantic_for(**arrow.to_h), arrow.target) } ] }
|
56
|
-
wiring = Hash[wiring]
|
57
|
-
|
58
|
-
# end events need this stupid special handling
|
59
|
-
# DISCUSS: currently, the END-SEMANTIC is read from the event's label.
|
60
|
-
wiring = wiring.merge(Hash[
|
61
|
-
end_events.collect do |_end|
|
62
|
-
ref, = wiring.find { |ref, _| ref.id == _end.id }
|
63
|
-
|
64
|
-
[ref, [inter.Out(semantic_for(**_end.to_h)|| raise, nil)]] # TODO: test the raise, happens when the semantic of an End can't be distinguished. # TODO: don't extract semantic from :label but from :data.
|
65
|
-
end
|
66
|
-
])
|
67
|
-
# pp wiring
|
68
|
-
|
69
|
-
ctx[:intermediate] = inter.new(wiring, end_events.collect(&:id), start_events.collect(&:id))
|
70
|
-
end
|
71
|
-
|
72
|
-
# private
|
73
|
-
|
74
|
-
def data_for(element)
|
75
|
-
{type: element.type}.merge(element.data)
|
76
|
-
end
|
77
|
-
|
78
|
-
# We currently use the {:label} field of an arrow to encode an output semantic.
|
79
|
-
# The {:symbol_style} part will be filtered out as semantic. Defaults to {:success}.
|
80
|
-
def semantic_for(label:nil, **)
|
81
|
-
return :success unless label
|
82
|
-
|
83
|
-
extract_semantic(label)
|
84
|
-
end
|
85
|
-
|
86
|
-
def extract_semantic(label)
|
87
|
-
label.to_sym
|
88
|
-
end
|
89
|
-
|
90
|
-
class Pipeline < Trailblazer::Activity::Railway
|
91
|
-
step Generate.method(:transform_from_hash), id: :transform_from_hash
|
92
|
-
step Generate.method(:find_start_events), id: :find_start_events
|
93
|
-
step Generate.method(:compute_intermediate), id: :compute_intermediate
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
# [Inter::Out(:success, nil)]
|
@@ -1,338 +0,0 @@
|
|
1
|
-
require "representable"
|
2
|
-
require "representable/xml"
|
3
|
-
|
4
|
-
require "trailblazer/developer/activity"
|
5
|
-
|
6
|
-
module Trailblazer
|
7
|
-
module Diagram
|
8
|
-
module BPMN # rubocop:disable Metrics/ModuleLength
|
9
|
-
Plane = Struct.new(:element, :shapes, :edges)
|
10
|
-
Shape = Struct.new(:id, :element, :bounds)
|
11
|
-
Edge = Struct.new(:id, :element, :waypoints)
|
12
|
-
Bounds = Struct.new(:x, :y, :width, :height)
|
13
|
-
Waypoint = Struct.new(:x, :y)
|
14
|
-
|
15
|
-
require "tsort"
|
16
|
-
# Helps sorting the tasks in a process "topologically", which is basically what the
|
17
|
-
# Sequence does for us, but this works for any kind of process.
|
18
|
-
# DISCUSS: should we work on the Model or Graph interface?
|
19
|
-
def self.topological_sort(model)
|
20
|
-
edges = {}
|
21
|
-
model.end_events.each { |task| edges[task.id] = {} }
|
22
|
-
model.sequence_flow.each do |edge|
|
23
|
-
edges[edge.sourceRef] ||= []
|
24
|
-
edges[edge.sourceRef] << edge.targetRef
|
25
|
-
end
|
26
|
-
|
27
|
-
# g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
|
28
|
-
each_node = ->(&b) { edges.each_key(&b) }
|
29
|
-
each_child = ->(n, &b) { edges[n].each(&b) }
|
30
|
-
TSort.tsort(each_node, each_child).reverse #=> [4, 2, 3, 1]
|
31
|
-
end
|
32
|
-
|
33
|
-
# FIXME: this should be called "linear layouter or something"
|
34
|
-
# Render an `Activity`'s circuit to a BPMN 2.0 XML `<process>` structure.
|
35
|
-
# @param activity Activity
|
36
|
-
# @param linear_task_ids [String] A list of task IDs that should be layouted sequentially in the provided order.
|
37
|
-
def self.to_xml(activity, linear_task_ids = nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
38
|
-
model = Trailblazer::Developer::Activity::Graph.to_model(activity.graph)
|
39
|
-
|
40
|
-
linear_task_ids ||= topological_sort(model)
|
41
|
-
|
42
|
-
# this layouter doesn't want End events in the linear part, we arrange them manually.
|
43
|
-
linear_task_ids -= model.end_events.map(&:id)
|
44
|
-
linear_task_ids -= model.start_events.map(&:id)
|
45
|
-
linear_tasks = linear_task_ids.collect do |id|
|
46
|
-
model.task.find { |task| task.id == id } || raise("task #{id} is not in model!")
|
47
|
-
end
|
48
|
-
|
49
|
-
start_x = 200
|
50
|
-
y_right = 200
|
51
|
-
y_left = 300
|
52
|
-
|
53
|
-
event_width = 54
|
54
|
-
|
55
|
-
shape_width = 81
|
56
|
-
shape_height = 54
|
57
|
-
shape_to_shape = 45
|
58
|
-
|
59
|
-
current = start_x
|
60
|
-
shapes = []
|
61
|
-
|
62
|
-
# add start.
|
63
|
-
shapes << Shape.new(
|
64
|
-
"Shape_#{model.start_events[0][:id]}",
|
65
|
-
model.start_events[0][:id],
|
66
|
-
Bounds.new(current, y_right, event_width, event_width)
|
67
|
-
)
|
68
|
-
current += event_width + shape_to_shape
|
69
|
-
|
70
|
-
# add tasks.
|
71
|
-
linear_tasks.each do |task|
|
72
|
-
is_right = %i[pass step].include?(task.options[:created_by])
|
73
|
-
|
74
|
-
shapes << Shape.new(
|
75
|
-
"Shape_#{task[:id]}",
|
76
|
-
task[:id],
|
77
|
-
Bounds.new(current, is_right ? y_right : y_left, shape_width, shape_height)
|
78
|
-
)
|
79
|
-
current += shape_width + shape_to_shape
|
80
|
-
end
|
81
|
-
|
82
|
-
# add ends.
|
83
|
-
horizontal_end_offset = 90
|
84
|
-
|
85
|
-
defaults = {
|
86
|
-
"End.success" => {y: y_right},
|
87
|
-
"End.failure" => {y: y_left},
|
88
|
-
"End.pass_fast" => {y: y_right - 90},
|
89
|
-
"End.fail_fast" => {y: y_left + 90}
|
90
|
-
}
|
91
|
-
|
92
|
-
success_end_events = []
|
93
|
-
failure_end_events = [] # rubocop:disable Lint/UselessAssignment
|
94
|
-
|
95
|
-
model.end_events.each do |evt|
|
96
|
-
id = evt[:id]
|
97
|
-
y = defaults[id] ? defaults[id][:y] : success_end_events.last + horizontal_end_offset
|
98
|
-
|
99
|
-
success_end_events << y
|
100
|
-
|
101
|
-
shapes << Shape.new("Shape_#{id}", id, Bounds.new(current, y, event_width, event_width))
|
102
|
-
end
|
103
|
-
|
104
|
-
edges = []
|
105
|
-
model.sequence_flow.each do |flow|
|
106
|
-
source = shapes.find { |shape| shape.id == "Shape_#{flow.sourceRef}" }.bounds
|
107
|
-
target = shapes.find { |shape| shape.id == "Shape_#{flow.targetRef}" }.bounds
|
108
|
-
|
109
|
-
edges << Edge.new("SequenceFlow_#{flow[:id]}", flow[:id], Path(source, target, target.x != current))
|
110
|
-
end
|
111
|
-
|
112
|
-
diagram = Struct.new(:plane).new(Plane.new(model.id, shapes, edges))
|
113
|
-
|
114
|
-
# render XML.
|
115
|
-
Representer::Definitions.new(Definitions.new(model, diagram)).to_xml
|
116
|
-
end
|
117
|
-
|
118
|
-
def self.Path(source, target, do_straight_line) # rubocop:disable Metrics/AbcSize
|
119
|
-
if source.y == target.y # --->
|
120
|
-
[Waypoint.new(*fromRight(source)), Waypoint.new(*toLeft(target))]
|
121
|
-
elsif do_straight_line
|
122
|
-
[Waypoint.new(*fromBottom(source)), Waypoint.new(*toLeft(target))]
|
123
|
-
elsif target.y > source.y # target below source.
|
124
|
-
[
|
125
|
-
l = Waypoint.new(*fromBottom(source)),
|
126
|
-
r = Waypoint.new(l.x, target.y + target.height / 2),
|
127
|
-
Waypoint.new(target.x, r.y)
|
128
|
-
]
|
129
|
-
else # target above source.
|
130
|
-
[l = Waypoint.new(*fromTop(source)), r = Waypoint.new(l.x, target.y + target.height / 2), Waypoint.new(target.x, r.y)]
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
def self.fromRight(left)
|
135
|
-
[left.x + left.width, left.y + left.height / 2]
|
136
|
-
end
|
137
|
-
|
138
|
-
def self.toLeft(bounds)
|
139
|
-
[bounds.x, bounds.y + bounds.height / 2]
|
140
|
-
end
|
141
|
-
|
142
|
-
def self.fromBottom(bounds)
|
143
|
-
[bounds.x + bounds.width / 2, bounds.y + bounds.height]
|
144
|
-
end
|
145
|
-
|
146
|
-
def self.fromTop(bounds)
|
147
|
-
[bounds.x + bounds.width / 2, bounds.y]
|
148
|
-
end
|
149
|
-
|
150
|
-
Definitions = Struct.new(:process, :diagram)
|
151
|
-
|
152
|
-
# Representers for BPMN XML.
|
153
|
-
module Representer
|
154
|
-
class Task < Representable::Decorator
|
155
|
-
include Representable::XML
|
156
|
-
include Representable::XML::Namespace
|
157
|
-
namespace "http://www.omg.org/spec/BPMN/20100524/MODEL"
|
158
|
-
|
159
|
-
self.representation_wrap = :task # overridden via :as.
|
160
|
-
|
161
|
-
property :id, attribute: true
|
162
|
-
property :name, attribute: true
|
163
|
-
|
164
|
-
collection :outgoing, exec_context: :decorator
|
165
|
-
collection :incoming, exec_context: :decorator
|
166
|
-
|
167
|
-
def outgoing
|
168
|
-
represented.outgoing.collect { |edge| edge[:id] }
|
169
|
-
end
|
170
|
-
|
171
|
-
def incoming
|
172
|
-
represented.incoming.collect { |edge| edge[:id] }
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
class SequenceFlow < Representable::Decorator
|
177
|
-
include Representable::XML
|
178
|
-
include Representable::XML::Namespace
|
179
|
-
self.representation_wrap = :sequenceFlow
|
180
|
-
namespace "http://www.omg.org/spec/BPMN/20100524/MODEL"
|
181
|
-
|
182
|
-
property :id, attribute: true
|
183
|
-
property :sourceRef, attribute: true, exec_context: :decorator
|
184
|
-
property :targetRef, attribute: true, exec_context: :decorator
|
185
|
-
property :direction, as: :conditionExpression
|
186
|
-
|
187
|
-
def sourceRef
|
188
|
-
represented.sourceRef
|
189
|
-
end
|
190
|
-
|
191
|
-
def targetRef
|
192
|
-
represented.targetRef
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
class Process < Representable::Decorator
|
197
|
-
include Representable::XML
|
198
|
-
include Representable::XML::Namespace
|
199
|
-
self.representation_wrap = :process
|
200
|
-
|
201
|
-
namespace "http://www.omg.org/spec/BPMN/20100524/MODEL"
|
202
|
-
|
203
|
-
property :id, attribute: true
|
204
|
-
|
205
|
-
collection :start_events, as: :startEvent, decorator: Task
|
206
|
-
collection :end_events, as: :endEvent, decorator: Task
|
207
|
-
collection :task, decorator: Task
|
208
|
-
collection :sequence_flow, decorator: SequenceFlow, as: :sequenceFlow
|
209
|
-
end
|
210
|
-
|
211
|
-
module Diagram
|
212
|
-
class Bounds < Representable::Decorator
|
213
|
-
include Representable::XML
|
214
|
-
include Representable::XML::Namespace
|
215
|
-
self.representation_wrap = :Bounds
|
216
|
-
|
217
|
-
namespace "http://www.omg.org/spec/DD/20100524/DC"
|
218
|
-
|
219
|
-
property :x, attribute: true
|
220
|
-
property :y, attribute: true
|
221
|
-
property :width, attribute: true
|
222
|
-
property :height, attribute: true
|
223
|
-
end
|
224
|
-
|
225
|
-
class Diagram < Representable::Decorator
|
226
|
-
feature Representable::XML
|
227
|
-
feature Representable::XML::Namespace
|
228
|
-
self.representation_wrap = :BPMNDiagram
|
229
|
-
|
230
|
-
namespace "http://www.omg.org/spec/BPMN/20100524/DI"
|
231
|
-
|
232
|
-
property :plane, as: "BPMNPlane" do
|
233
|
-
self.representation_wrap = :plane
|
234
|
-
|
235
|
-
property :element, as: :bpmnElement, attribute: true
|
236
|
-
|
237
|
-
namespace "http://www.omg.org/spec/BPMN/20100524/DI"
|
238
|
-
|
239
|
-
collection :shapes, as: "BPMNShape" do
|
240
|
-
self.representation_wrap = :BPMNShape
|
241
|
-
namespace "http://www.omg.org/spec/BPMN/20100524/DI"
|
242
|
-
|
243
|
-
property :id, attribute: true
|
244
|
-
property :element, as: :bpmnElement, attribute: true
|
245
|
-
|
246
|
-
property :bounds, as: "Bounds", decorator: Bounds
|
247
|
-
end
|
248
|
-
|
249
|
-
collection :edges, as: "BPMNEdge" do
|
250
|
-
self.representation_wrap = :BPMNEdge
|
251
|
-
namespace "http://www.omg.org/spec/BPMN/20100524/DI"
|
252
|
-
|
253
|
-
property :id, attribute: true
|
254
|
-
property :element, as: :bpmnElement, attribute: true
|
255
|
-
|
256
|
-
# <di:waypoint xsi:type="dc:Point" x="136" y="118" />
|
257
|
-
collection :waypoints, as: :waypoint do
|
258
|
-
namespace "http://www.omg.org/spec/DD/20100524/DI"
|
259
|
-
|
260
|
-
property :type, as: "xsi:type", exec_context: :decorator, attribute: true
|
261
|
-
property :x, attribute: true
|
262
|
-
property :y, attribute: true
|
263
|
-
|
264
|
-
def type
|
265
|
-
"dc:Point"
|
266
|
-
end
|
267
|
-
end
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
# namespace "http://www.w3.org/2001/XMLSchema-instance" # xsi
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
class Definitions < Representable::Decorator
|
276
|
-
include Representable::XML
|
277
|
-
include Representable::XML::Namespace
|
278
|
-
self.representation_wrap = :definitions
|
279
|
-
|
280
|
-
namespace "http://www.omg.org/spec/BPMN/20100524/MODEL"
|
281
|
-
namespace_def bpmn: "http://www.omg.org/spec/BPMN/20100524/MODEL"
|
282
|
-
namespace_def bpmndi: "http://www.omg.org/spec/BPMN/20100524/DI"
|
283
|
-
namespace_def di: "http://www.omg.org/spec/DD/20100524/DI"
|
284
|
-
|
285
|
-
namespace_def dc: "http://www.omg.org/spec/DD/20100524/DC" # <cd:Bounds>
|
286
|
-
namespace_def xsi: "http://www.w3.org/2001/XMLSchema-instance" # used in waypoint.
|
287
|
-
|
288
|
-
property :process, decorator: Process
|
289
|
-
property :diagram, decorator: Diagram::Diagram, as: :BPMNDiagram
|
290
|
-
end
|
291
|
-
end
|
292
|
-
end
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
# <bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
297
|
-
# <bpmndi:BPMNPlane id="BPMNPlane_1">
|
298
|
-
# <bpmndi:BPMNShape id="_BPMNShape_Task_2" bpmnElement="Task_2">
|
299
|
-
# <dc:Bounds x="100" y="100" width="36" height="36" />
|
300
|
-
# </bpmndi:BPMNShape>
|
301
|
-
# <bpmndi:BPMNShape id="_BPMNShape_Task_3" bpmnElement="Task_3">
|
302
|
-
# <dc:Bounds x="236" y="78" width="100" height="80" />
|
303
|
-
# </bpmndi:BPMNShape>
|
304
|
-
# <bpmndi:BPMNEdge id="_BPMNConnection_Flow_4" bpmnElement="Flow_4">
|
305
|
-
# <di:waypoint xsi:type="dc:Point" x="136" y="118" />
|
306
|
-
# <di:waypoint xsi:type="dc:Point" x="236" y="118" />
|
307
|
-
# </bpmndi:BPMNEdge>
|
308
|
-
# <bpmndi:BPMNShape id="_BPMNShape_Task_5" bpmnElement="Task_5">
|
309
|
-
# <dc:Bounds x="436" y="78" width="100" height="80" />
|
310
|
-
# </bpmndi:BPMNShape>
|
311
|
-
# <bpmndi:BPMNEdge id="_BPMNConnection_Flow_6" bpmnElement="Flow_6">
|
312
|
-
# <di:waypoint xsi:type="dc:Point" x="336" y="118" />
|
313
|
-
# <di:waypoint xsi:type="dc:Point" x="436" y="118" />
|
314
|
-
# </bpmndi:BPMNEdge>
|
315
|
-
# <bpmndi:BPMNShape id="_BPMNShape_Task_1" bpmnElement="Task_1">
|
316
|
-
# <dc:Bounds x="636" y="100" width="36" height="36" />
|
317
|
-
# </bpmndi:BPMNShape>
|
318
|
-
# <bpmndi:BPMNShape id="_BPMNShape_Task_8" bpmnElement="Task_8">
|
319
|
-
# <dc:Bounds x="636" y="266" width="100" height="80" />
|
320
|
-
# </bpmndi:BPMNShape>
|
321
|
-
# <bpmndi:BPMNEdge id="_BPMNConnection_Flow_7" bpmnElement="Flow_7">
|
322
|
-
# <di:waypoint xsi:type="dc:Point" x="536" y="118" />
|
323
|
-
# <di:waypoint xsi:type="dc:Point" x="636" y="118" />
|
324
|
-
# </bpmndi:BPMNEdge>
|
325
|
-
# <bpmndi:BPMNEdge id="_BPMNConnection_Flow_9" bpmnElement="Flow_9">
|
326
|
-
# <di:waypoint xsi:type="dc:Point" x="536" y="118" />
|
327
|
-
# <di:waypoint xsi:type="dc:Point" x="586" y="118" />
|
328
|
-
# <di:waypoint xsi:type="dc:Point" x="586" y="306" />
|
329
|
-
# <di:waypoint xsi:type="dc:Point" x="636" y="306" />
|
330
|
-
# </bpmndi:BPMNEdge>
|
331
|
-
# <bpmndi:BPMNEdge id="_BPMNConnection_Flow_10" bpmnElement="Flow_10">
|
332
|
-
# <di:waypoint xsi:type="dc:Point" x="686" y="266" />
|
333
|
-
# <di:waypoint xsi:type="dc:Point" x="686" y="201" />
|
334
|
-
# <di:waypoint xsi:type="dc:Point" x="654" y="201" />
|
335
|
-
# <di:waypoint xsi:type="dc:Point" x="654" y="136" />
|
336
|
-
# </bpmndi:BPMNEdge>
|
337
|
-
# </bpmndi:BPMNPlane>
|
338
|
-
# </bpmndi:BPMNDiagram>
|