trailblazer-circuit 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
+ SHA1:
3
+ metadata.gz: 5a7e34849fb98696e6c453bbe8177d90b3932ecd
4
+ data.tar.gz: 5c2d5ab3e575f380fda9f2976bddd77d2a5b9fb1
5
+ SHA512:
6
+ metadata.gz: 9e64cefc10dfc6606ebe9be18cc918145ba4b1a4a62854b35a00e97b0dc26233a85d81e39b96f0534dc469f763c6796618b1350480bea30ec566f3c1a45ef1f1
7
+ data.tar.gz: c71327201a57cddb155d70c5cb6e1bbb67daf040a988be09045831c2e93900e05072689f564ad0de1a1a6eafc837d4f5a956a269259f5fe09f9fc1b08684fdf0
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/
data/.travis.yml ADDED
@@ -0,0 +1,13 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.0
5
+ - 2.1
6
+ - 2.2
7
+ - 2.3.3
8
+ - 2.4.0
9
+ matrix:
10
+ include:
11
+ - rvm: jruby-9.1.7.0
12
+ env: JRUBY_OPTS="--profile.api"
13
+ before_install: gem install bundler
data/CHANGES.md ADDED
@@ -0,0 +1,3 @@
1
+ # 0.0.1
2
+
3
+ * First release into an unsuspecting world. 🚀
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in workflow.gemspec
4
+ gemspec
5
+
6
+ gem "minitest-line"
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,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -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,5 @@
1
+ module Trailblazer
2
+ class Circuit
3
+ VERSION = "0.0.1"
4
+ end
5
+ 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: []