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,47 @@
1
+ module Trailblazer
2
+ class Activity
3
+ # all code related to the magnetic building of a circuit hash lives in this namespace.
4
+ module Magnetic
5
+ # PlusPole "radiates" a color that MinusPoles are attracted to.
6
+ #
7
+ # This datastructure is produced by the DSL and sits in an ADDS.
8
+ PlusPole = Struct.new(:output, :color) do
9
+ private :output
10
+
11
+ def signal
12
+ output.signal
13
+ end
14
+ end # PlusPole
15
+ end
16
+
17
+ class Process
18
+ def self.plan(options={}, &block)
19
+ Magnetic::Builder::Path.plan(options, &block)
20
+ end
21
+
22
+ def self.build(options={}, &block)
23
+ Magnetic::Builder::Path.build(options, &block)
24
+ end
25
+
26
+ def self.draft(options={}, &block) # TODO: remove me!
27
+ Magnetic::Builder::Path.draft(options, &block)
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+
34
+ require "trailblazer/activity/magnetic/dsl"
35
+ require "trailblazer/activity/magnetic/dsl/plus_poles"
36
+ require "trailblazer/activity/magnetic/dsl/alterations"
37
+
38
+ require "trailblazer/activity/schema/dependencies"
39
+
40
+ require "trailblazer/activity/magnetic"
41
+ require "trailblazer/activity/magnetic/builder"
42
+ require "trailblazer/activity/magnetic/builder/block"
43
+ require "trailblazer/activity/magnetic/builder/path"
44
+ require "trailblazer/activity/magnetic/builder/railway"
45
+ require "trailblazer/activity/magnetic/builder/fast_track" # TODO: move to Operation gem.
46
+
47
+ require "trailblazer/activity/magnetic/generate"
@@ -0,0 +1,161 @@
1
+ require "trailblazer/activity/magnetic/finalizer"
2
+
3
+ module Trailblazer
4
+ module Activity::Magnetic
5
+ def self.Builder(implementation, normalizer, builder_options={})
6
+ builder = implementation.new(normalizer, builder_options.freeze).freeze # e.g. Path.new(...)
7
+
8
+ return builder, implementation.InitialAdds(builder_options)
9
+ end
10
+
11
+ class Builder
12
+
13
+ def self.plan_for(builder, adds, &block)
14
+ adds += Block.new(builder).(&block) # returns ADDS
15
+ end
16
+
17
+ def self.build(options={}, &block)
18
+ adds = plan( options, &block )
19
+
20
+ Finalizer.(adds)
21
+ end
22
+
23
+ def initialize(normalizer, builder_options)
24
+ @normalizer, @builder_options = normalizer, builder_options
25
+ end
26
+
27
+ def self.merge(activity, merged)
28
+ merged = merged[2..-1] || []
29
+
30
+ activity + merged
31
+ end
32
+
33
+ module DSLMethods
34
+ # Output( Left, :failure )
35
+ # Output( :failure ) #=> Output::Semantic
36
+ def Output(signal, semantic=nil)
37
+ return DSL::Output::Semantic.new(signal) if semantic.nil?
38
+
39
+ Activity.Output(signal, semantic)
40
+ end
41
+
42
+ def End(name, semantic)
43
+ Activity.End(name, semantic)
44
+ end
45
+
46
+ def Path(track_color: "track_#{rand}", end_semantic: :success, **options)
47
+ options = options.merge(track_color: track_color, end_semantic: end_semantic)
48
+
49
+ # this block is called in DSL::ProcessTuples.
50
+ ->(block) { [ track_color, Builder::Path.plan( options, @normalizer, &block ) ] }
51
+ end
52
+ end
53
+
54
+ include DSLMethods
55
+
56
+ private
57
+
58
+ # Internal top-level entry point to add task(s) and connections.
59
+ def insert_element(impl, polarizations, task, options, &block)
60
+ adds, *returned_options = Builder.adds_for(polarizations, @normalizer, impl.keywords, task, options, &block)
61
+ end
62
+
63
+ # Options valid for all DSL calls with this Builder framework.
64
+ def self.generic_keywords
65
+ [ :id, :plus_poles, :magnetic_to, :adds ]
66
+ end
67
+
68
+ def self.sequence_keywords
69
+ [ :group, :before, :after, :replace, :delete ] # hard-wires Builder to Sequence/Alterations.
70
+ end
71
+
72
+ # Produce two hashes, one "local" options with DSL-specific options such as `:fast_track`,
73
+ # one with generic DSL options, for example tuples like `Right=>Output(:failure)`.
74
+ def self.normalize(options, local_keys)
75
+ local, foreign = {}, {}
76
+ options.each { |k,v| local_keys.include?(k) ? local[k] = v : foreign[k] = v }
77
+
78
+ return foreign, local
79
+ end
80
+
81
+ # @return Adds
82
+ # High level interface for DSL calls like ::task or ::step.
83
+ # TODO: RETURN ALL OPTIONS
84
+ def self.adds_for(polarizations, normalizer, keywords, task, options, &block)
85
+ task, local_options, options, sequence_options = normalize_options(normalizer, keywords, task, options)
86
+
87
+ initial_plus_poles = local_options[:plus_poles]
88
+ magnetic_to = local_options[:magnetic_to]
89
+ adds = local_options[:adds] || []
90
+
91
+ # go through all wiring options such as Output()=>:color.
92
+ polarizations_from_user_options, additional_adds = DSL::ProcessOptions.(local_options[:id], options, initial_plus_poles, &block)
93
+
94
+ result = adds(local_options[:id], task, initial_plus_poles, polarizations, polarizations_from_user_options, local_options, sequence_options, magnetic_to)
95
+
96
+ return result + adds + additional_adds, task, local_options, options, sequence_options
97
+ end
98
+
99
+ # @private
100
+ def self.normalize_options(normalizer, keywords, task, options)
101
+ # sort through the "original" user DSL options.
102
+ options, local_options = normalize( options, generic_keywords+keywords ) # DISCUSS:
103
+ options, sequence_options = normalize( options, sequence_keywords )
104
+
105
+ task, local_options, options, sequence_options = normalizer.(task, local_options, options, sequence_options)
106
+
107
+ return task, local_options, options, sequence_options
108
+ end
109
+
110
+ # Low-level interface for DSL calls (e.g. Start, where "you know what you're doing")
111
+ # @private
112
+ def self.adds(id, task, initial_plus_poles, polarization, polarizations_from_user_options, options, sequence_options, magnetic_to = nil)
113
+ magnetic_to, plus_poles = apply_polarizations(
114
+ polarization + polarizations_from_user_options,
115
+ magnetic_to,
116
+ initial_plus_poles,
117
+ options
118
+ )
119
+
120
+ Add( id, task, magnetic_to, plus_poles,
121
+ options, #{ fast_track: true },
122
+ sequence_options #{ group: :main }
123
+ )
124
+ end
125
+
126
+ # Called once per DSL method call, e.g. ::step.
127
+ #
128
+ # The idea is to chain a bunch of PlusPoles transformations (and magnetic_to "transformations")
129
+ # for each DSL call, and thus realize things like path+railway+fast_track
130
+ def self.apply_polarizations(polarizations, magnetic_to, plus_poles, options)
131
+ polarizations.inject([magnetic_to, plus_poles]) do |args, pol|
132
+ magnetic, plus_poles = pol.(*args, options)
133
+ end
134
+ end
135
+
136
+ def self.Add(id, task, magnetic_to, plus_poles, options, sequence_options)
137
+ [
138
+ [ :add, [id, [ magnetic_to, task, plus_poles.to_a ], sequence_options] ],
139
+ ]
140
+ end
141
+
142
+ class DefaultNormalizer
143
+ def initialize( **default_options )
144
+ raise "you didn't specify default :plus_poles" unless default_options[:plus_poles]
145
+
146
+ @default_options = default_options
147
+ end
148
+
149
+ # called for every ::task, ::step call etc to defaultize the `local_options`.
150
+ def call(task, local_options, options, sequence_options)
151
+ local_options = @default_options.merge(local_options) # here, we merge default :plus_poles.
152
+
153
+ return task, local_options, options, sequence_options
154
+ end
155
+ end
156
+
157
+ end # Builder
158
+ end
159
+ end
160
+
161
+
@@ -0,0 +1,37 @@
1
+ module Trailblazer
2
+ module Activity::Magnetic
3
+ # Mutable DSL object.
4
+ #
5
+ # This is the only object mutated when building the ADDS, and it's only
6
+ # used when using block-syntax, such as
7
+ # Path.plan do
8
+ # # ..
9
+ # end
10
+ class Builder::Block
11
+ def initialize(builder)
12
+ @builder = builder
13
+ @adds = [] # mutable
14
+ end
15
+
16
+ # Evaluate user's block and return the new ADDS.
17
+ # Used in Builder::plan or in nested DSL calls.
18
+ def call(&block)
19
+ instance_exec(&block)
20
+ @adds
21
+ end
22
+
23
+ [:task, :step, :pass, :fail].each do |name| # create :step, :pass, :task, etc.
24
+ define_method(name) { |*args, &block| capture_adds(name, *args, &block) } # def step(..) => forward to builder, track ADDS
25
+ end
26
+
27
+ extend Forwardable
28
+ def_delegators :@builder, :Output, :Path, :End # TODO: make this official.
29
+
30
+ # #task, #step, etc. are called via the immutable builder.
31
+ def capture_adds(name, *args, &block)
32
+ adds, *returned_options = @builder.send(name, *args, &block)
33
+ @adds += adds
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,141 @@
1
+ # TODO: move me to Operation.
2
+ module Trailblazer
3
+ module Activity::Magnetic
4
+ class Builder
5
+
6
+ class FastTrack < Builder
7
+ def self.for(normalizer, builder_options={}) # Build the Builder.
8
+ Activity::Magnetic::Builder(
9
+ FastTrack,
10
+ normalizer,
11
+ { track_color: :success, end_semantic: :success, failure_color: :failure }.merge( builder_options )
12
+ )
13
+ end
14
+
15
+ def self.plan(options={}, normalizer=DefaultNormalizer.new(plus_poles: default_plus_poles), &block)
16
+ plan_for( *FastTrack.for(normalizer, options), &block )
17
+ end
18
+
19
+ def self.StepPolarizations(**options)
20
+ [
21
+ *Railway.StepPolarizations(options),
22
+ StepPolarization.new(options)
23
+ ]
24
+ end
25
+
26
+ def self.FailPolarizations(**options)
27
+ [
28
+ *Railway.FailPolarizations(options),
29
+ FailPolarization.new(options)
30
+ ]
31
+ end
32
+
33
+ def self.PassPolarizations(**options)
34
+ [
35
+ *Railway.PassPolarizations(options),
36
+ PassPolarization.new(options)
37
+ ]
38
+ end
39
+
40
+ # pass_fast: true simply means: color my :success Output with :pass_fast color
41
+ class StepPolarization < Railway::StepPolarization
42
+ def call(magnetic_to, plus_poles, options)
43
+ plus_poles = plus_poles.reconnect( :success => :pass_fast ) if options[:pass_fast]
44
+ plus_poles = plus_poles.reconnect( :failure => :fail_fast ) if options[:fail_fast]
45
+
46
+ # add fast track outputs if :fast_track
47
+ plus_poles = plus_poles.reverse_merge(
48
+ Activity.Output(FailFast, :fail_fast) => :fail_fast,
49
+ Activity.Output(PassFast, :pass_fast) => :pass_fast
50
+ ) if options[:fast_track]
51
+
52
+ [
53
+ magnetic_to,
54
+ plus_poles
55
+ ]
56
+ end
57
+ end
58
+
59
+ class FailPolarization < Railway::StepPolarization
60
+ def call(magnetic_to, plus_poles, options)
61
+ plus_poles = plus_poles.reconnect( :failure => :fail_fast, :success => :fail_fast ) if options[:fail_fast]
62
+ plus_poles = plus_poles.reverse_merge( Activity.Output(FailFast, :fail_fast) => :fail_fast, Activity.Output(PassFast, :pass_fast) => :pass_fast ) if options[:fast_track]
63
+
64
+ [
65
+ magnetic_to,
66
+ plus_poles
67
+ ]
68
+ end
69
+ end
70
+
71
+ class PassPolarization < Railway::StepPolarization
72
+ def call(magnetic_to, plus_poles, options)
73
+ plus_poles = plus_poles.reconnect( :success => :pass_fast, :failure => :pass_fast ) if options[:pass_fast]
74
+ plus_poles = plus_poles.reverse_merge( Activity.Output(FailFast, :fail_fast) => :fail_fast, Activity.Output(PassFast, :pass_fast) => :pass_fast ) if options[:fast_track]
75
+
76
+ [
77
+ magnetic_to,
78
+ plus_poles
79
+ ]
80
+ end
81
+ end
82
+
83
+ def self.default_plus_poles(*args)
84
+ Railway.default_plus_poles(*args)
85
+ end
86
+
87
+ def self.keywords
88
+ [:fail_fast, :pass_fast, :fast_track, :type, :override]
89
+ end
90
+
91
+
92
+ # Adds the End.fail_fast and End.pass_fast end to the Railway sequence.
93
+ def self.InitialAdds(pass_fast_end: Activity.End("pass_fast", :pass_fast), fail_fast_end: Activity.End("fail_fast", :fail_fast), **builder_options)
94
+ path_adds = Railway.InitialAdds(**builder_options)
95
+
96
+ ends =
97
+ adds(
98
+ "End.pass_fast", pass_fast_end,
99
+
100
+ {}, # plus_poles
101
+ Path::TaskPolarizations(builder_options.merge( type: :End )),
102
+ [],
103
+
104
+ {},
105
+ { group: :end },
106
+ [:pass_fast]
107
+ )+
108
+ adds(
109
+ "End.fail_fast", fail_fast_end,
110
+
111
+ {}, # plus_poles
112
+ Path::TaskPolarizations(builder_options.merge( type: :End )),
113
+ [],
114
+
115
+ {},
116
+ { group: :end },
117
+ [:fail_fast]
118
+ )
119
+
120
+ path_adds + ends
121
+ end
122
+
123
+ # Direction signals.
124
+ FailFast = Class.new(Activity::Signal)
125
+ PassFast = Class.new(Activity::Signal)
126
+
127
+ def step(task, options={}, &block)
128
+ insert_element( FastTrack, FastTrack.StepPolarizations(@builder_options), task, options, &block )
129
+ end
130
+
131
+ def fail(task, options={}, &block)
132
+ insert_element( FastTrack, FastTrack.FailPolarizations(@builder_options), task, options, &block )
133
+ end
134
+
135
+ def pass(task, options={}, &block)
136
+ insert_element( FastTrack, FastTrack.PassPolarizations(@builder_options), task, options, &block )
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,98 @@
1
+ module Trailblazer
2
+ module Activity::Magnetic
3
+ class Builder
4
+
5
+ class Path < Builder
6
+ # strategy_options:
7
+ # :track_color
8
+ # :end_semantic
9
+ def self.for(normalizer, builder_options={}) # Build the Builder.
10
+ Activity::Magnetic::Builder(
11
+ Path,
12
+ normalizer,
13
+ { track_color: :success, end_semantic: :success }.merge( builder_options )
14
+ )
15
+ end
16
+
17
+ def self.plan(options={}, normalizer=DefaultNormalizer.new(plus_poles: default_plus_poles), &block)
18
+ plan_for( *Path.for(normalizer, options), &block )
19
+ end
20
+
21
+
22
+ def self.keywords
23
+ [:type]
24
+ end
25
+
26
+ def task(task, options={}, &block)
27
+ polarizations = Path.TaskPolarizations( @builder_options.merge(type: options[:type]) ) # DISCUSS: handle :type here? Really?
28
+
29
+ insert_element( Path, polarizations, task, options, &block )
30
+ end
31
+
32
+
33
+ def self.default_plus_poles
34
+ DSL::PlusPoles.new.merge(
35
+ Activity.Output( Activity::Right, :success ) => nil
36
+ ).freeze
37
+ end
38
+
39
+ # @return [Adds] list of Adds instances that can be chained or added to an existing sequence.
40
+ def self.InitialAdds(track_color:raise, end_semantic:raise, default_plus_poles: self.default_plus_poles, track_end: Activity.End(track_color, end_semantic), **o)
41
+
42
+ builder_options={ track_color: track_color, end_semantic: end_semantic }
43
+
44
+ start_adds = adds(
45
+ "Start.default", Activity::Start.new(:default),
46
+
47
+ default_plus_poles,
48
+ TaskPolarizations(builder_options),
49
+ [],
50
+
51
+ {}, { group: :start },
52
+ [] # magnetic_to
53
+ )
54
+
55
+ end_adds = adds(
56
+ "End.#{track_color}", track_end,
57
+
58
+ {}, # plus_poles
59
+ TaskPolarizations(builder_options.merge( type: :End )),
60
+ [],
61
+
62
+ {}, { group: :end }
63
+ )
64
+
65
+ start_adds + end_adds
66
+ end
67
+
68
+ def self.TaskPolarizations(track_color:raise, type: :task, **o)
69
+ return [EndPolarization.new( track_color: track_color )] if type == :End # DISCUSS: should this dispatch be here?
70
+
71
+ [TaskPolarization.new( track_color: track_color )]
72
+ end
73
+
74
+ class TaskPolarization
75
+ def initialize( track_color:raise )
76
+ @track_color = track_color
77
+ end
78
+
79
+ def call(magnetic_to, plus_poles, options)
80
+ [
81
+ magnetic_to || [@track_color],
82
+ plus_poles.reconnect( :success => @track_color )
83
+ ]
84
+ end
85
+ end # TaskPolarization
86
+
87
+ class EndPolarization < TaskPolarization
88
+ def call(magnetic_to, plus_poles, options)
89
+ [
90
+ magnetic_to || [@track_color],
91
+ {}
92
+ ]
93
+ end
94
+ end # EndPolarization
95
+ end # Path
96
+ end # Builder
97
+ end
98
+ end