trailblazer-activity 0.2.1 → 0.3.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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +4 -0
  3. data/NOTES_ +36 -0
  4. data/README.md +7 -7
  5. data/Rakefile +1 -1
  6. data/lib/trailblazer/activity.rb +137 -96
  7. data/lib/trailblazer/activity/heritage.rb +30 -0
  8. data/lib/trailblazer/activity/introspection.rb +105 -0
  9. data/lib/trailblazer/activity/magnetic.rb +47 -0
  10. data/lib/trailblazer/activity/magnetic/builder.rb +161 -0
  11. data/lib/trailblazer/activity/magnetic/builder/block.rb +37 -0
  12. data/lib/trailblazer/activity/magnetic/builder/fast_track.rb +141 -0
  13. data/lib/trailblazer/activity/magnetic/builder/path.rb +98 -0
  14. data/lib/trailblazer/activity/magnetic/builder/railway.rb +123 -0
  15. data/lib/trailblazer/activity/magnetic/dsl.rb +90 -0
  16. data/lib/trailblazer/activity/magnetic/dsl/alterations.rb +44 -0
  17. data/lib/trailblazer/activity/magnetic/dsl/plus_poles.rb +59 -0
  18. data/lib/trailblazer/activity/magnetic/finalizer.rb +55 -0
  19. data/lib/trailblazer/activity/magnetic/generate.rb +62 -0
  20. data/lib/trailblazer/activity/present.rb +12 -19
  21. data/lib/trailblazer/activity/process.rb +16 -0
  22. data/lib/trailblazer/activity/schema/dependencies.rb +41 -0
  23. data/lib/trailblazer/activity/schema/sequence.rb +46 -0
  24. data/lib/trailblazer/activity/structures.rb +41 -0
  25. data/lib/trailblazer/activity/subprocess.rb +9 -1
  26. data/lib/trailblazer/activity/trace.rb +25 -16
  27. data/lib/trailblazer/activity/version.rb +1 -1
  28. data/lib/trailblazer/activity/wrap.rb +4 -13
  29. data/lib/trailblazer/circuit.rb +4 -35
  30. data/lib/trailblazer/wrap/call_task.rb +2 -2
  31. data/lib/trailblazer/wrap/runner.rb +7 -1
  32. data/lib/trailblazer/wrap/trace.rb +6 -5
  33. metadata +21 -10
  34. data/lib/trailblazer/activity/graph.rb +0 -157
  35. data/lib/trailblazer/circuit/testing.rb +0 -58
  36. data/lib/trailblazer/container_chain.rb +0 -45
  37. data/lib/trailblazer/context.rb +0 -68
  38. data/lib/trailblazer/option.rb +0 -78
  39. data/lib/trailblazer/wrap/inject.rb +0 -32
  40. data/lib/trailblazer/wrap/variable_mapping.rb +0 -92
@@ -0,0 +1,123 @@
1
+ module Trailblazer
2
+ module Activity::Magnetic
3
+ class Builder
4
+ class Railway < Builder
5
+ def self.for(normalizer, builder_options={}) # Build the Builder.
6
+ Activity::Magnetic::Builder(
7
+ Railway,
8
+ normalizer,
9
+ { track_color: :success, end_semantic: :success, failure_color: :failure }.merge( builder_options )
10
+ )
11
+ end
12
+
13
+ def self.plan(options={}, normalizer=DefaultNormalizer.new(plus_poles: default_plus_poles), &block)
14
+ plan_for( *Railway.for(normalizer, options), &block )
15
+ end
16
+
17
+ def self.keywords
18
+ [:type]
19
+ end
20
+
21
+ def step(task, options={}, &block)
22
+ insert_element( Railway, Railway.StepPolarizations(@builder_options), task, options, &block )
23
+ end
24
+
25
+ def fail(task, options={}, &block)
26
+ insert_element( Railway, Railway.FailPolarizations(@builder_options), task, options, &block )
27
+ end
28
+
29
+ def pass(task, options={}, &block)
30
+ insert_element( Railway, Railway.PassPolarizations(@builder_options), task, options, &block )
31
+ end
32
+
33
+ def self.default_plus_poles
34
+ DSL::PlusPoles.new.merge(
35
+ Activity.Output(Activity::Right, :success) => nil,
36
+ Activity.Output(Activity::Left, :failure) => nil,
37
+ ).freeze
38
+ end
39
+
40
+ # Adds the End.failure end to the Path sequence.
41
+ # @return [Adds] list of Adds instances that can be chained or added to an existing sequence.
42
+ def self.InitialAdds(failure_color:raise, failure_end: Activity.End(failure_color, :failure), **builder_options)
43
+ path_adds = Path.InitialAdds(**builder_options)
44
+
45
+ end_adds = adds(
46
+ "End.#{failure_color}", failure_end,
47
+
48
+ {}, # plus_poles
49
+ Path::TaskPolarizations(builder_options.merge( type: :End )),
50
+ [],
51
+
52
+ {},
53
+ { group: :end },
54
+ [failure_color]
55
+ )
56
+
57
+ path_adds + end_adds
58
+ end
59
+
60
+ # ONLY JOB: magnetic_to and Outputs ("Polarization") via PlusPoles.merge
61
+ def self.StepPolarizations(**options)
62
+ [
63
+ *Path.TaskPolarizations(options),
64
+ StepPolarization.new(options)
65
+ ]
66
+ end
67
+
68
+ def self.PassPolarizations(options)
69
+ [
70
+ Railway::PassPolarization.new( options )
71
+ ]
72
+ end
73
+
74
+ def self.FailPolarizations(options)
75
+ [
76
+ Railway::FailPolarization.new( options )
77
+ ]
78
+ end
79
+
80
+ class StepPolarization
81
+ def initialize(track_color: :success, failure_color: :failure, **o)
82
+ @track_color, @failure_color = track_color, failure_color
83
+ end
84
+
85
+ # Returns the polarization for a DSL call. Takes care of user options such as :magnetic_to.
86
+ def call(magnetic_to, plus_poles, options)
87
+ [
88
+ magnetic_to || default_magnetic_to,
89
+ plus_poles_for(plus_poles, options),
90
+ ]
91
+ end
92
+
93
+ private
94
+
95
+ def plus_poles_for(plus_poles, options)
96
+ plus_poles.reconnect( :failure => @failure_color )
97
+ end
98
+
99
+ def default_magnetic_to
100
+ [@track_color]
101
+ end
102
+ end
103
+
104
+ class PassPolarization < StepPolarization
105
+ def plus_poles_for(plus_poles, options)
106
+ plus_poles.reconnect( :failure => @track_color, :success => @track_color )
107
+ end
108
+ end
109
+
110
+ class FailPolarization < StepPolarization
111
+ def default_magnetic_to
112
+ [@failure_color]
113
+ end
114
+
115
+ def plus_poles_for(plus_poles, options)
116
+ plus_poles.reconnect( :failure => @failure_color, :success => @failure_color )
117
+ end
118
+ end
119
+
120
+ end # Railway
121
+ end # Builder
122
+ end
123
+ end
@@ -0,0 +1,90 @@
1
+ module Trailblazer
2
+ module Activity::Magnetic
3
+ module DSL
4
+ class Polarization
5
+ def initialize( output:raise, color:raise )
6
+ @output, @color = output, color
7
+ end
8
+
9
+ def call(magnetic_to, plus_poles, options)
10
+ [
11
+ magnetic_to,
12
+ plus_poles.merge( @output => @color ) # this usually adds a new Output to the task.
13
+ ]
14
+ end
15
+ end # Polarization
16
+
17
+ # This module only processes additional "wiring" options from the DSL calls
18
+ # Output(:success) => End("my.new")
19
+ #
20
+ # Returns PlusPoles and additional sequence alterations.
21
+ module ProcessOptions
22
+ module_function
23
+
24
+ # Output => target (End/"id"/:color)
25
+ # @return [PlusPole]
26
+ # @return additional alterations
27
+ #
28
+ # options:
29
+ # { DSL::Output[::Semantic] => target }
30
+ #
31
+ def call(id, options, initial_plus_poles, &block)
32
+ polarization, adds =
33
+ options.
34
+ collect { |key, task|
35
+ # this method call is the only thing that really matters here. # TODO: make this transformation a bit more obvious.
36
+ process_tuple(id, key, task, initial_plus_poles, &block)
37
+ }.
38
+ inject([[],[]]) { |memo, (polarization, adds)| memo[0]<<polarization; memo[1]<<adds; memo }
39
+
40
+ return polarization, adds.flatten(1)
41
+ end
42
+
43
+ def process_tuple(id, output, task, initial_plus_poles, &block)
44
+ output = output_for(output, initial_plus_poles) if output.kind_of?(DSL::Output::Semantic)
45
+
46
+ if task.kind_of?(Activity::End)
47
+ new_edge = "#{id}-#{output.signal}"
48
+
49
+ [
50
+ Polarization.new( output: output, color: new_edge ),
51
+ [ [:add, [task.instance_variable_get(:@name), [ [new_edge], task, [] ], group: :end]] ]
52
+ ]
53
+ elsif task.is_a?(String) # let's say this means an existing step
54
+ new_edge = "#{output.signal}-#{task}"
55
+
56
+ [
57
+ Polarization.new( output: output, color: new_edge ),
58
+ [[ :magnetic_to, [ task, [new_edge] ] ]],
59
+ ]
60
+ # procs come from DSL calls such as `Path() do ... end`.
61
+ elsif task.is_a?(Proc)
62
+ start_color, adds = task.(block)
63
+
64
+ [
65
+ Polarization.new( output: output, color: start_color ),
66
+ # TODO: this is a pseudo-"merge" and should be public API at some point.
67
+ adds[1..-1] # drop start
68
+ ]
69
+ else # An additional plus polarization. Example: Output => :success
70
+ [
71
+ Polarization.new( output: output, color: task )
72
+ ]
73
+ end
74
+ end
75
+
76
+ # @param semantic DSL::Output::Semantic
77
+ def output_for(semantic, plus_poles)
78
+ # DISCUSS: review PlusPoles#[]
79
+ output, _ = plus_poles.instance_variable_get(:@plus_poles)[semantic.value]
80
+ output or raise("Couldn't find existing output for `#{semantic.value.inspect}`.")
81
+ end
82
+ end # OptionsProcessing
83
+
84
+ # DSL datastructures
85
+ module Output
86
+ Semantic = Struct.new(:value)
87
+ end
88
+ end # DSL
89
+ end
90
+ end
@@ -0,0 +1,44 @@
1
+ module Trailblazer
2
+ module Activity::Magnetic
3
+ module DSL
4
+ # works on a generic Dependencies structure that has no knowledge of magnetism.
5
+ class Alterations
6
+ def initialize
7
+ @groups = Activity::Schema::Dependencies.new
8
+ @future_magnetic_to = {} # DISCUSS: future - should it be here?
9
+ end
10
+
11
+ def add(id, options, **sequence_options)
12
+ @groups.add(id, options, **sequence_options)
13
+
14
+ # DISCUSS: future - should it be here?
15
+ if magnetic_to = @future_magnetic_to.delete(id)
16
+ magnetic_to( id, magnetic_to )
17
+ end
18
+
19
+ self
20
+ end
21
+
22
+ # make `id` magnetic_to
23
+ def magnetic_to(id, magnetic_to)
24
+ group, index = @groups.find(id) # this can be a future task!
25
+
26
+ unless group # DISCUSS: future - should it be here?
27
+ @future_magnetic_to[id] = magnetic_to
28
+ return
29
+ end
30
+
31
+ arr = group[index].configuration.dup
32
+
33
+ arr[0] = arr[0] + magnetic_to
34
+ group.add(id, arr, replace: id)
35
+ end
36
+
37
+ # Returns array of tripletts.
38
+ def to_a
39
+ @groups.to_a
40
+ end
41
+ end
42
+ end # DSL
43
+ end
44
+ end
@@ -0,0 +1,59 @@
1
+ module Trailblazer
2
+ module Activity::Magnetic
3
+ module DSL
4
+ # Output(:signal, :semantic) => :color
5
+ # add / merge
6
+ # change existing, => color
7
+ #
8
+ # Mutable DSL datastructure for managing all PlusPoles for a particular task.
9
+ #
10
+ # Produces [ PlusPole, PlusPole, ] via `to_a`.
11
+ #
12
+ # @privat
13
+ # @note This is private until we know what we want.
14
+ class PlusPoles
15
+ def initialize(plus_poles={})
16
+ @plus_poles = plus_poles.freeze
17
+ end
18
+
19
+ # merge( Activity::Magnetic.Output(Right, :success) => :success
20
+ def merge(output_to_color)
21
+ overrides = ::Hash[ output_to_color.collect { |output, color| [ output.semantic, [output, color] ] } ]
22
+ PlusPoles.new(@plus_poles.merge(overrides))
23
+ end
24
+
25
+ def reverse_merge(output_to_color)
26
+ existing_colors = @plus_poles.values.collect { |pole_cfg| pole_cfg.last }
27
+
28
+ overrides = output_to_color.find_all { |output, color| !existing_colors.include?(color) } # filter all outputs with a color that already exists.
29
+ merge(overrides)
30
+ end
31
+
32
+ def reconnect(semantic_to_color)
33
+ ary = semantic_to_color.collect do |semantic, color|
34
+ existing_output, _ = @plus_poles[semantic]
35
+
36
+ next unless existing_output
37
+
38
+ [ Activity.Output(existing_output.signal, existing_output.semantic), color ]
39
+ end
40
+
41
+ merge( ::Hash[ary.compact] )
42
+ end
43
+
44
+ # The DSL is a series of transformations that yield in tasks with several PlusPole instances each.
45
+ def to_a
46
+ @plus_poles.values.collect { |output, color| PlusPole.new(output, color) }
47
+ end
48
+
49
+ # Builds PlusPoles from { semantic => Output }, which, surprisingly, is exactly what Activity::outputs looks like.
50
+ # The plus pole's color is set to the output's semantic.
51
+ def self.from_outputs(outputs)
52
+ ary = outputs.collect { |semantic, output| [ output, semantic ] }
53
+
54
+ new.merge(::Hash[ary])
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,55 @@
1
+ module Trailblazer
2
+ module Activity::Magnetic
3
+ class Builder
4
+ module Finalizer
5
+
6
+ def self.call(adds)
7
+ tripletts = adds_to_tripletts(adds)
8
+
9
+ circuit_hash = tripletts_to_circuit_hash( tripletts )
10
+
11
+ circuit_hash_to_process( circuit_hash )
12
+ end
13
+
14
+ def self.adds_to_tripletts(adds)
15
+ alterations = adds_to_alterations(adds)
16
+
17
+ alterations.to_a
18
+ end
19
+
20
+ def self.adds_to_alterations(adds)
21
+ alterations = DSL::Alterations.new
22
+
23
+ adds = adds.compact # TODO: test me explicitly, and where does this come from anyway?
24
+
25
+ adds.each { |method, cfg| alterations.send( method, *cfg ) }
26
+
27
+ alterations
28
+ end
29
+
30
+ def self.tripletts_to_circuit_hash(tripletts)
31
+ Activity::Magnetic::Generate.( tripletts )
32
+ end
33
+
34
+ def self.circuit_hash_to_process(circuit_hash)
35
+ end_events = end_events_for(circuit_hash)
36
+
37
+ return Activity::Process.new( circuit_hash, end_events ), end_events
38
+ end
39
+
40
+ # Filters out unconnected ends, e.g. the standard end in nested tracks that weren't used.
41
+ def self.end_events_for(circuit_hash)
42
+ tasks_with_incoming_edge = circuit_hash.values.collect { |connections| connections.values }.flatten(1)
43
+
44
+ ary = circuit_hash.collect do |task, connections|
45
+ task.kind_of?(Activity::End) &&
46
+ connections.empty? &&
47
+ tasks_with_incoming_edge.include?(task) ? task : nil
48
+ end
49
+
50
+ ary.compact
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,62 @@
1
+ module Trailblazer
2
+ module Activity::Magnetic
3
+ # Transforms an array of {Triplett}s into a circuit hash.
4
+ module Generate
5
+ Line = Struct.new(:source, :output)
6
+ MinusPole = Struct.new(:color)
7
+
8
+ class OpenLines
9
+ def initialize
10
+ @arr = []
11
+ end
12
+
13
+ def pop(signal, &block)
14
+ lines = @arr.find_all { |line| line.output.color == signal }
15
+ @arr -= lines
16
+
17
+ lines.each(&block)
18
+ lines.any?
19
+ end
20
+
21
+ def <<((node, output))
22
+ @arr << Line.new(node, output)
23
+ end
24
+ end
25
+
26
+ def self.call(tasks)
27
+ open_plus_poles = OpenLines.new
28
+ open_minus_poles = OpenLines.new
29
+ circuit_hash = {}
30
+
31
+ tasks.each do |(magnetic_to, node, outputs)|
32
+ circuit_hash[ node ] ||= {} # DISCUSS: or needed?
33
+
34
+ magnetic_to.each do |edge_color| # minus poles
35
+ open_plus_poles.pop(edge_color) do |line|
36
+ connect( circuit_hash, line.source, line.output.signal, node )
37
+ end and next
38
+
39
+ # only run when there were no open_minus_poles
40
+ open_minus_poles << [node, MinusPole.new(edge_color)] # open inputs on a node, waiting to be connected.
41
+ end
42
+
43
+ outputs.each do |output|
44
+ open_minus_poles.pop(output.color) do |line|
45
+ connect( circuit_hash, node, output.signal, line.source )
46
+ end and next
47
+
48
+ # only run when there were no open_plus_poles
49
+ open_plus_poles << [node, output]
50
+ end
51
+ end
52
+
53
+ circuit_hash
54
+ end
55
+
56
+ # plus minus
57
+ def self.connect(circuit_hash, source, signal, target)
58
+ circuit_hash[ source ][ signal ] = target
59
+ end
60
+ end # Magnetic
61
+ end
62
+ end
@@ -3,8 +3,7 @@ require "hirb"
3
3
  module Trailblazer
4
4
  class Activity
5
5
  module Trace
6
- # TODO:
7
- # * Struct for debug_item
6
+ # TODO: make this simpler.
8
7
  module Present
9
8
  module_function
10
9
 
@@ -14,24 +13,18 @@ module Trailblazer
14
13
  Hirb::Console.format_output(tree, class: :tree, type: :directory)
15
14
  end
16
15
 
17
- # API HERE is: we only know the current element (e.g. task), input, output, and have an "introspection" object that tells us more about the element.
18
- # TODO: the debug_item's "api" sucks, this should be a struct.
19
16
  def tree_for(stack, level, tree)
20
- stack.each do |debug_item|
21
- task = debug_item[0][0]
17
+ stack.each do |captured, *returned|
18
+ task = captured.task
22
19
 
23
- if debug_item.size == 2 # flat
24
- introspect = debug_item[0].last
20
+ name = (node = captured.introspection[task]) ? node[:id] : task
25
21
 
26
- name = (node = introspect[task]) ? node[:id] : task
27
-
28
- tree << [ level, name ]
22
+ if returned.size == 1 # flat
23
+ tree << [ level, name ]
29
24
  else # nesting
30
- tree << [ level, task ]
31
-
32
- tree_for(debug_item[1..-2], level + 1, tree)
25
+ tree << [ level, name ]
33
26
 
34
- tree << [ level+1, debug_item[-1][0] ]
27
+ tree_for(returned[0..-2], level + 1, tree)
35
28
  end
36
29
 
37
30
  tree
@@ -59,10 +52,10 @@ module Trailblazer
59
52
 
60
53
  def color_map
61
54
  {
62
- Trailblazer::Circuit::Start => :blue,
63
- Trailblazer::Circuit::End => :pink,
64
- Trailblazer::Circuit::Right => :green,
65
- Trailblazer::Circuit::Left => :red
55
+ Trailblazer::Activity::Start => :blue,
56
+ Trailblazer::Activity::End => :pink,
57
+ Trailblazer::Activity::Right => :green,
58
+ Trailblazer::Activity::Left => :red
66
59
  }
67
60
  end
68
61