trailblazer-activity 0.5.2 → 0.6.0
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/.travis.yml +4 -10
- data/CHANGES.md +22 -0
- data/lib/trailblazer/activity/dsl/add_task.rb +2 -2
- data/lib/trailblazer/activity/dsl/helper.rb +5 -0
- data/lib/trailblazer/activity/dsl/magnetic/builder/default_normalizer.rb +3 -3
- data/lib/trailblazer/activity/dsl/magnetic/builder/normalizer.rb +15 -10
- data/lib/trailblazer/activity/dsl/magnetic/builder/path.rb +26 -11
- data/lib/trailblazer/activity/dsl/magnetic/builder.rb +6 -6
- data/lib/trailblazer/activity/dsl/magnetic/finalizer.rb +1 -1
- data/lib/trailblazer/activity/dsl/magnetic/process_options.rb +2 -2
- data/lib/trailblazer/activity/dsl/strategy/path.rb +0 -1
- data/lib/trailblazer/activity/introspect.rb +39 -14
- data/lib/trailblazer/activity/present.rb +4 -1
- data/lib/trailblazer/activity/structures.rb +4 -4
- data/lib/trailblazer/activity/task_wrap/call_task.rb +1 -1
- data/lib/trailblazer/activity/task_wrap/merge.rb +1 -1
- data/lib/trailblazer/activity/task_wrap/runner.rb +14 -4
- data/lib/trailblazer/activity/task_wrap/trace.rb +25 -9
- data/lib/trailblazer/activity/task_wrap.rb +7 -19
- data/lib/trailblazer/activity/testing.rb +32 -0
- data/lib/trailblazer/activity/trace.rb +4 -7
- data/lib/trailblazer/activity/version.rb +1 -1
- data/lib/trailblazer/activity.rb +10 -10
- data/lib/trailblazer/circuit.rb +9 -21
- data/lib/trailblazer-activity.rb +1 -0
- metadata +3 -3
- data/inspeccccccccct.rb +0 -19
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 656825c80059310cf4a2d89a2214a20618bbd688c182285e8d3c24c8475b8233
|
|
4
|
+
data.tar.gz: 4fb358255fe07a12fcb94172a05f466ce3ac0cb66d3b9502a3622d2a4392a71f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 74361dffc29b0108e198a3a2bee9637b6519b2ad56b4fb9172aceffc92bee1bcb5dc5e1417dab33d9ba48340749bd5f4306bd16a2c22a1f5eb66cfea4991f04b
|
|
7
|
+
data.tar.gz: ad2fd6a2d6d4df1a2c8e167fd395ef05899f7389b95f8219239d4533c75632001992ff38eb240b300d1b2260544b85abd276372783f8a4c2480afc2a3e45a1a5
|
data/.travis.yml
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
sudo: false
|
|
2
2
|
language: ruby
|
|
3
3
|
rvm:
|
|
4
|
-
|
|
5
|
-
- 2.
|
|
6
|
-
- 2.
|
|
7
|
-
- 2.
|
|
8
|
-
- 2.4.3
|
|
9
|
-
- 2.5.0
|
|
10
|
-
matrix:
|
|
11
|
-
include:
|
|
12
|
-
- rvm: jruby-head
|
|
13
|
-
env: JRUBY_OPTS="--profile.api"
|
|
4
|
+
- 2.5.1
|
|
5
|
+
- 2.4.4
|
|
6
|
+
- 2.3.7
|
|
7
|
+
- 2.2.10
|
|
14
8
|
before_install: gem install bundler
|
data/CHANGES.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
# 0.6.0
|
|
2
|
+
|
|
3
|
+
* The `:task` option in `Circuit::call` is now named `:start_task` for consistency.
|
|
4
|
+
* Removed the `:argumenter` option for `Activity::call`. Instead, an `Activity` passes itself via the `:activity` option.
|
|
5
|
+
* Removed the `:extension` option. Instead, any option from the DSL that `is_a?(DSL::Extension)` will be processed in `add_task!`.
|
|
6
|
+
* Replace argumenters with `TaskWrap::invoke`. This simplifies the whole `call` process, and moves all initialization of args to the top.
|
|
7
|
+
* Added `Introspect::Graph::find`.
|
|
8
|
+
* Removed `Introspect::Enumerator` in favor of the `Graph` API.
|
|
9
|
+
|
|
10
|
+
# 0.5.4
|
|
11
|
+
|
|
12
|
+
* Introducing `Introspect::Enumerator` and removing `Introspect.find`. `Enumerator` contains `Enumerable` and exposes all necessary utility methods.
|
|
13
|
+
|
|
14
|
+
# 0.5.3
|
|
15
|
+
|
|
16
|
+
* In Path(), allow referencing an existing task, instead of creating an end event.
|
|
17
|
+
This avoids having to use two `Output() => ..` and is much cleaner.
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
Path( end_id: :find_model) do .. end
|
|
21
|
+
```
|
|
22
|
+
|
|
1
23
|
# 0.5.2
|
|
2
24
|
|
|
3
25
|
* In `Path()`, we removed the `#path` method in favor of a cleaner `task` DSL method. We now use the default plus_poles `success` and `failure` everywhere for consistency. This means that a `task` has two outputs, and if you referenced `Output(:success)`, that would be only one of them. We're planning to have `pass` back which has one `success` plus_pole, only. This change makes the DSL wiring behavior much more consistent.
|
|
@@ -12,10 +12,10 @@ class Trailblazer::Activity < Module
|
|
|
12
12
|
self[:circuit] = circuit
|
|
13
13
|
self[:outputs] = outputs
|
|
14
14
|
|
|
15
|
-
_, local_options = returned_options
|
|
15
|
+
_, local_options, connections, sequence_options, extension_options = returned_options
|
|
16
16
|
|
|
17
17
|
# {Extension API} call all extensions.
|
|
18
|
-
|
|
18
|
+
extension_options.keys.collect { |ext| ext.( self, *returned_options, original_dsl_args: [name, task, options, block] ) }
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
end
|
|
@@ -15,6 +15,11 @@ module Trailblazer
|
|
|
15
15
|
# @api private
|
|
16
16
|
OutputSemantic = Struct.new(:value)
|
|
17
17
|
Track = Struct.new(:color)
|
|
18
|
+
Extension = Struct.new(:callable) do
|
|
19
|
+
def call(*args, &block)
|
|
20
|
+
callable.(*args, &block)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
18
23
|
|
|
19
24
|
# Shortcut functions for the DSL. These have no state.
|
|
20
25
|
module Helper
|
|
@@ -5,8 +5,8 @@ module Trailblazer
|
|
|
5
5
|
# task Callable, id: "success", before: "another"
|
|
6
6
|
class DefaultNormalizer
|
|
7
7
|
# Declarative::Variables
|
|
8
|
-
def self.build(plus_poles:,
|
|
9
|
-
return new(plus_poles: plus_poles
|
|
8
|
+
def self.build(plus_poles:, **options)
|
|
9
|
+
return new(plus_poles: plus_poles), options
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def initialize(**default_options)
|
|
@@ -19,7 +19,7 @@ module Trailblazer
|
|
|
19
19
|
|
|
20
20
|
local_options, sequence_options = Options.normalize( local_options, Activity::Schema::Dependencies.sequence_keywords )
|
|
21
21
|
|
|
22
|
-
return task, local_options, {}, sequence_options
|
|
22
|
+
return task, local_options, {}, sequence_options, {}
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
end
|
|
@@ -30,9 +30,9 @@ module Trailblazer
|
|
|
30
30
|
default_outputs: @default_outputs,
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
signal, (ctx, ) = @pipeline.( [ctx] )
|
|
33
|
+
signal, (ctx, ) = @pipeline.( [ctx], {} )
|
|
34
34
|
|
|
35
|
-
return ctx[:options][:task], ctx[:local_options], ctx[:connection_options], ctx[:sequence_options]
|
|
35
|
+
return ctx[:options][:task], ctx[:local_options], ctx[:connection_options], ctx[:sequence_options], ctx[:extension_options]
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
# needs the basic Normalizer
|
|
@@ -42,15 +42,18 @@ module Trailblazer
|
|
|
42
42
|
extend Trailblazer::Activity::Path( normalizer_class: DefaultNormalizer, plus_poles: PlusPoles.new.merge( Builder::Path.default_outputs.values ) ) # FIXME: the DefaultNormalizer actually doesn't need Left.
|
|
43
43
|
|
|
44
44
|
def self.split_options( ctx, task:, options:, ** )
|
|
45
|
-
keywords
|
|
45
|
+
keywords = extract_dsl_keywords(options)
|
|
46
|
+
extensions = extract_extensions(options)
|
|
46
47
|
|
|
47
48
|
# sort through the "original" user DSL options.
|
|
49
|
+
options, extension_options = Options.normalize( options, extensions ) # DISCUSS:
|
|
48
50
|
options, local_options = Options.normalize( options, keywords ) # DISCUSS:
|
|
49
51
|
local_options, sequence_options = Options.normalize( local_options, Activity::Schema::Dependencies.sequence_keywords )
|
|
50
52
|
|
|
51
53
|
ctx[:local_options],
|
|
52
54
|
ctx[:connection_options],
|
|
53
|
-
ctx[:sequence_options]
|
|
55
|
+
ctx[:sequence_options],
|
|
56
|
+
ctx[:extension_options] = local_options, options, sequence_options, extension_options
|
|
54
57
|
end
|
|
55
58
|
|
|
56
59
|
# Filter out connections, e.g. `Output(:fail_fast) => :success` and return only the keywords like `:id` or `:replace`.
|
|
@@ -58,17 +61,19 @@ module Trailblazer
|
|
|
58
61
|
options.keys - options.keys.find_all { |k| connection_classes.include?( k.class ) }
|
|
59
62
|
end
|
|
60
63
|
|
|
64
|
+
def self.extract_extensions(options, extensions_classes = [Activity::DSL::Extension])
|
|
65
|
+
options.keys.find_all { |k| extensions_classes.include?( k.class ) }
|
|
66
|
+
end
|
|
67
|
+
|
|
61
68
|
# FIXME; why don't we use the extensions passed into the initializer?
|
|
62
|
-
def self.
|
|
63
|
-
|
|
69
|
+
def self.initialize_extension_option( ctx, options:, ** )
|
|
70
|
+
ctx[:options] = options.merge( Activity::DSL::Extension.new( Activity::DSL.method(:record) ) => true )
|
|
64
71
|
end
|
|
65
72
|
|
|
66
|
-
# Normalizes ctx[:options]
|
|
73
|
+
# Normalizes ctx[:options] (the user-options via the DSL) into the internal Hash format.
|
|
67
74
|
def self.normalize_for_macro( ctx, task:, options:, task_builder:, ** )
|
|
68
75
|
ctx[:options] =
|
|
69
76
|
if task.is_a?(::Hash) # macro.
|
|
70
|
-
options = options.merge(extension: (options[:extension]||[])+(task[:extension]||[]) ) # FIXME.
|
|
71
|
-
|
|
72
77
|
task.merge(options) # Note that the user options are merged over the macro options.
|
|
73
78
|
else # user step
|
|
74
79
|
{ id: task }
|
|
@@ -93,9 +98,9 @@ module Trailblazer
|
|
|
93
98
|
.merge(local_options)
|
|
94
99
|
end
|
|
95
100
|
|
|
101
|
+
task Activity::TaskBuilder::Binary( method(:initialize_extension_option) ), id: "initialize_extension_option"
|
|
96
102
|
task Activity::TaskBuilder::Binary( method(:normalize_for_macro) ), id: "normalize_for_macro"
|
|
97
103
|
task Activity::TaskBuilder::Binary( method(:split_options) ), id: "split_options"
|
|
98
|
-
task Activity::TaskBuilder::Binary( method(:normalize_extension_option) ), id: "normalize_extension_option"
|
|
99
104
|
task Activity::TaskBuilder::Binary( method(:initialize_plus_poles) ), id: "initialize_plus_poles"
|
|
100
105
|
# task ->((ctx, _), **) { pp ctx; [Activity::Right, [ctx, _]] }
|
|
101
106
|
end
|
|
@@ -24,11 +24,15 @@ module Trailblazer
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
# @return [Adds] list of Adds instances that can be chained or added to an existing sequence.
|
|
27
|
-
def self.InitialAdds(
|
|
27
|
+
def self.InitialAdds(**options)
|
|
28
|
+
StartAdds(**options) + EndAdds(**options)
|
|
29
|
+
end
|
|
28
30
|
|
|
31
|
+
# TODO: make this nicer.
|
|
32
|
+
def self.StartAdds(track_color:, end_semantic:, start_outputs: {success: self.default_outputs[:success]}, **)
|
|
29
33
|
builder_options={ track_color: track_color, end_semantic: end_semantic }
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
adds(
|
|
32
36
|
Activity::Start.new(semantic: :default),
|
|
33
37
|
|
|
34
38
|
TaskPolarizations(builder_options),
|
|
@@ -39,20 +43,31 @@ module Trailblazer
|
|
|
39
43
|
magnetic_to: [],
|
|
40
44
|
plus_poles: PlusPoles.initial(start_outputs), # FIXME: this is actually redundant with Normalizer
|
|
41
45
|
)
|
|
46
|
+
end
|
|
42
47
|
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
# TODO: make this nicer.
|
|
49
|
+
def self.EndAdds(track_color:, end_semantic:, track_end: Activity.End(end_semantic), end_id: nil, **)
|
|
50
|
+
# an end can either be a reference to another task,
|
|
51
|
+
# or a "real" end event.
|
|
52
|
+
if end_id
|
|
53
|
+
[[:magnetic_to,
|
|
54
|
+
[ end_id, [track_color] ] ]
|
|
55
|
+
]
|
|
56
|
+
else
|
|
57
|
+
builder_options={ track_color: track_color, end_semantic: end_semantic }
|
|
45
58
|
|
|
46
|
-
|
|
59
|
+
adds(
|
|
60
|
+
track_end,
|
|
47
61
|
|
|
48
|
-
|
|
62
|
+
EndEventPolarizations(builder_options), # only sets :magnetic_to.
|
|
49
63
|
|
|
50
|
-
|
|
51
|
-
plus_poles: {},
|
|
52
|
-
magnetic_to: nil,
|
|
53
|
-
)
|
|
64
|
+
{}, { group: :end },
|
|
54
65
|
|
|
55
|
-
|
|
66
|
+
id: "End.#{track_color}",
|
|
67
|
+
plus_poles: {},
|
|
68
|
+
magnetic_to: nil,
|
|
69
|
+
)
|
|
70
|
+
end
|
|
56
71
|
end
|
|
57
72
|
|
|
58
73
|
def self.TaskPolarizations(track_color:, **)
|
|
@@ -42,23 +42,23 @@ module Trailblazer
|
|
|
42
42
|
def insert(strategy, polarizer, task, options, &block)
|
|
43
43
|
normalizer = options[:normalizer] || @normalizer # DISCUSS: do this at a deeper point?
|
|
44
44
|
|
|
45
|
-
task, local_options, connection_options, sequence_options = normalizer.(task, options)
|
|
45
|
+
task, local_options, connection_options, sequence_options, extension_options = normalizer.(task, options)
|
|
46
46
|
|
|
47
47
|
polarizations = strategy.send(polarizer, @builder_options) # Railway.StepPolarizations( @builder_options )
|
|
48
48
|
|
|
49
|
-
insert_element( polarizations, task, local_options, connection_options, sequence_options, &block )
|
|
49
|
+
insert_element( polarizations, task, local_options, connection_options, sequence_options, extension_options, &block )
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
private
|
|
53
53
|
|
|
54
54
|
# Internal top-level entry point to add task(s) and connections.
|
|
55
55
|
# High level interface for DSL calls like ::task or ::step.
|
|
56
|
-
def insert_element(polarizations, task, local_options, connection_options, sequence_options, &block)
|
|
57
|
-
adds, *returned_options = Builder.adds_for(polarizations, task, local_options, connection_options, sequence_options, &block)
|
|
56
|
+
def insert_element(polarizations, task, local_options, connection_options, sequence_options, extension_options, &block)
|
|
57
|
+
adds, *returned_options = Builder.adds_for(polarizations, task, local_options, connection_options, sequence_options, extension_options, &block)
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
# @return Adds
|
|
61
|
-
def self.adds_for(polarizations, task, local_options, connection_options, sequence_options, &block)
|
|
61
|
+
def self.adds_for(polarizations, task, local_options, connection_options, sequence_options, extension_options, &block)
|
|
62
62
|
# go through all wiring options such as Output()=>:color.
|
|
63
63
|
polarizations_from_user_options, additional_adds = process_dsl_options(connection_options, local_options, &block)
|
|
64
64
|
|
|
@@ -66,7 +66,7 @@ module Trailblazer
|
|
|
66
66
|
|
|
67
67
|
result = adds(task, polarizations, local_options, sequence_options, local_options)
|
|
68
68
|
|
|
69
|
-
return result + (local_options[:adds] || []) + additional_adds, task, local_options, connection_options, sequence_options
|
|
69
|
+
return result + (local_options[:adds] || []) + additional_adds, task, local_options, connection_options, sequence_options, extension_options
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
def self.process_dsl_options(options, id:nil, plus_poles:, **, &block)
|
|
@@ -34,7 +34,7 @@ module Trailblazer
|
|
|
34
34
|
def self.circuit_hash_to_process(circuit_hash)
|
|
35
35
|
end_events = end_events_for(circuit_hash)
|
|
36
36
|
|
|
37
|
-
return Circuit.new(circuit_hash, end_events), end_events
|
|
37
|
+
return Circuit.new(circuit_hash, end_events, start_task: circuit_hash.keys.first), end_events
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
# Find all end events that don't have outgoing connections.
|
|
@@ -55,11 +55,11 @@ module Trailblazer
|
|
|
55
55
|
Polarization.new( output: output, color: task.color )
|
|
56
56
|
]
|
|
57
57
|
else # ID: existing step
|
|
58
|
-
new_edge = "#{id}-#{output.signal}-#{task}"
|
|
58
|
+
new_edge = "#{id}-#{output.signal}-#{task}" # edge from <id> to <target>
|
|
59
59
|
|
|
60
60
|
[
|
|
61
61
|
Polarization.new( output: output, color: new_edge ),
|
|
62
|
-
[[ :magnetic_to, [ task, [new_edge] ] ]],
|
|
62
|
+
[[ :magnetic_to, [ task, [new_edge] ] ]], # mark target (`task`) as magnetic to the new edge.
|
|
63
63
|
]
|
|
64
64
|
end
|
|
65
65
|
end
|
|
@@ -12,7 +12,6 @@ module Trailblazer
|
|
|
12
12
|
builder_class: Magnetic::Builder::Path, # we use the Activity-based Normalizer
|
|
13
13
|
normalizer_class: Magnetic::Normalizer,
|
|
14
14
|
default_outputs: Magnetic::Builder::Path.default_outputs, # binary outputs
|
|
15
|
-
extension: [ Introspect.method(:add_introspection) ],
|
|
16
15
|
|
|
17
16
|
extend: [
|
|
18
17
|
# DSL.def_dsl(:task, Magnetic::Builder::Path, :PassPolarizations),
|
|
@@ -1,27 +1,52 @@
|
|
|
1
1
|
module Trailblazer
|
|
2
|
-
class Activity < Module
|
|
2
|
+
class Activity < Module
|
|
3
|
+
# Compile-time.
|
|
4
|
+
# Introspection is not used at run-time except for rendering diagrams, tracing, and the like.
|
|
3
5
|
module Introspect
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
circuit_options = circuit_options.merge( introspect: activity.debug )
|
|
7
|
-
|
|
8
|
-
return activity, [ options, flow_options ], circuit_options
|
|
6
|
+
def self.Graph(*args)
|
|
7
|
+
Graph.new(*args)
|
|
9
8
|
end
|
|
10
9
|
|
|
11
|
-
#
|
|
12
|
-
|
|
13
|
-
activity
|
|
14
|
-
|
|
10
|
+
# @private This API is still under construction.
|
|
11
|
+
class Graph
|
|
12
|
+
def initialize(activity)
|
|
13
|
+
@activity = activity
|
|
14
|
+
@circuit = activity.to_h[:circuit]
|
|
15
|
+
@adds = activity.to_h[:adds].compact # FIXME: why are there nils in Adds?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def find(id=nil, &block)
|
|
19
|
+
return find_by_id(id) unless block_given?
|
|
20
|
+
find_with_block(&block)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def find_by_id(id)
|
|
26
|
+
(_, (id, triplett)) = @adds.find { |(op, (_id, triplett))| _id == id }
|
|
27
|
+
|
|
28
|
+
Node(triplett[1], id, triplett[0])
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def find_with_block(&block)
|
|
32
|
+
adds = @adds.find { |(op, (id, triplett))| yield( Node(triplett[1], id, triplett[0]) ) }
|
|
33
|
+
return nil unless adds
|
|
34
|
+
|
|
35
|
+
(op, (id, triplett)) = adds
|
|
15
36
|
|
|
37
|
+
Node(triplett[1], id, triplett[0])
|
|
38
|
+
end
|
|
16
39
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
40
|
+
def Node(*args)
|
|
41
|
+
Node.new(*args).freeze
|
|
42
|
+
end
|
|
20
43
|
|
|
21
|
-
|
|
44
|
+
Node = Struct.new(:task, :id, :magnetic_to)
|
|
22
45
|
end
|
|
23
46
|
|
|
24
47
|
|
|
48
|
+
# FIXME: remove this
|
|
49
|
+
# @private This will be removed shortly.
|
|
25
50
|
def self.collect(activity, options={}, &block)
|
|
26
51
|
circuit = activity.to_h[:circuit]
|
|
27
52
|
circuit_hash = circuit.to_h[:map]
|
|
@@ -17,7 +17,10 @@ module Trailblazer
|
|
|
17
17
|
stack.each do |captured, *returned|
|
|
18
18
|
task = captured.task
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
graph = Introspect::Graph(captured.activity)
|
|
21
|
+
|
|
22
|
+
name = (node = graph.find { |node| node[:task] == task }) ? node[:id] : task
|
|
23
|
+
name ||= task # FIXME: bullshit
|
|
21
24
|
|
|
22
25
|
if returned.size == 1 # flat
|
|
23
26
|
tree << [ level, name ]
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
@options = options.merge(semantic: semantic)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
def call(
|
|
22
|
-
return self,
|
|
21
|
+
def call(args, circuit_options)
|
|
22
|
+
return self, args, circuit_options
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def to_h
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
class Start < End
|
|
37
|
-
def call(
|
|
38
|
-
return Activity::Right,
|
|
37
|
+
def call(args, circuit_options)
|
|
38
|
+
return Activity::Right, args, circuit_options
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -8,7 +8,7 @@ class Trailblazer::Activity < Module
|
|
|
8
8
|
|
|
9
9
|
# Call the actual task we're wrapping here.
|
|
10
10
|
# puts "~~~~wrap.call: #{task}"
|
|
11
|
-
return_signal, return_args = task.(
|
|
11
|
+
return_signal, return_args = task.(*original_args)
|
|
12
12
|
|
|
13
13
|
# DISCUSS: do we want original_args here to be passed on, or the "effective" return_args which are different to original_args now?
|
|
14
14
|
wrap_ctx = wrap_ctx.merge( return_signal: return_signal, return_args: return_args )
|
|
@@ -11,7 +11,7 @@ module Trailblazer
|
|
|
11
11
|
# {:extension API}
|
|
12
12
|
def call(activity, task, local_options, *returned_options)
|
|
13
13
|
# we could make the default initial_activity injectable via the DSL, the value would sit in returned_options or local_options.
|
|
14
|
-
static_wrap = Activity::TaskWrap.wrap_static_for(task,
|
|
14
|
+
static_wrap = Activity::TaskWrap.wrap_static_for(task, activity: activity)
|
|
15
15
|
|
|
16
16
|
# # macro might want to apply changes to the static task_wrap (e.g. Inject)
|
|
17
17
|
new_wrap = Activity::Path::Plan.merge( static_wrap, @extension_plan )
|
|
@@ -9,7 +9,7 @@ class Trailblazer::Activity < Module
|
|
|
9
9
|
#
|
|
10
10
|
# @api private
|
|
11
11
|
# @interface Runner
|
|
12
|
-
def self.call(task, args,
|
|
12
|
+
def self.call(task, args, circuit_options)
|
|
13
13
|
wrap_ctx = { task: task }
|
|
14
14
|
|
|
15
15
|
# this activity is "wrapped around" the actual `task`.
|
|
@@ -18,9 +18,11 @@ class Trailblazer::Activity < Module
|
|
|
18
18
|
# We save all original args passed into this Runner.call, because we want to return them later after this wrap
|
|
19
19
|
# is finished.
|
|
20
20
|
original_args = [ args, circuit_options ]
|
|
21
|
+
|
|
21
22
|
# call the wrap {Activity} around the task.
|
|
22
23
|
wrap_end_signal, ( wrap_ctx, _ ) = task_wrap_activity.(
|
|
23
|
-
[ wrap_ctx, original_args ] # we omit circuit_options here on purpose, so the wrapping activity uses the default, plain Runner.
|
|
24
|
+
[ wrap_ctx, original_args ], # we omit circuit_options here on purpose, so the wrapping activity uses the default, plain Runner.
|
|
25
|
+
{}
|
|
24
26
|
)
|
|
25
27
|
|
|
26
28
|
# don't return the wrap's end signal, but the one from call_task.
|
|
@@ -37,7 +39,10 @@ class Trailblazer::Activity < Module
|
|
|
37
39
|
# unnecessary computations at `call`-time since steps might not even be executed.
|
|
38
40
|
# TODO: make this faster.
|
|
39
41
|
def self.merge_static_with_runtime(task, wrap_runtime:, **circuit_options)
|
|
40
|
-
wrap_activity = TaskWrap.wrap_static_for(
|
|
42
|
+
wrap_activity = TaskWrap.wrap_static_for(
|
|
43
|
+
task,
|
|
44
|
+
circuit_options
|
|
45
|
+
) # find static wrap for this specific task, or default wrap activity.
|
|
41
46
|
|
|
42
47
|
# Apply runtime alterations.
|
|
43
48
|
# Grab the additional wirings for the particular `task` from `wrap_runtime` (returns default otherwise).
|
|
@@ -45,7 +50,12 @@ class Trailblazer::Activity < Module
|
|
|
45
50
|
end
|
|
46
51
|
end # Runner
|
|
47
52
|
|
|
48
|
-
|
|
53
|
+
# Retrieve the static wrap config from {activity}.
|
|
54
|
+
# I do not like this part too much, I'd prefer computing the {:wrap_static} at compile-time for the entire
|
|
55
|
+
# object graph (including nesteds) and simply pass it through to all Runner calls.
|
|
56
|
+
# TODO: simplify that. See above
|
|
57
|
+
def self.wrap_static_for(task, activity:, default_activity: TaskWrap.initial_activity, **)
|
|
58
|
+
wrap_static = activity[:wrap_static] || {}
|
|
49
59
|
wrap_static[task] || default_activity
|
|
50
60
|
end
|
|
51
61
|
end
|
|
@@ -2,26 +2,42 @@ class Trailblazer::Activity < Module
|
|
|
2
2
|
module TaskWrap
|
|
3
3
|
# TaskWrap tasks for tracing.
|
|
4
4
|
module Trace
|
|
5
|
-
|
|
6
|
-
def self.capture_args((wrap_config, original_args), **circuit_options)
|
|
7
|
-
(original_options, original_flow_options), original_circuit_options = original_args
|
|
5
|
+
module_function
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
# taskWrap step to capture incoming arguments of a step.
|
|
8
|
+
# def self.capture_args(direction, options, flow_options, wrap_config, original_flow_options)
|
|
9
|
+
def capture_args((wrap_config, original_args), **circuit_options)
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
original_args = capture_for(wrap_config[:task], *original_args)
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
return Trailblazer::Activity::Right, [wrap_config, original_args], circuit_options
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
# taskWrap step to capture outgoing arguments from a step.
|
|
17
|
+
def capture_return((wrap_config, original_args), **circuit_options)
|
|
17
18
|
(original_options, original_flow_options, _) = original_args[0]
|
|
18
19
|
|
|
19
|
-
original_flow_options[:stack] << Trailblazer::Activity::Trace::Entity.new(
|
|
20
|
+
original_flow_options[:stack] << Trailblazer::Activity::Trace::Entity.new(
|
|
21
|
+
wrap_config[:task], {}, :return, wrap_config[:return_signal]
|
|
22
|
+
)
|
|
20
23
|
|
|
21
24
|
original_flow_options[:stack].unindent!
|
|
22
25
|
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
return Trailblazer::Activity::Right, [wrap_config, original_args], circuit_options
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# It's important to understand that {flow[:stack]} is mutated by design. This is needed so
|
|
31
|
+
# in case of exceptions we still have a "global" trace - unfortunately Ruby doesn't allow
|
|
32
|
+
# us a better way.
|
|
33
|
+
def capture_for(task, (ctx, flow), activity:, **circuit_options)
|
|
34
|
+
flow[:stack].indent!
|
|
35
|
+
|
|
36
|
+
flow[:stack] << Trailblazer::Activity::Trace::Entity.new(
|
|
37
|
+
task, activity, :args, nil, {}
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
return [ctx, flow], circuit_options.merge(activity: activity)
|
|
25
41
|
end
|
|
26
42
|
end
|
|
27
43
|
end
|
|
@@ -24,29 +24,17 @@ module Trailblazer
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
# Compute runtime arguments necessary to execute a taskWrap per task of the activity.
|
|
27
|
-
def self.
|
|
27
|
+
def self.invoke(activity, args, wrap_runtime: {}, **circuit_options)
|
|
28
28
|
circuit_options = circuit_options.merge(
|
|
29
29
|
runner: TaskWrap::Runner,
|
|
30
|
-
wrap_runtime:
|
|
31
|
-
wrap_static: activity[:wrap_static] || {},
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
return activity, [ options, flow_options ], circuit_options
|
|
35
|
-
end
|
|
30
|
+
wrap_runtime: wrap_runtime,
|
|
36
31
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
circuit_options = circuit_options.merge(
|
|
40
|
-
runner: TaskWrap::Runner,
|
|
41
|
-
wrap_runtime: circuit_options[:wrap_runtime] || {}, # FIXME:this sucks. (was:) this overwrites wrap_runtime from outside.
|
|
42
|
-
wrap_static: ::Hash.new(TaskWrap.initial_activity), # add a default static wrap.
|
|
43
|
-
)
|
|
32
|
+
activity: { adds: [], circuit: {} }, # for Runner
|
|
33
|
+
)
|
|
44
34
|
|
|
45
|
-
|
|
46
|
-
|
|
35
|
+
# signal, (ctx, flow), circuit_options =
|
|
36
|
+
Runner.(activity, args, circuit_options)
|
|
47
37
|
end
|
|
48
|
-
|
|
49
|
-
# better: MyClass < Activity(TaskWrap, ...)
|
|
50
|
-
end
|
|
38
|
+
end # TaskWrap
|
|
51
39
|
end
|
|
52
40
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# DISCUSS: move to trailblazer-activity-test ?
|
|
2
|
+
|
|
3
|
+
# Helpers to quickly create steps and tasks.
|
|
4
|
+
module Trailblazer::Activity::Testing
|
|
5
|
+
# Creates a module with one step method for each name.
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# extend T.def_steps(:create, :save)
|
|
9
|
+
def self.def_steps(*names)
|
|
10
|
+
Module.new do
|
|
11
|
+
names.each do |name|
|
|
12
|
+
define_method(name) do | ctx, ** |
|
|
13
|
+
ctx[:seq] << name
|
|
14
|
+
ctx.key?(name) ? ctx[name] : true
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Creates a method instance with a task interface.
|
|
21
|
+
#
|
|
22
|
+
# @example
|
|
23
|
+
# task task: T.def_task(:create)
|
|
24
|
+
def self.def_task(name)
|
|
25
|
+
Module.new do
|
|
26
|
+
define_singleton_method(name) do | (ctx, flow_options), ** |
|
|
27
|
+
ctx[:seq] << name
|
|
28
|
+
return Trailblazer::Activity::Right, [ctx, flow_options]
|
|
29
|
+
end
|
|
30
|
+
end.method(name)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -22,13 +22,10 @@ module Trailblazer
|
|
|
22
22
|
return activity, [ options, flow_options.merge(tracing_flow_options) ], circuit_options.merge(tracing_circuit_options)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
def self.call(activity, (options, flow_options),
|
|
26
|
-
activity, (options, flow_options), circuit_options = Trace.arguments_for_call( activity, [options, flow_options],
|
|
25
|
+
def self.call(activity, (options, flow_options), circuit_options={})
|
|
26
|
+
activity, (options, flow_options), circuit_options = Trace.arguments_for_call( activity, [options, flow_options], circuit_options ) # only run once for the entire circuit!
|
|
27
27
|
last_signal, (options, flow_options) =
|
|
28
|
-
|
|
29
|
-
[options, flow_options],
|
|
30
|
-
circuit_options.merge({ argumenter: [ Introspect.method(:arguments_for_call), TaskWrap.method(:arguments_for_call) ] })
|
|
31
|
-
)
|
|
28
|
+
Activity::TaskWrap.invoke(activity, [options, flow_options], circuit_options)
|
|
32
29
|
|
|
33
30
|
return flow_options[:stack].to_a, last_signal, [options, flow_options]
|
|
34
31
|
end
|
|
@@ -48,7 +45,7 @@ module Trailblazer
|
|
|
48
45
|
end
|
|
49
46
|
end
|
|
50
47
|
|
|
51
|
-
Entity = Struct.new(:task, :type, :value, :value2
|
|
48
|
+
Entity = Struct.new(:task, :activity, :type, :value, :value2)
|
|
52
49
|
|
|
53
50
|
# Mutable/stateful per design. We want a (global) stack!
|
|
54
51
|
class Stack
|
data/lib/trailblazer/activity.rb
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
require "trailblazer/activity/version"
|
|
2
|
-
|
|
3
1
|
module Trailblazer
|
|
4
|
-
def self.Activity(implementation=Activity::Path, options={})
|
|
5
|
-
Activity.new(implementation, state)
|
|
6
|
-
end
|
|
7
|
-
|
|
8
2
|
class Activity < Module
|
|
9
3
|
attr_reader :initial_state
|
|
10
4
|
|
|
@@ -51,6 +45,12 @@ module Trailblazer
|
|
|
51
45
|
end
|
|
52
46
|
end
|
|
53
47
|
|
|
48
|
+
# Reader and writer method for DSL objects.
|
|
49
|
+
# The writer {dsl[:key] = "value"} exposes immutable behavior and will replace the old
|
|
50
|
+
# @state with a new, modified copy.
|
|
51
|
+
#
|
|
52
|
+
# Always use the DSL::Accessor accessors to avoid leaking state to other components
|
|
53
|
+
# due to mutable write operations.
|
|
54
54
|
module Accessor
|
|
55
55
|
def []=(*args)
|
|
56
56
|
@state = State::Config.send(:[]=, @state, *args)
|
|
@@ -81,12 +81,12 @@ module Trailblazer
|
|
|
81
81
|
require "trailblazer/activity/dsl/magnetic/merge"
|
|
82
82
|
include Magnetic::Merge # Activity#merge!
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
self[:circuit].( args, circuit_options.merge(argumenter: argumenter) )
|
|
84
|
+
# @private Note that {Activity.call} is considered private until the public API is stable.
|
|
85
|
+
def call(args, circuit_options={})
|
|
86
|
+
self[:circuit].( args, circuit_options.merge(activity: self) )
|
|
88
87
|
end
|
|
89
88
|
end
|
|
89
|
+
|
|
90
90
|
end # Activity
|
|
91
91
|
end
|
|
92
92
|
|
data/lib/trailblazer/circuit.rb
CHANGED
|
@@ -14,7 +14,7 @@ module Trailblazer
|
|
|
14
14
|
#
|
|
15
15
|
# This is the "pipeline operator"'s implementation.
|
|
16
16
|
class Circuit
|
|
17
|
-
def initialize(map, stop_events, name: nil
|
|
17
|
+
def initialize(map, stop_events, start_task:, name: nil)
|
|
18
18
|
@map = map
|
|
19
19
|
@stop_events = stop_events
|
|
20
20
|
@name = name
|
|
@@ -36,8 +36,9 @@ module Trailblazer
|
|
|
36
36
|
# @return [last_signal, options, flow_options, *args]
|
|
37
37
|
#
|
|
38
38
|
# NOTE: returned circuit_options are discarded when calling the runner.
|
|
39
|
-
def call(args,
|
|
39
|
+
def call(args, start_task: @start_task, runner: Run, **circuit_options)
|
|
40
40
|
circuit_options = circuit_options.merge( runner: runner ).freeze # TODO: set the :runner option via arguments_for_call to save the merge?
|
|
41
|
+
task = start_task
|
|
41
42
|
|
|
42
43
|
loop do
|
|
43
44
|
last_signal, args, _discarded_circuit_options = runner.(
|
|
@@ -49,35 +50,22 @@ module Trailblazer
|
|
|
49
50
|
# Stop execution of the circuit when we hit a stop event (< End). This could be an task's End or Suspend.
|
|
50
51
|
return [ last_signal, args ] if @stop_events.include?(task) # DISCUSS: return circuit_options here?
|
|
51
52
|
|
|
52
|
-
task = next_for(task, last_signal)
|
|
53
|
-
raise IllegalInputError.new("#{task}") unless in_map
|
|
54
|
-
raise IllegalOutputSignalError.new("<#{@name}>[#{task}][ #{last_signal.inspect} ]") unless next_task
|
|
55
|
-
end
|
|
53
|
+
task = next_for(task, last_signal) or raise IllegalSignalError.new("<#{@name}>[#{task}][ #{last_signal.inspect} ]")
|
|
56
54
|
end
|
|
57
55
|
end
|
|
58
56
|
|
|
59
57
|
# Returns the circuit's components.
|
|
60
58
|
def to_h
|
|
61
|
-
{ map: @map, end_events: @stop_events }
|
|
59
|
+
{ map: @map, end_events: @stop_events, start_task: @start_task }
|
|
62
60
|
end
|
|
63
61
|
|
|
64
62
|
private
|
|
65
|
-
def next_for(last_task,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
cfg = @map.keys.find { |t| t == last_task } and in_map = true
|
|
69
|
-
cfg = @map[cfg] if cfg
|
|
70
|
-
cfg ||= {}
|
|
71
|
-
next_task = cfg[emitted_signal]
|
|
72
|
-
yield next_task, in_map
|
|
73
|
-
|
|
74
|
-
next_task
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
class IllegalInputError < RuntimeError
|
|
63
|
+
def next_for(last_task, signal)
|
|
64
|
+
outputs = @map[last_task]
|
|
65
|
+
outputs[signal]
|
|
78
66
|
end
|
|
79
67
|
|
|
80
|
-
class
|
|
68
|
+
class IllegalSignalError < RuntimeError
|
|
81
69
|
end
|
|
82
70
|
end
|
|
83
71
|
end
|
data/lib/trailblazer-activity.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: trailblazer-activity
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nick Sutterer
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2018-03
|
|
11
|
+
date: 2018-05-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: hirb
|
|
@@ -111,7 +111,6 @@ files:
|
|
|
111
111
|
- NOTES
|
|
112
112
|
- README.md
|
|
113
113
|
- Rakefile
|
|
114
|
-
- inspeccccccccct.rb
|
|
115
114
|
- lib/trailblazer-activity.rb
|
|
116
115
|
- lib/trailblazer/activity.rb
|
|
117
116
|
- lib/trailblazer/activity/config.rb
|
|
@@ -150,6 +149,7 @@ files:
|
|
|
150
149
|
- lib/trailblazer/activity/task_wrap/merge.rb
|
|
151
150
|
- lib/trailblazer/activity/task_wrap/runner.rb
|
|
152
151
|
- lib/trailblazer/activity/task_wrap/trace.rb
|
|
152
|
+
- lib/trailblazer/activity/testing.rb
|
|
153
153
|
- lib/trailblazer/activity/trace.rb
|
|
154
154
|
- lib/trailblazer/activity/version.rb
|
|
155
155
|
- lib/trailblazer/circuit.rb
|
data/inspeccccccccct.rb
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
module Inspect
|
|
3
|
-
def inspect
|
|
4
|
-
"nice view!"
|
|
5
|
-
end
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
module A
|
|
9
|
-
extend Inspect
|
|
10
|
-
|
|
11
|
-
def self.a
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# jruby 9.1.7
|
|
16
|
-
puts A.method(:a).inspect #=> #<Method: A.a>
|
|
17
|
-
# 2.5
|
|
18
|
-
puts A.method(:a).inspect #=> #<Method: nice view!.a>
|
|
19
|
-
|