trailblazer-developer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 41c30437086de2bc9a0148eccf3db59603e484f92c032ff923cd0275fd369db0
4
+ data.tar.gz: bd45234bd028d5a5ef5348959da4b834ffdc6bf79af3821689718d76055ef4df
5
+ SHA512:
6
+ metadata.gz: a66246114d6111e30c76993e18308675544b7623a8afcf732c9e0feab79cdda46dbcc3f789c20ea990c2276f1ef48321802b8f59c40706beab6480aa587bee80
7
+ data.tar.gz: 4bfdbbb63921aadf36c77c4c96a2da0e2efc0d283140b0263c504743bca92fbbc26a6f31ad7910bae09654e80a58c8d1deb53513fb1866c9e49bb060cc5ad313
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,115 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.5.0
3
+ DisplayCopNames: true
4
+ Layout/CaseIndentation:
5
+ IndentOneStep: true
6
+ Layout/FirstArrayElementLineBreak:
7
+ Enabled: true
8
+ Layout/FirstHashElementLineBreak:
9
+ Enabled: true
10
+ Layout/FirstMethodArgumentLineBreak:
11
+ Enabled: true
12
+ Layout/FirstMethodParameterLineBreak:
13
+ Enabled: true
14
+ Layout/MultilineAssignmentLayout:
15
+ Enabled: true
16
+ EnforcedStyle: same_line
17
+ Layout/SpaceInsideHashLiteralBraces:
18
+ EnforcedStyle: no_space
19
+ Metrics/LineLength:
20
+ Max: 130
21
+ Metrics/ParameterLists:
22
+ Max: 5
23
+ Naming/VariableNumber:
24
+ EnforcedStyle: snake_case
25
+ Style/AndOr:
26
+ EnforcedStyle: conditionals
27
+ Style/AutoResourceCleanup:
28
+ Enabled: true
29
+ Style/CollectionMethods:
30
+ Enabled: true
31
+ Style/Documentation:
32
+ Enabled: false
33
+ Style/EmptyLiteral:
34
+ Enabled: false
35
+ Style/EmptyMethod:
36
+ EnforcedStyle: expanded
37
+ Style/FormatStringToken:
38
+ EnforcedStyle: template
39
+ Style/ImplicitRuntimeError:
40
+ Enabled: true
41
+ Style/MethodCalledOnDoEndBlock:
42
+ Enabled: true
43
+ Style/MethodDefParentheses:
44
+ EnforcedStyle: require_parentheses
45
+ Style/MissingElse:
46
+ Enabled: true
47
+ EnforcedStyle: case
48
+ Style/NumericLiterals:
49
+ Enabled: false
50
+ Style/OptionHash:
51
+ Enabled: true
52
+ Style/PercentLiteralDelimiters:
53
+ PreferredDelimiters:
54
+ "%w": "[]"
55
+ "%W": "[]"
56
+ "%i": "[]"
57
+ "%I": "[]"
58
+ "%r": "()"
59
+ Style/ReturnNil:
60
+ Enabled: true
61
+ Style/SafeNavigation:
62
+ Enabled: false
63
+ Style/Send:
64
+ Enabled: true
65
+ Style/SignalException:
66
+ EnforcedStyle: semantic
67
+ Style/StringLiterals:
68
+ EnforcedStyle: double_quotes
69
+ Style/StringLiteralsInInterpolation:
70
+ EnforcedStyle: double_quotes
71
+ Style/StringMethods:
72
+ Enabled: true
73
+ Style/SymbolArray:
74
+ Enabled: true
75
+ # this allows in rspec to have expect { } with multiple lines
76
+ Style/BlockDelimiters:
77
+ EnforcedStyle: braces_for_chaining
78
+ Layout/EndOfLine:
79
+ Enabled: false
80
+ # don't need these checks in test folders
81
+ Metrics/ModuleLength:
82
+ Exclude:
83
+ - "spec/**/*"
84
+ - "test/**/*"
85
+ Metrics/BlockLength:
86
+ Exclude:
87
+ - "spec/**/*"
88
+ - "test/**/*"
89
+ - "*.gemspec" # definitely not in the gemspec
90
+ Metrics/MethodLength:
91
+ Max: 20
92
+ Lint/UnreachableCode:
93
+ Description: 'Unreachable code.'
94
+ Enabled: false
95
+ Lint/Void:
96
+ Enabled: false
97
+ Layout/AlignHash:
98
+ EnforcedLastArgumentHashStyle: ignore_implicit
99
+ Metrics/AbcSize:
100
+ Max: 25
101
+ Style/LambdaCall:
102
+ Enabled: false
103
+ Style/Semicolon:
104
+ Enabled: false
105
+ Naming/UncommunicativeMethodParamName:
106
+ Enabled: false
107
+ Style/ClassAndModuleChildren:
108
+ Enabled: false
109
+ Layout/LeadingCommentSpace:
110
+ Exclude:
111
+ - 'test/docs/**/*'
112
+ Layout/AlignHash:
113
+ EnforcedHashRocketStyle: table
114
+ Style/FrozenStringLiteralComment:
115
+ Enabled: false
data/.rubocop.yml ADDED
@@ -0,0 +1,36 @@
1
+ inherit_from:
2
+ - https://raw.githubusercontent.com/trailblazer/meta/master/rubocop.yml
3
+
4
+ AllCops:
5
+ TargetRubyVersion: 2.1
6
+ Exclude:
7
+ - 'test/hash/bla.rb'
8
+
9
+ Metrics/LineLength:
10
+ Exclude:
11
+ - test/diagram_test.rb
12
+
13
+ Naming/MethodName:
14
+ Enabled: false
15
+
16
+ Style/ClassAndModuleChildren:
17
+ Enabled: false
18
+
19
+ # DISCUSS: user map instead of collect
20
+ Style/CollectionMethods:
21
+ Enabled: false
22
+
23
+ # DISCUSS: Use raise with an explicit exception class and message, rather than just a message
24
+ Style/ImplicitRuntimeError:
25
+ Enabled: false
26
+
27
+ Style/Lambda:
28
+ EnforcedStyle: literal
29
+
30
+ # DISCUSS: Use fail instead of raise to signal exceptions
31
+ Style/SignalException:
32
+ Enabled: false
33
+
34
+ # DISUSS: this could be false because we have if locals blabla
35
+ Bundler/DuplicatedGem:
36
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install:
6
+ - gem install bundler
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in trailblazer-developer.gemspec
4
+ gemspec
5
+
6
+ # gem "trailblazer-activity", ">= 0.7.1"
7
+ gem "trailblazer-activity", path: "../trailblazer-activity"
8
+ gem "trailblazer-activity-dsl-linear", path: "../trailblazer-activity-dsl-linear"
9
+
10
+ # gem "json"
11
+
12
+ gem "representable"
data/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # Trailblazer::Developer
2
+
3
+ _Developer tools for Trailblazers._
4
+
5
+ ## Documentation
6
+
7
+ Find the complete documentation on the project website: [http://trb.to/2.1#developer]
8
+
9
+ ## Summary
10
+
11
+ The `developer` gem provides the following neat tools.
12
+
13
+ * Quick rendering of activities, including their taskWraps.
14
+ * `wtf?` mode™: Run a broken activity and trace where an exception happened.
15
+ * Connect to the visual TRB-editor: load, store, organize and render your app's activities.
16
+ * Generate {Intermediate} data structures from TRB-editor.js files.
17
+ * Debugging mode: run and debug workflows in the TRB-editor.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+ require "rubocop/rake_task"
4
+
5
+ Rake::TestTask.new(:test) do |t|
6
+ t.libs << "test"
7
+ t.libs << "lib"
8
+ t.test_files = FileList["test/**/*_test.rb"]
9
+ end
10
+
11
+ RuboCop::RakeTask.new(:rubocop)
12
+
13
+ desc "Running Tests"
14
+ task default: %i[test rubocop]
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "trailblazer/developer"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,12 @@
1
+ require "trailblazer/developer/version"
2
+
3
+ module Trailblazer
4
+ module Developer
5
+ # Your code goes here...
6
+ end
7
+ end
8
+
9
+ require "trailblazer/developer/generate"
10
+ require "trailblazer/developer/render/circuit"
11
+
12
+ # require "trailblazer/developer/client"
@@ -0,0 +1,51 @@
1
+ module Trailblazer
2
+ module Developer
3
+ # Transforms an {Activity::Graph} into an abstract data structure that represents the graph via a
4
+ # well-defined API. The goal is to decouple graph internals from the presentation layer.
5
+ # The {Model} is usually passed into Renderer and Layouter, to render the bpmn:Diagram XML or JSON.
6
+ #
7
+ # It returns {Model} with {Task}s and {Flow}s.
8
+ module Activity
9
+ module Graph
10
+ class << self
11
+ Model = Struct.new(:id, :start_events, :end_events, :task, :sequence_flow)
12
+ Task = Struct.new(:id, :name, :options, :incoming, :outgoing)
13
+ # DISCUSS: direction ATM is the "condition" for the BPMN rendering.
14
+ Flow = Struct.new(:id, :sourceRef, :targetRef, :direction)
15
+
16
+ # @param Graph an object implementing the Activity::Graph interface
17
+ # @return Model Generic representation of the graph, ready for rendering.
18
+ def self.to_model(graph, id: "some-process") # rubocop:disable Metrics/AbcSize
19
+ start_events = graph.find_all("Start.default") # FIXME. this is a static assumption.
20
+ end_events = graph.find_all { |node| graph.successors(node).size.zero? }
21
+ tasks = graph.find_all { |_node| true }
22
+ tasks -= start_events
23
+ tasks -= end_events
24
+
25
+ # transform nodes into BPMN elements.
26
+ start_events = start_events.collect do |evt|
27
+ Task.new(evt[:id], evt[:id], evt, Incomings(graph, evt), Outgoings(graph, evt))
28
+ end
29
+ end_events = end_events.collect do |evt|
30
+ Task.new(evt[:id], evt[:id], evt, Incomings(graph, evt), Outgoings(graph, evt))
31
+ end
32
+ tasks = tasks.collect { |evt| Task.new(evt[:id], evt[:id], evt, Incomings(graph, evt), Outgoings(graph, evt)) }
33
+ edges = (start_events + end_events + tasks).collect { |task| [task.incoming, task.outgoing] }.flatten(2).uniq
34
+
35
+ Model.new(id, start_events, end_events, tasks, edges)
36
+ end
37
+
38
+ private
39
+
40
+ def Outgoings(graph, source)
41
+ graph.successors(source).collect { |target, edge| Flow.new(edge[:id], source[:id], target[:id], edge[:_wrapped]) }
42
+ end
43
+
44
+ def Incomings(graph, target)
45
+ graph.predecessors(target).collect { |source, edge| Flow.new(edge[:id], source[:id], target[:id], edge[:_wrapped]) }
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,21 @@
1
+ require "faraday"
2
+
3
+ module Trailblazer::Developer
4
+ module Client
5
+ def self.push(operation:, name:)
6
+ xml = Trailblazer::Diagram::BPMN.to_xml(operation["__activity__"], operation["__sequence__"].map(&:id))
7
+ token = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJpZCI6MywidXNlcm5hbWUiOiJhcG90b25pY2siLCJlbWFpbCI6Im5pY2tAdHJhaWxibGF6ZXIudG8ifQ." # rubocop:disable Metrics/LineLength
8
+ conn = Faraday.new(url: "https://api.trb.to")
9
+ response = conn.post do |req|
10
+ req.url "/dev/v1/import"
11
+ req.headers["Content-Type"] = "application/json"
12
+ req.headers["Authorization"] = token
13
+ require "base64"
14
+
15
+ req.body = %({ "name": "#{name}", "xml":"#{Base64.strict_encode64(xml)}" })
16
+ end
17
+
18
+ puts response.status.inspect
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,66 @@
1
+ require "representable/hash"
2
+
3
+ module Trailblazer
4
+ module Developer
5
+ # Computes an {Intermediate} data structure from a TRB-editor.js file.
6
+ module Generate
7
+ module_function
8
+
9
+ Element = Struct.new(:id, :type, :linksTo, :data, :label)
10
+ Arrow = Struct.new(:target, :label)
11
+
12
+ module Representer
13
+ class Activity < Representable::Decorator
14
+ include Representable::Hash
15
+
16
+ collection :elements, class: Element do
17
+ property :id
18
+ property :type
19
+ collection :linksTo, class: Arrow, default: [] do
20
+ property :target
21
+ property :label
22
+ end
23
+ property :data, default: {}
24
+ property :label
25
+ end
26
+ end
27
+ end
28
+
29
+ def call(hash)
30
+ elements = Representer::Activity.new(OpenStruct.new).from_hash(hash).elements
31
+
32
+ start_events = elements.find_all { |el| el.type == "Event" }
33
+ end_events = elements.find_all { |el| el.type == "EndEventTerminate" }# DISCUSS: TERMINATE?
34
+
35
+ inter = Activity::Schema::Intermediate
36
+
37
+ wiring = elements.collect { |el| [inter.TaskRef(el.id, el.data), el.linksTo.collect { |arrow| inter.Out(semantic_for(arrow.to_h), arrow.target) } ] }
38
+ wiring = Hash[wiring]
39
+
40
+ # end events need this stupid special handling
41
+ wiring = wiring.merge(Hash[
42
+ end_events.collect do |_end|
43
+ ref, outputs = wiring.find { |ref, _| ref.id == _end.id }
44
+
45
+ [ref, [inter.Out(semantic_for(_end.to_h), nil)]]
46
+ end
47
+ ])
48
+ # pp wiring
49
+
50
+ inter.new(wiring, end_events.collect(&:id), start_events.collect(&:id))
51
+ end
52
+
53
+ # private
54
+
55
+ # We currently use the {:label} field of an arrow to encode an output semantic.
56
+ # The {:symbol_style} part will be filtered out as semantic. Defaults to {:success}.
57
+ def semantic_for(label:nil, **)
58
+ return :success unless label
59
+
60
+ m = label.match(/:(\w+)/)
61
+ return m[1].to_sym
62
+ end
63
+ end
64
+ end
65
+ end
66
+ # [Inter::Out(:success, nil)]
@@ -0,0 +1,51 @@
1
+ module Trailblazer
2
+ module Developer
3
+ module Render
4
+ module Circuit
5
+ module_function
6
+
7
+ # Render an {Activity}'s circuit as a simple hash.
8
+ def call(activity, **options)
9
+ graph = Activity::Introspect::Graph(activity)
10
+
11
+ circuit_hash(graph, **options)
12
+ end
13
+
14
+ def circuit_hash(graph, **options)
15
+ content = graph.collect do |node|
16
+ conns = node.outgoings.collect do |outgoing|
17
+ " {#{outgoing.output.signal}} => #{inspect_with_matcher(outgoing.task, **options)}"
18
+ end
19
+
20
+ [ inspect_with_matcher(node.task, **options), conns.join("\n") ]
21
+ end
22
+
23
+ content = content.join("\n")
24
+
25
+ return "\n#{content}".gsub(/0x\w+/, "0x")#.gsub(/0.\d+/, "0.")
26
+ end
27
+
28
+ # If Ruby had pattern matching, this function wasn't necessary.
29
+ def inspect_with_matcher(task, inspect_task: method(:inspect_task), inspect_end: method(:inspect_end))
30
+ return inspect_task.(task) unless task.kind_of?(Trailblazer::Activity::End)
31
+ inspect_end.(task)
32
+ end
33
+
34
+ def inspect_task(task)
35
+ task.inspect
36
+ end
37
+
38
+ def inspect_end(task)
39
+ class_name = strip(task.class)
40
+ options = task.to_h
41
+
42
+ "#<#{class_name}/#{options[:semantic].inspect}>"
43
+ end
44
+
45
+ def strip(string)
46
+ string.to_s.sub("Trailblazer::Activity::", "")
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,5 @@
1
+ module Trailblazer
2
+ module Developer
3
+ VERSION = "0.0.1".freeze
4
+ end
5
+ end
@@ -0,0 +1,92 @@
1
+ module Trailblazer::Developer
2
+ module_function
3
+
4
+ def wtf(activity, args)
5
+ Wtf.invoke(activity, args)
6
+ end
7
+ alias_method :wtf?, :wtf
8
+
9
+ module Wtf
10
+ module_function
11
+
12
+ # Run {activity} with tracing enabled and inject a mutable {Stack} instance.
13
+ # This allows to display the trace even when an exception happened
14
+ def invoke(activity, (ctx, flow_options))
15
+ flow_options ||= {} # Ruby sucks.
16
+
17
+ # this instance gets mutated with every step. unfortunately, there is
18
+ # no other way in Ruby to keep the trace even when an exception was thrown.
19
+ stack = Trailblazer::Activity::Trace::Stack.new
20
+
21
+ begin
22
+ returned_stack, _ = Trailblazer::Activity::Trace.invoke( activity,
23
+ [
24
+ ctx,
25
+ flow_options.merge(stack: stack)
26
+ ]
27
+ )
28
+ rescue
29
+
30
+ # DISCUSS: we shouldn't use internal knowledge of the Stack/Level API here.
31
+ closest = stack.to_a
32
+ while closest.is_a?(Trailblazer::Activity::Trace::Level) && closest = closest.last do # FIXME: deep-dive via Stack API.
33
+ end
34
+
35
+ # pp closest.task # this was the last executed task
36
+
37
+ handle(stack, $!, closest.task, activity, [ctx, flow_options])
38
+ end
39
+ end
40
+
41
+ def exception_renderer(stack:, level:, input:, name:, closest_task:)
42
+ return [ level, %{#{fmt(fmt(name, :red), :bold)}} ] if input.task == closest_task
43
+ [ level, %{#{name}} ]
44
+ end
45
+
46
+ # TODO: make this injectable
47
+ def handle(stack, exception, closest_task, activity, *args)
48
+ puts "[Trailblazer] Exception tracing"
49
+ puts "#{fmt(exception.inspect, :bold)}"
50
+ puts " #{exception.backtrace[0]}"
51
+ puts " #{exception.backtrace[1]}"
52
+ puts
53
+ puts Trailblazer::Activity::Trace::Present.(stack, closest_task: closest_task, renderer: method(:exception_renderer))
54
+ end
55
+
56
+
57
+
58
+ def fmt(line, style)
59
+ String.send(style, line)
60
+ end
61
+
62
+ # Stolen from https://stackoverflow.com/questions/1489183/colorized-ruby-output
63
+ #
64
+ # TODO: this is just prototyping
65
+ module String
66
+ module_function
67
+ def black(str); "\e[30m#{str}\e[0m" end
68
+ def red(str); "\e[31m#{str}\e[0m" end
69
+ def green(str); "\e[32m#{str}\e[0m" end
70
+ def brown(str); "\e[33m#{str}\e[0m" end
71
+ def blue(str); "\e[34m#{str}\e[0m" end
72
+ def magenta(str); "\e[35m#{str}\e[0m" end
73
+ def cyan(str); "\e[36m#{str}\e[0m" end
74
+ def gray(str); "\e[37m#{str}\e[0m" end
75
+
76
+ def bg_black(str); "\e[40m#{str}\e[0m" end
77
+ def bg_red(str); "\e[41m#{str}\e[0m" end
78
+ def bg_green(str); "\e[42m#{str}\e[0m" end
79
+ def bg_brown(str); "\e[43m#{str}\e[0m" end
80
+ def bg_blue(str); "\e[44m#{str}\e[0m" end
81
+ def bg_magenta(str); "\e[45m#{str}\e[0m" end
82
+ def bg_cyan(str); "\e[46m#{str}\e[0m" end
83
+ def bg_gray(str); "\e[47m#{str}\e[0m" end
84
+
85
+ def bold(str); "\e[1m#{str}\e[22m" end
86
+ def italic(str); "\e[3m#{str}\e[23m" end
87
+ def underline(str); "\e[4m#{str}\e[24m" end
88
+ def blink(str); "\e[5m#{str}\e[25m" end
89
+ def reverse_color(str); "\e[7m#{str}\e[27m" end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,338 @@
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>
@@ -0,0 +1,27 @@
1
+ lib = File.expand_path("lib", __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "trailblazer/developer/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "trailblazer-developer"
7
+ spec.version = Trailblazer::Developer::VERSION
8
+ spec.authors = ["Nick Sutterer"]
9
+ spec.email = ["apotonick@gmail.com"]
10
+
11
+ spec.summary = "Developer tools for Trailblazer."
12
+ spec.description = "Developer tools for Trailblazer: debugger, tracing, visual editor integration."
13
+ spec.homepage = "http://trailblazer.to/gems/trailblazer/developer.html"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{^(test)/})
17
+ end
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler"
21
+ spec.add_development_dependency "minitest", "~> 5.0"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rubocop"
24
+
25
+ spec.add_dependency "trailblazer-activity", "~> 0.8"
26
+ spec.add_dependency "representable"
27
+ end
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: trailblazer-developer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Nick Sutterer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-03-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: trailblazer-activity
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.8'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.8'
83
+ - !ruby/object:Gem::Dependency
84
+ name: representable
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: 'Developer tools for Trailblazer: debugger, tracing, visual editor integration.'
98
+ email:
99
+ - apotonick@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".rubocop-https---raw-githubusercontent-com-trailblazer-meta-master-rubocop-yml"
106
+ - ".rubocop.yml"
107
+ - ".travis.yml"
108
+ - Gemfile
109
+ - README.md
110
+ - Rakefile
111
+ - bin/console
112
+ - bin/setup
113
+ - lib/trailblazer/developer.rb
114
+ - lib/trailblazer/developer/activity.rb
115
+ - lib/trailblazer/developer/client.rb
116
+ - lib/trailblazer/developer/generate.rb
117
+ - lib/trailblazer/developer/render/circuit.rb
118
+ - lib/trailblazer/developer/version.rb
119
+ - lib/trailblazer/developer/wtf.rb
120
+ - lib/trailblazer/diagram/bpmn.rb
121
+ - trailblazer-developer.gemspec
122
+ homepage: http://trailblazer.to/gems/trailblazer/developer.html
123
+ licenses: []
124
+ metadata: {}
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 2.7.3
142
+ signing_key:
143
+ specification_version: 4
144
+ summary: Developer tools for Trailblazer.
145
+ test_files: []