trailblazer-developer 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.
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: []