trailblazer-circuit 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|