trailblazer-circuit 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES.md +6 -0
- data/lib/trailblazer/circuit/present.rb +19 -16
- data/lib/trailblazer/circuit/task.rb +1 -1
- data/lib/trailblazer/circuit/trace.rb +68 -13
- data/lib/trailblazer/circuit/version.rb +1 -1
- data/lib/trailblazer/circuit/wrapped.rb +58 -0
- data/lib/trailblazer/circuit.rb +23 -10
- data/lib/trailblazer/context.rb +78 -0
- data/trailblazer-circuit.gemspec +2 -0
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c9e50c60fbd29ee44bc11f122198acec25c02122
|
4
|
+
data.tar.gz: de1fb71290d6c4a817827bfad946cf739a1d4291
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8b5ea44e5ca1a618da4c4b4c5130d27700ada1766c621e4753e4d58765995f6799490e3b842f89183ec1ea860704e0c3d55549270cc1a539fab93b5f503269c
|
7
|
+
data.tar.gz: 0cf6a6dd1cc39496a236eb2d816e360efca4f60d263a0d591a8f799890bc5e29bd3a8a0ccdcf9b89aa04b11071b40cf7aa0b196e6a7d41f2bf1273df2c646747
|
data/CHANGES.md
CHANGED
@@ -1,24 +1,35 @@
|
|
1
|
+
require "hirb"
|
2
|
+
|
1
3
|
module Trailblazer
|
2
4
|
class Circuit
|
3
|
-
|
5
|
+
module Trace
|
4
6
|
# TODO:
|
5
7
|
# * Struct for debug_item
|
6
8
|
module Present
|
7
|
-
FREE_SPACE = (' ' * 3).freeze
|
8
9
|
module_function
|
9
10
|
|
10
|
-
def tree(stack, level =
|
11
|
+
def tree(stack, level=1, tree=[])
|
12
|
+
tree_for(stack, level, tree)
|
13
|
+
|
14
|
+
Hirb::Console.format_output(tree, class: :tree, type: :directory)
|
15
|
+
end
|
16
|
+
|
17
|
+
def tree_for(stack, level, tree)
|
11
18
|
stack.each do |debug_item|
|
12
|
-
|
19
|
+
if debug_item.size == 2 # flat
|
20
|
+
tree << [ level, debug_item[0].last[debug_item[0][0]] || debug_item[0][0] ]
|
21
|
+
else # nesting
|
22
|
+
tree << [ level, debug_item[0][0] ]
|
23
|
+
|
24
|
+
tree_for(debug_item[1..-2], level + 1, tree)
|
13
25
|
|
14
|
-
|
15
|
-
tree(debug_item.last, level + 1)
|
26
|
+
tree << [ level+1, debug_item[-1][0] ]
|
16
27
|
end
|
28
|
+
|
29
|
+
tree
|
17
30
|
end
|
18
31
|
end
|
19
32
|
|
20
|
-
# private
|
21
|
-
|
22
33
|
def to_name(debug_item)
|
23
34
|
track = debug_item[2]
|
24
35
|
klass = track.class == Class ? track : track.class
|
@@ -56,14 +67,6 @@ module Trailblazer
|
|
56
67
|
pink: 35
|
57
68
|
}
|
58
69
|
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
70
|
end
|
68
71
|
end
|
69
72
|
end
|
@@ -40,7 +40,7 @@ class Trailblazer::Circuit
|
|
40
40
|
# Make the context's instance method a "lambda" and reuse #call!.
|
41
41
|
# TODO: should we make :context a kwarg?
|
42
42
|
def meth!(proc, direction, options, flow_options, *args)
|
43
|
-
call!(flow_options[:
|
43
|
+
call!(flow_options[:exec_context].method(proc), direction, options, flow_options, *args)
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
@@ -1,22 +1,77 @@
|
|
1
1
|
module Trailblazer
|
2
2
|
class Circuit
|
3
|
-
#
|
3
|
+
# Trace#call will call the activities and trace what steps are called, options passed,
|
4
|
+
# and the order and nesting.
|
5
|
+
#
|
6
|
+
# stack, _ = Trailblazer::Circuit::Trace.(activity, activity[:Start], { id: 1 })
|
7
|
+
# puts Trailblazer::Circuit::Present.tree(stack) # renders the trail.
|
8
|
+
module Trace
|
9
|
+
def self.call(activity, direction, options, flow_options={})
|
10
|
+
# activity_wrap is the circuit/pipeline that wraps each step and implements tracing (and more, like input/output contracts, etc!).
|
11
|
+
activity_wrap = Activity::Before( Activity::Wrapped::Activity, Activity::Wrapped::Call, Trace.method(:capture_args), direction: Right )
|
12
|
+
activity_wrap = Activity::Before( activity_wrap, Activity::Wrapped::Activity[:End], Trace.method(:capture_return), direction: Right )
|
4
13
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def call(activity, direction, args, debug:raise, stack:raise, **flow_options)
|
9
|
-
activity_name, _ = debug[activity]
|
10
|
-
activity_name ||= activity
|
14
|
+
step_runners = {
|
15
|
+
nil => activity_wrap, # call all steps with tracing.
|
16
|
+
}
|
11
17
|
|
12
|
-
|
13
|
-
|
18
|
+
tracing_flow_options = {
|
19
|
+
runner: Activity::Wrapped::Runner,
|
20
|
+
stack: Circuit::Trace::Stack.new,
|
21
|
+
step_runners: step_runners,
|
22
|
+
debug: activity.circuit.instance_variable_get(:@name)
|
23
|
+
}
|
14
24
|
|
15
|
-
|
16
|
-
stack << [activity_name, activity, direction, options, options.inspect, _flow_options[:stack].any? ? _flow_options[:stack] : nil ]
|
25
|
+
direction, options, flow_options = activity.( direction, options, tracing_flow_options.merge(flow_options) )
|
17
26
|
|
18
|
-
return direction, options,
|
27
|
+
return flow_options[:stack].to_a, direction, options, flow_options
|
19
28
|
end
|
20
|
-
|
29
|
+
|
30
|
+
def self.capture_args(direction, options, flow_options, wrap_config, original_flow_options)
|
31
|
+
original_flow_options[:stack].indent!
|
32
|
+
|
33
|
+
original_flow_options[:stack] << [ wrap_config[:step], :args, nil, options.dup, original_flow_options[:debug] ]
|
34
|
+
|
35
|
+
[ direction, options, flow_options, wrap_config, original_flow_options ]
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.capture_return(direction, options, flow_options, wrap_config, original_flow_options)
|
39
|
+
original_flow_options[:stack] << [ wrap_config[:step], :return, flow_options[:result_direction], options.dup ]
|
40
|
+
|
41
|
+
original_flow_options[:stack].unindent!
|
42
|
+
|
43
|
+
[ direction, options, flow_options, wrap_config, original_flow_options ]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Mutable/stateful per design. We want a (global) stack!
|
47
|
+
class Stack
|
48
|
+
def initialize
|
49
|
+
@nested = []
|
50
|
+
@stack = [ @nested ]
|
51
|
+
end
|
52
|
+
|
53
|
+
def indent!
|
54
|
+
current << indented = []
|
55
|
+
@stack << indented
|
56
|
+
end
|
57
|
+
|
58
|
+
def unindent!
|
59
|
+
@stack.pop
|
60
|
+
end
|
61
|
+
|
62
|
+
def <<(args)
|
63
|
+
current << args
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_a
|
67
|
+
@nested
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def current
|
72
|
+
@stack.last
|
73
|
+
end
|
74
|
+
end # Stack
|
75
|
+
end
|
21
76
|
end
|
22
77
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class Trailblazer::Circuit
|
2
|
+
module Activity::Wrapped
|
3
|
+
# Input = ->(direction, options, flow_options) { [direction, options, flow_options] }
|
4
|
+
|
5
|
+
def self.call_activity(direction, options, flow_options, wrap_config, original_flow_options)
|
6
|
+
task = wrap_config[:step]
|
7
|
+
|
8
|
+
# Call the actual task we're wrapping here.
|
9
|
+
wrap_config[:result_direction], options, flow_options = task.( direction, options, original_flow_options )
|
10
|
+
|
11
|
+
[ direction, options, flow_options, wrap_config, original_flow_options ]
|
12
|
+
end
|
13
|
+
|
14
|
+
Call = method(:call_activity)
|
15
|
+
|
16
|
+
# Output = ->(direction, options, flow_options) { [direction, options, flow_options] }
|
17
|
+
|
18
|
+
class End < Trailblazer::Circuit::End
|
19
|
+
def call(direction, options, flow_options, wrap_config, *args)
|
20
|
+
[ wrap_config[:result_direction], options, flow_options, wrap_config, *args ]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Activity = Trailblazer::Circuit::Activity({ id: "task.wrap" }, end: { default: End.new(:default) }) do |act|
|
25
|
+
{
|
26
|
+
act[:Start] => { Right => Call }, # options from outside
|
27
|
+
# Input => { Circuit::Right => Trace::CaptureArgs },
|
28
|
+
# MyInject => { Circuit::Right => Trace::CaptureArgs },
|
29
|
+
# Trace::CaptureArgs => { Circuit::Right => Call },
|
30
|
+
Call => { Right => act[:End] },
|
31
|
+
# Trace::CaptureReturn => { Circuit::Right => Output },
|
32
|
+
# Output => { Circuit::Right => act[:End] }
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
# Find the respective wrap per task, and run it.
|
37
|
+
class Runner
|
38
|
+
# private flow_options[ :step_runners ]
|
39
|
+
def self.call(task, direction, options, flow_options)
|
40
|
+
# TODO: test this decider!
|
41
|
+
task_wrap = flow_options[:step_runners][task] || flow_options[:step_runners][nil] # DISCUSS: default could be more explicit@
|
42
|
+
|
43
|
+
# we can't pass :runner in here since the Step::Pipeline would call itself again, then.
|
44
|
+
# However, we need the runner in nested activities.
|
45
|
+
wrap_config = { step: task }
|
46
|
+
|
47
|
+
# Call the task_wrap circuit:
|
48
|
+
# |-- Start
|
49
|
+
# |-- Trace.capture_args [optional]
|
50
|
+
# |-- Call (call actual task)
|
51
|
+
# |-- Trace.capture_return [optional]
|
52
|
+
# |-- End
|
53
|
+
# Pass empty flow_options to the task_wrap, so it doesn't infinite-loop.
|
54
|
+
task_wrap.( task_wrap[:Start], options, {}, wrap_config, flow_options ) # all tasks in Wrap have to implement this signature.
|
55
|
+
end
|
56
|
+
end # Runner
|
57
|
+
end
|
58
|
+
end
|
data/lib/trailblazer/circuit.rb
CHANGED
@@ -29,16 +29,15 @@ module Trailblazer
|
|
29
29
|
#
|
30
30
|
# @param activity A task from the circuit where to start
|
31
31
|
# @param args An array of options passed to the first task.
|
32
|
-
def call(activity,
|
33
|
-
|
34
|
-
|
35
|
-
flow_options = { runner: runner, debug: @name }.merge(flow_options) # DISCUSS: make this better?
|
32
|
+
def call(activity, options, flow_options={}, *args)
|
33
|
+
direction = nil
|
34
|
+
runner = flow_options[:runner] || Run
|
36
35
|
|
37
36
|
loop do
|
38
|
-
direction,
|
37
|
+
direction, options, flow_options = runner.( activity, direction, options, flow_options, *args )
|
39
38
|
|
40
39
|
# Stop execution of the circuit when we hit a stop event (< End). This could be an activity's End or Suspend.
|
41
|
-
return [ direction,
|
40
|
+
return [ direction, options, flow_options ] if @stop_events.include?(activity)
|
42
41
|
|
43
42
|
activity = next_for(activity, direction) do |next_activity, in_map|
|
44
43
|
activity_name = @name[activity] || activity # TODO: this must be implemented only once, somewhere.
|
@@ -97,10 +96,20 @@ module Trailblazer
|
|
97
96
|
end
|
98
97
|
|
99
98
|
# Builder for running a nested process from a specific `start_at` position.
|
100
|
-
def self.Nested(
|
101
|
-
|
102
|
-
|
103
|
-
|
99
|
+
def self.Nested(*args)
|
100
|
+
Nested.new(*args)
|
101
|
+
end
|
102
|
+
|
103
|
+
class Nested
|
104
|
+
def initialize(activity, start_with=activity[:Start])
|
105
|
+
@activity, @start_with = activity, start_with
|
106
|
+
end
|
107
|
+
|
108
|
+
def call(start_at, *args)
|
109
|
+
@activity.(@start_with, *args)
|
110
|
+
end
|
111
|
+
|
112
|
+
attr_reader :activity
|
104
113
|
end
|
105
114
|
|
106
115
|
class Direction; end
|
@@ -113,3 +122,7 @@ require "trailblazer/circuit/activity"
|
|
113
122
|
require "trailblazer/circuit/task"
|
114
123
|
require "trailblazer/circuit/alter"
|
115
124
|
require "trailblazer/circuit/trace"
|
125
|
+
require "trailblazer/circuit/present"
|
126
|
+
require "trailblazer/circuit/wrapped"
|
127
|
+
|
128
|
+
require "trailblazer/context"
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# "di" step_di: order: 1. runtime, 2. { contract.class: A } (dynamic at runtime?)
|
2
|
+
|
3
|
+
=begin
|
4
|
+
* test "stripe scenario"
|
5
|
+
def pay!(options, stripe: Stripe.new) # can be overridden in e.g. test via runtime dependencies or container.
|
6
|
+
* problem: HOW TO payment.stripe.engine ? step M, underscore_dots: true
|
7
|
+
* Create["payment.stripe.engine"] = Stripe.new
|
8
|
+
|
9
|
+
=end
|
10
|
+
|
11
|
+
=begin
|
12
|
+
* In an operation, there's always a Context object that holds the Containers and the initial mutable data.
|
13
|
+
* SIMPLE/CURRENT WAY: we can simply pass this on without wrapping (no boundaries)
|
14
|
+
* wrap for Nested, unwrap after Nested
|
15
|
+
=end
|
16
|
+
|
17
|
+
# TODO: mark/make all but mutable_options as frozen.
|
18
|
+
# The idea of Skill is to have a generic, ordered read/write interface that
|
19
|
+
# collects mutable runtime-computed data while providing access to compile-time
|
20
|
+
# information.
|
21
|
+
# The runtime-data takes precedence over the class data.
|
22
|
+
module Trailblazer
|
23
|
+
# Holds local options (aka `mutable_options`) and "original" options from the "outer"
|
24
|
+
# activity (aka wrapped_options).
|
25
|
+
|
26
|
+
# only public creator: Build
|
27
|
+
class Context # :data object:
|
28
|
+
def initialize(wrapped_options, mutable_options)
|
29
|
+
@wrapped_options, @mutable_options = wrapped_options, mutable_options
|
30
|
+
end
|
31
|
+
|
32
|
+
def [](name)
|
33
|
+
@mutable_options[name] || @wrapped_options[name]
|
34
|
+
end
|
35
|
+
|
36
|
+
def []=(name, value)
|
37
|
+
@mutable_options[name] = value
|
38
|
+
end
|
39
|
+
|
40
|
+
def merge(hash)
|
41
|
+
original, mutable_options = decompose
|
42
|
+
|
43
|
+
ctx = Trailblazer::Context( original, mutable_options.merge(hash) )
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return the Context's two components. Used when computing the new output for
|
47
|
+
# the next activity.
|
48
|
+
def decompose
|
49
|
+
# it would be cool if that could "destroy" the original object.
|
50
|
+
# also, if those hashes were immutable, that'd be amazing.
|
51
|
+
[ @wrapped_options, @mutable_options ]
|
52
|
+
end
|
53
|
+
|
54
|
+
# TODO: massive performance bottleneck. also, we could already "know" here what keys the
|
55
|
+
# transformation wants.
|
56
|
+
def to_hash
|
57
|
+
{}.tap do |hash|
|
58
|
+
# arr = to_runtime_data << to_mutable_data << tmp_options
|
59
|
+
|
60
|
+
# the "key" here is to call to_hash on all containers.
|
61
|
+
[ @wrapped_options.to_hash, @mutable_options.to_hash ].each do |options|
|
62
|
+
options.each { |k, v| hash[k.to_sym] = v }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def Build
|
68
|
+
wrapped_options, mutable_options = *decompose
|
69
|
+
wrapped_options = yield(wrapped_options, mutable_options) if block_given?
|
70
|
+
|
71
|
+
Trailblazer::Context(wrapped_options)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.Context(wrapped_options, mutable_options={})
|
76
|
+
Context.new(wrapped_options, mutable_options)
|
77
|
+
end
|
78
|
+
end
|
data/trailblazer-circuit.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trailblazer-circuit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-06-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: hirb
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
description: BPMN-compliant workflows or state machines. Used in Trailblazer's Operation
|
70
84
|
to implement the Railway.
|
71
85
|
email:
|
@@ -89,6 +103,8 @@ files:
|
|
89
103
|
- lib/trailblazer/circuit/testing.rb
|
90
104
|
- lib/trailblazer/circuit/trace.rb
|
91
105
|
- lib/trailblazer/circuit/version.rb
|
106
|
+
- lib/trailblazer/circuit/wrapped.rb
|
107
|
+
- lib/trailblazer/context.rb
|
92
108
|
- trailblazer-circuit.gemspec
|
93
109
|
homepage: http://trailblazer.to/gems/workflow
|
94
110
|
licenses: []
|