trailblazer-circuit 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +13 -0
- data/CHANGES.md +3 -0
- data/Gemfile +6 -0
- data/README.md +117 -0
- data/Rakefile +10 -0
- data/lib/trailblazer/circuit/activity.rb +60 -0
- data/lib/trailblazer/circuit/alter.rb +49 -0
- data/lib/trailblazer/circuit/present.rb +70 -0
- data/lib/trailblazer/circuit/task.rb +47 -0
- data/lib/trailblazer/circuit/trace.rb +19 -0
- data/lib/trailblazer/circuit/version.rb +5 -0
- data/lib/trailblazer/circuit.rb +125 -0
- data/lib/trailblazer-circuit.rb +1 -0
- data/trailblazer-circuit.gemspec +28 -0
- metadata +115 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5a7e34849fb98696e6c453bbe8177d90b3932ecd
|
4
|
+
data.tar.gz: 5c2d5ab3e575f380fda9f2976bddd77d2a5b9fb1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9e64cefc10dfc6606ebe9be18cc918145ba4b1a4a62854b35a00e97b0dc26233a85d81e39b96f0534dc469f763c6796618b1350480bea30ec566f3c1a45ef1f1
|
7
|
+
data.tar.gz: c71327201a57cddb155d70c5cb6e1bbb67daf040a988be09045831c2e93900e05072689f564ad0de1a1a6eafc837d4f5a956a269259f5fe09f9fc1b08684fdf0
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CHANGES.md
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
# Circuit
|
2
|
+
|
3
|
+
_The Circuit of Life._
|
4
|
+
|
5
|
+
Circuit provides a simplified [flowchart](https://en.wikipedia.org/wiki/Flowchart) implementation with terminals (for example, start or end state), connectors and tasks (processes). It allows to define the flow (the actual *circuit*) and execute it.
|
6
|
+
|
7
|
+
Circuit refrains from implementing deciders. The decisions are encoded in the output signals of tasks.
|
8
|
+
|
9
|
+
`Circuit` and `workflow` use [BPMN](http://www.bpmn.org/) lingo and concepts for describing processes and flows. This document can be found in the [Trailblazer documentation](http://trailblazer.to/gems/workflow/circuit.html), too.
|
10
|
+
|
11
|
+
{% callout %}
|
12
|
+
The `circuit` gem is the lowest level of abstraction and is used in `operation` and `workflow`, which both provide higher-level APIs for the Railway pattern and complex BPMN workflows.
|
13
|
+
{% endcallout %}
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
To use circuits, activities and nested tasks, you need one gem, only.
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem "trailblazer-circuit"
|
21
|
+
```
|
22
|
+
|
23
|
+
The `trailblazer-circuit` gem is often just called the `circuit` gem. It ships with the `operation` gem and implements the internal Railway.
|
24
|
+
|
25
|
+
## Overview
|
26
|
+
|
27
|
+
The following diagram illustrates a common use-case for `circuit`, the task of publishing a blog post.
|
28
|
+
|
29
|
+
<img src="/images/diagrams/blog-bpmn1.png">
|
30
|
+
|
31
|
+
After writing and spell-checking, the author has the chance to publish the post or, in case of typos, go back, correct, and go through the same flow, again. Note that there's only a handful of defined transistions, or connections. An author, for example, is not allowed to jump from "correct" into "publish" without going through the check.
|
32
|
+
|
33
|
+
The `circuit` gem allows you to define this *activity* and takes care of implementing the control flow, running the activity and making sure no invalid paths are taken.
|
34
|
+
|
35
|
+
Your job is solely to implement the tasks and deciders put into this activity - you don't have to take care of executing it in the right order, and so on.
|
36
|
+
|
37
|
+
## Definition
|
38
|
+
|
39
|
+
In order to define an activity, you can use the BPMN editor of your choice and run it through the Trailblazer circuit generator, use our online tool (if [you're a PRO member](http://pro.trailblazer.to)) or simply define it using plain Ruby.
|
40
|
+
|
41
|
+
{{ "test/docs/activity_test.rb:basic:../trailblazer-circuit" | tsnippet }}
|
42
|
+
|
43
|
+
The `Activity` function is a convenient tool to create an activity. Note that the yielded object allows to access *events* from the activity, such as the `Start` and `End` event that are created per default.
|
44
|
+
|
45
|
+
This defines the control flow - the next step is to actually implement the tasks in this activity.
|
46
|
+
|
47
|
+
## Task
|
48
|
+
|
49
|
+
A *task* usually maps to a particular box in your diagram. Its API is very simple: a task needs to expose a `call` method, allowing it to be a lambda or any other callable object.
|
50
|
+
|
51
|
+
{{ "test/docs/activity_test.rb:write:../trailblazer-circuit" | tsnippet }}
|
52
|
+
|
53
|
+
It receives all arguments returned from the task run before. This means a task should return everything it receives.
|
54
|
+
|
55
|
+
To transport data across the flow, you can change the return value. In this example, we use one global hash `options` that is passed from task to task and used for writing.
|
56
|
+
|
57
|
+
The first return value is crucial: it dictates what will be the next step when executing the flow.
|
58
|
+
|
59
|
+
For example, the `SpellCheck` task needs to decide which route to take.
|
60
|
+
|
61
|
+
{{ "test/docs/activity_test.rb:spell:../trailblazer-circuit" | tsnippet }}
|
62
|
+
|
63
|
+
It's as simple as returning the appropriate signal.
|
64
|
+
|
65
|
+
{% callout %}
|
66
|
+
You can use any object as a direction signal and return it, as long as it's defined in the circuit.
|
67
|
+
{% endcallout %}
|
68
|
+
|
69
|
+
## Call
|
70
|
+
|
71
|
+
After defining circuit and implementing the tasks, the circuit can be executed using its very own `call` method.
|
72
|
+
|
73
|
+
{{ "test/docs/activity_test.rb:call:../trailblazer-circuit" | tsnippet }}
|
74
|
+
|
75
|
+
The first argument is where to start the circuit. Usually, this will be the activity's `Start` event accessable via `activity[:Start]`.
|
76
|
+
|
77
|
+
All options are passed straight to the first task, which in turn has to make sure it returns an appropriate result set.
|
78
|
+
|
79
|
+
The activity's return set is the last run task and all arguments from the last task.
|
80
|
+
|
81
|
+
{{ "test/docs/activity_test.rb:call-ret:../trailblazer-circuit" | tsnippet }}
|
82
|
+
|
83
|
+
As opposed to higher abstractions such as `Operation`, it is completely up to the developer what interfaces they provide to tasks and their return values. What is a mutable hash here could be an explicit array of return values in another implementation style, and so on.
|
84
|
+
|
85
|
+
## Tracing
|
86
|
+
|
87
|
+
For debugging or simply understanding the flows of circuits, you can use tracing.
|
88
|
+
|
89
|
+
{{ "test/docs/activity_test.rb:trace-act:../trailblazer-circuit" | tsnippet }}
|
90
|
+
|
91
|
+
The second argument to `Activity` takes debugging information, so you can set readable names for tasks.
|
92
|
+
|
93
|
+
When invoking the activity, the `:runner` option will activate tracing and write debugging information about any executed task onto the `:stack` array.
|
94
|
+
|
95
|
+
{{ "test/docs/activity_test.rb:trace-call:../trailblazer-circuit" | tsnippet }}
|
96
|
+
|
97
|
+
The `stack` can then be passed to a presenter.
|
98
|
+
|
99
|
+
{{ "test/docs/activity_test.rb:trace-res:../trailblazer-circuit" | tsnippet }}
|
100
|
+
|
101
|
+
Tracing is extremely efficient to find out what is going wrong and supersedes cryptic debuggers by many times. Note that tracing also works for deeply nested circuits.
|
102
|
+
|
103
|
+
{% callout %}
|
104
|
+
🌅 In future versions of Trailblazer, our own debugger will take advantage of the explicit, traceable nature of circuits and also integrate with Ruby's exception handling.
|
105
|
+
|
106
|
+
Also, more options will make debugging of complex, nested workflows easier.
|
107
|
+
{% endcallout %}
|
108
|
+
|
109
|
+
## Event
|
110
|
+
|
111
|
+
* how to add more ends, etc.
|
112
|
+
|
113
|
+
## Nested
|
114
|
+
|
115
|
+
## Operation
|
116
|
+
|
117
|
+
If you need a higher abstraction of `circuit`, check out Trailblazer's [operation](localhost:4000/gems/operation/2.0/api.html) implemenation which provides a simple Railway-oriented interface to create linear circuits.
|
data/Rakefile
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Circuit
|
3
|
+
# This is a code structure to encapsulate the circuit execution behavior and the
|
4
|
+
# start, intermediate and end events, within a "physical" business process.
|
5
|
+
#
|
6
|
+
# activity[:Start]
|
7
|
+
# activity.()
|
8
|
+
# activity.values
|
9
|
+
Activity = Struct.new(:circuit, :events) do
|
10
|
+
def [](*args)
|
11
|
+
events[*args]
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(*args, &block)
|
15
|
+
circuit.(*args, &block)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# DSL
|
20
|
+
# Conveniently build an Activity with Circuit instance and
|
21
|
+
# all its signals/events.
|
22
|
+
def self.Activity(name=:default, events={}, end_events=nil, implementation=false, &block)
|
23
|
+
# default events:
|
24
|
+
start = events[:start] || { default: Start.new(:default) }
|
25
|
+
_end = events[:end] || { default: End.new(:default) }
|
26
|
+
|
27
|
+
events = { start: start }.merge(events)
|
28
|
+
events = { end: _end }.merge(events)
|
29
|
+
|
30
|
+
end_events ||= _end.values
|
31
|
+
|
32
|
+
evts = Events(events)
|
33
|
+
circuit = Circuit(name, evts, end_events, &block)
|
34
|
+
|
35
|
+
# DISCUSS: remove this!
|
36
|
+
circuit = implementation.(circuit) if implementation
|
37
|
+
|
38
|
+
Activity.new(circuit, evts)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.Circuit(name=:default, events, end_events)
|
42
|
+
Circuit.new(yield(events), end_events, name)
|
43
|
+
end
|
44
|
+
|
45
|
+
# DSL
|
46
|
+
# events[:Start]
|
47
|
+
#
|
48
|
+
# :private:
|
49
|
+
def self.Events(events)
|
50
|
+
evts = Struct.new(*events.keys) do # [Start, End, Resume]
|
51
|
+
def [](event, name=:default)
|
52
|
+
cfg = super(event.downcase)
|
53
|
+
cfg[name] or raise "[Circuit] Event `#{event}.#{name} unknown."
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
evts.new(*events.values)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Circuit::Activity
|
3
|
+
# Find all `direction` connections TO <old_task> and rewire them to new_task,
|
4
|
+
# then connect new to old with `direction`.
|
5
|
+
def self.Before(activity, old_task, new_task, direction:raise, **kws)
|
6
|
+
Rewrite(activity, **kws) do |new_map|
|
7
|
+
cfg = new_map.find_all { |act, outputs| outputs[direction]==old_task }
|
8
|
+
# rewire old line to new task.
|
9
|
+
cfg.each { |(activity, outputs)| outputs[direction] = new_task }
|
10
|
+
# connect new_task --> old_task.
|
11
|
+
new_map[new_task] = { direction => old_task }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.Connect(activity, from, to, direction:raise)
|
16
|
+
Rewrite(activity) do |new_map|
|
17
|
+
new_map[from][direction] = to
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Deep-clones an Activity's circuit and allows to alter its map by yielding it.
|
22
|
+
#
|
23
|
+
# activity = Circuit::Activity::Rewrite(activity) do |map, evt|
|
24
|
+
# map[some_task] = { Circuit::Right => evt[:End] }
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# You can only add events as they might already be in use in the existing map.
|
28
|
+
#
|
29
|
+
# :private:
|
30
|
+
def self.Rewrite(activity, debug:{}, events:{})
|
31
|
+
# decompose Activity and Circuit.
|
32
|
+
circuit, original_events = activity.values
|
33
|
+
map, end_events, original_debug = circuit.to_fields
|
34
|
+
|
35
|
+
original_events = original_events.to_h
|
36
|
+
events.each { |k, v| original_events[k] = (original_events[k] || {}).merge(v) }
|
37
|
+
|
38
|
+
# events = events.to_h.merge(added_events) # add new events.
|
39
|
+
debug = original_debug.to_h.merge(debug) # add new events.
|
40
|
+
|
41
|
+
new_map = {} # deep-dup.
|
42
|
+
map.each { |act, outputs| new_map[act] = outputs.dup }
|
43
|
+
|
44
|
+
# recompose to an Activity.
|
45
|
+
# new_map is mutable.
|
46
|
+
Circuit::Activity(debug, original_events) { |evts| yield(new_map, evts); new_map }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Circuit
|
3
|
+
class Trace
|
4
|
+
# TODO:
|
5
|
+
# * Struct for debug_item
|
6
|
+
module Present
|
7
|
+
FREE_SPACE = (' ' * 3).freeze
|
8
|
+
module_function
|
9
|
+
|
10
|
+
def tree(stack, level = 1)
|
11
|
+
stack.each do |debug_item|
|
12
|
+
puts FREE_SPACE * level + delimeter(stack, debug_item) + '--' + '> ' + to_name(debug_item) + to_options(debug_item)
|
13
|
+
|
14
|
+
if debug_item.last.is_a?(Array)
|
15
|
+
tree(debug_item.last, level + 1)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# private
|
21
|
+
|
22
|
+
def to_name(debug_item)
|
23
|
+
track = debug_item[2]
|
24
|
+
klass = track.class == Class ? track : track.class
|
25
|
+
color = color_map[klass]
|
26
|
+
|
27
|
+
return debug_item[0].to_s unless color
|
28
|
+
colorify(debug_item[0], color)
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_options(debug_item)
|
32
|
+
debug_item[4]
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
def colorify(string, color)
|
38
|
+
"\e[#{color_table[color]}m#{string}\e[0m"
|
39
|
+
end
|
40
|
+
|
41
|
+
def color_map
|
42
|
+
{
|
43
|
+
Trailblazer::Circuit::Start => :blue,
|
44
|
+
Trailblazer::Circuit::End => :pink,
|
45
|
+
Trailblazer::Circuit::Right => :green,
|
46
|
+
Trailblazer::Circuit::Left => :red
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def color_table
|
51
|
+
{
|
52
|
+
red: 31,
|
53
|
+
green: 32,
|
54
|
+
yellow: 33,
|
55
|
+
blue: 34,
|
56
|
+
pink: 35
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def delimeter(stack, debug_item)
|
61
|
+
if stack.last == debug_item || debug_item.last.is_a?(Array)
|
62
|
+
'`'
|
63
|
+
else
|
64
|
+
'|'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class Trailblazer::Circuit
|
2
|
+
module Task
|
3
|
+
module_function
|
4
|
+
# Convenience functions for tasks. Totally optional.
|
5
|
+
|
6
|
+
# Task::Binary aka "step"
|
7
|
+
# Step is binary task: true=> Right, false=>Left.
|
8
|
+
# Step call proc.(options, flow_options)
|
9
|
+
# Step is supposed to run Option::KW, so `step` should be Option::KW.
|
10
|
+
#
|
11
|
+
# Returns task to call the proc with (options, flow_options), omitting `direction`.
|
12
|
+
# When called, the task always returns a direction signal.
|
13
|
+
def Binary(step, on_true=Right, on_false=Left)
|
14
|
+
->(direction, *args) do # Activity/Task interface.
|
15
|
+
[ step.(direction, *args) ? on_true : on_false, *args ] # <=> Activity/Task interface
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module Args
|
20
|
+
module_function
|
21
|
+
# :private:
|
22
|
+
# Return task to call the proc with keyword arguments. Ruby >= 2.0.
|
23
|
+
# This is used by `Operation::step` to wrap the argument and make it
|
24
|
+
# callable in the circuit.
|
25
|
+
def KW(proc)
|
26
|
+
if proc.is_a? Symbol
|
27
|
+
->(*args) { meth!(proc, *args) } # Activity interface
|
28
|
+
else
|
29
|
+
->(*args) { call!(proc, *args) } # Activity interface
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# DISCUSS: standardize tmp_options.
|
34
|
+
# Calls `proc` with a step interface.
|
35
|
+
def call!(proc, direction, options, flow_options, tmp_options={})
|
36
|
+
# proc.(options, **options.to_hash(tmp_options))
|
37
|
+
proc.(options, **options.to_hash)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Make the context's instance method a "lambda" and reuse #call!.
|
41
|
+
# TODO: should we make :context a kwarg?
|
42
|
+
def meth!(proc, direction, options, flow_options, *args)
|
43
|
+
call!(flow_options[:context].method(proc), direction, options, flow_options, *args)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Circuit
|
3
|
+
# direction, result = circuit.( circuit[:Start], options, runner: Circuit::Trace.new, stack: [] )
|
4
|
+
|
5
|
+
# Every `activity.call` is considered nested
|
6
|
+
class Trace
|
7
|
+
def call(activity, direction, args, debug:raise, stack:raise, **flow_options)
|
8
|
+
activity_name, _ = debug[activity]
|
9
|
+
activity_name ||= activity
|
10
|
+
|
11
|
+
Run.(activity, direction, args, stack:[], **flow_options).tap do |direction, outgoing_options, **flow_options| # TODO: USE KW ARG FOR :stack
|
12
|
+
# raise activity_name.inspect if flow_options[:stack].nil? # TODO: remove this.
|
13
|
+
# TODO: fix the inspect, we need a snapshot, deep-nested.
|
14
|
+
stack << [activity_name, activity, direction, outgoing_options, outgoing_options.inspect, flow_options[:stack].any? ? flow_options[:stack] : nil ]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end # Trace
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require "trailblazer/circuit/version"
|
2
|
+
|
3
|
+
# Start, Suspend, Resume, End can return something other than the next symbol?
|
4
|
+
# Nested could replace options with local options
|
5
|
+
|
6
|
+
|
7
|
+
module Trailblazer
|
8
|
+
# Circuit executes ties, finds next step and stops when reaching a Stop signal (or derived from).
|
9
|
+
#
|
10
|
+
# circuit.()
|
11
|
+
#
|
12
|
+
# Cicuit doesn't know anything about contexts, options, etc. tasks: what steps follows? call it!
|
13
|
+
class Circuit
|
14
|
+
def initialize(map, stop_events, name)
|
15
|
+
@name = name
|
16
|
+
@map = map
|
17
|
+
@stop_events = stop_events
|
18
|
+
end
|
19
|
+
|
20
|
+
# the idea is to always have a breakpoint state that has only one outgoing edge. we then start from
|
21
|
+
# that vertix. it's up to the caller to test if the "persisted" state == requested state.
|
22
|
+
# activity: where to start
|
23
|
+
Run = ->(activity, direction, *args) { activity.(direction, *args) }
|
24
|
+
|
25
|
+
def call(activity, args, runner: Run, **o) # DISCUSS: should start activity be @activity and we omit it here?
|
26
|
+
# TODO: args
|
27
|
+
direction = nil
|
28
|
+
|
29
|
+
loop do
|
30
|
+
# puts "[#{@name}]. #{activity}"
|
31
|
+
direction, args = runner.(activity, direction, args, runner: runner, debug: @name, **o)
|
32
|
+
|
33
|
+
# last task in a process is always either its Stop or its Suspend.
|
34
|
+
return [ direction, args, **o ] if @stop_events.include?(activity)
|
35
|
+
|
36
|
+
activity = next_for(activity, direction) do |next_activity, in_map|
|
37
|
+
# puts "[#{@name}]...`#{activity}`[#{direction}] => #{next_activity}"
|
38
|
+
|
39
|
+
raise IllegalInputError.new("#{@name} #{activity}") unless in_map
|
40
|
+
# last activity didn't emit knowns signal, it's not connected.
|
41
|
+
raise IllegalOutputSignalError.new("from #{@name};;#{activity}"+ direction.inspect) unless next_activity
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_fields
|
47
|
+
[ @map, @stop_events, @name]
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def next_for(last_activity, emitted_direction)
|
52
|
+
# p @map
|
53
|
+
in_map = false
|
54
|
+
cfg = @map.keys.find { |t| t == last_activity } and in_map = true
|
55
|
+
cfg = @map[cfg] if cfg
|
56
|
+
cfg ||= {}
|
57
|
+
next_activity = cfg[emitted_direction]
|
58
|
+
yield next_activity, in_map
|
59
|
+
|
60
|
+
next_activity
|
61
|
+
end
|
62
|
+
|
63
|
+
class IllegalInputError < RuntimeError
|
64
|
+
end
|
65
|
+
|
66
|
+
class IllegalOutputSignalError < RuntimeError
|
67
|
+
end
|
68
|
+
|
69
|
+
def Right
|
70
|
+
Right
|
71
|
+
end
|
72
|
+
|
73
|
+
def Left
|
74
|
+
Left
|
75
|
+
end
|
76
|
+
|
77
|
+
class End
|
78
|
+
def initialize(name, options={})
|
79
|
+
@name = name
|
80
|
+
@options = options
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_s
|
84
|
+
%{#<End: #{@name} #{@options.inspect}>}
|
85
|
+
end
|
86
|
+
|
87
|
+
def inspect
|
88
|
+
to_s
|
89
|
+
end
|
90
|
+
|
91
|
+
def call(direction, *args)
|
92
|
+
[ self, *args ]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class Start < End
|
97
|
+
def call(direction, *args)
|
98
|
+
[Right, *args]
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_s
|
102
|
+
%{#<Start: #{@name} #{@options.inspect}>}
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# # run a nested process.
|
107
|
+
def self.Nested(activity, start_with=activity[:Start])
|
108
|
+
# TODO: currently, we only support only 1 start event. you can use multiple in BPMN.
|
109
|
+
# "The BPMN standard allows for multiple start and end events to be used at the same process level. "
|
110
|
+
->(start_at, options, *args) {
|
111
|
+
# puts "@@@@@ #{options.inspect}"
|
112
|
+
activity.(start_with, options, *args)
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
class Direction; end
|
117
|
+
class Right < Direction; end
|
118
|
+
class Left < Direction; end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
require "trailblazer/circuit/activity"
|
123
|
+
require "trailblazer/circuit/task"
|
124
|
+
require "trailblazer/circuit/alter"
|
125
|
+
require "trailblazer/circuit/trace"
|
@@ -0,0 +1 @@
|
|
1
|
+
require "trailblazer/circuit"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'trailblazer/circuit/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "trailblazer-circuit"
|
7
|
+
spec.version = Trailblazer::Circuit::VERSION
|
8
|
+
spec.authors = ["Nick Sutterer"]
|
9
|
+
spec.email = ["apotonick@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = %q{BPMN-compliant workflows or state machines.}
|
12
|
+
spec.description = %q{BPMN-compliant workflows or state machines. Used in Trailblazer's Operation to implement the Railway.}
|
13
|
+
spec.homepage = "http://trailblazer.to/gems/workflow"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
16
|
+
f.match(%r{^(test|spec|features)/})
|
17
|
+
end
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.14"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
25
|
+
spec.add_development_dependency "pry"
|
26
|
+
|
27
|
+
spec.required_ruby_version = '>= 2.0.0'
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: trailblazer-circuit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nick Sutterer
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-04-27 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: '1.14'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.14'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
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
|
+
description: BPMN-compliant workflows or state machines. Used in Trailblazer's Operation
|
70
|
+
to implement the Railway.
|
71
|
+
email:
|
72
|
+
- apotonick@gmail.com
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- ".travis.yml"
|
79
|
+
- CHANGES.md
|
80
|
+
- Gemfile
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- lib/trailblazer-circuit.rb
|
84
|
+
- lib/trailblazer/circuit.rb
|
85
|
+
- lib/trailblazer/circuit/activity.rb
|
86
|
+
- lib/trailblazer/circuit/alter.rb
|
87
|
+
- lib/trailblazer/circuit/present.rb
|
88
|
+
- lib/trailblazer/circuit/task.rb
|
89
|
+
- lib/trailblazer/circuit/trace.rb
|
90
|
+
- lib/trailblazer/circuit/version.rb
|
91
|
+
- trailblazer-circuit.gemspec
|
92
|
+
homepage: http://trailblazer.to/gems/workflow
|
93
|
+
licenses: []
|
94
|
+
metadata: {}
|
95
|
+
post_install_message:
|
96
|
+
rdoc_options: []
|
97
|
+
require_paths:
|
98
|
+
- lib
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 2.0.0
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
requirements: []
|
110
|
+
rubyforge_project:
|
111
|
+
rubygems_version: 2.5.2
|
112
|
+
signing_key:
|
113
|
+
specification_version: 4
|
114
|
+
summary: BPMN-compliant workflows or state machines.
|
115
|
+
test_files: []
|