trailblazer-activity 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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