trailblazer-future 2.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (161) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.travis.yml +6 -0
  4. data/CHANGES.md +4 -0
  5. data/LICENSE.txt +9 -0
  6. data/README.md +48 -0
  7. data/Rakefile +10 -0
  8. data/bin/console +14 -0
  9. data/bin/setup +8 -0
  10. data/gems.rb +3 -0
  11. data/lib/trailblazer/future.rb +9 -0
  12. data/lib/trailblazer/future/version.rb +5 -0
  13. data/lib/trailblazer/v2_1/activity.rb +123 -0
  14. data/lib/trailblazer/v2_1/activity/config.rb +37 -0
  15. data/lib/trailblazer/v2_1/activity/dsl/add_task.rb +22 -0
  16. data/lib/trailblazer/v2_1/activity/dsl/helper.rb +68 -0
  17. data/lib/trailblazer/v2_1/activity/dsl/magnetic.rb +36 -0
  18. data/lib/trailblazer/v2_1/activity/dsl/magnetic/builder.rb +101 -0
  19. data/lib/trailblazer/v2_1/activity/dsl/magnetic/builder/default_normalizer.rb +26 -0
  20. data/lib/trailblazer/v2_1/activity/dsl/magnetic/builder/fast_track.rb +118 -0
  21. data/lib/trailblazer/v2_1/activity/dsl/magnetic/builder/normalizer.rb +113 -0
  22. data/lib/trailblazer/v2_1/activity/dsl/magnetic/builder/path.rb +105 -0
  23. data/lib/trailblazer/v2_1/activity/dsl/magnetic/builder/railway.rb +97 -0
  24. data/lib/trailblazer/v2_1/activity/dsl/magnetic/builder/state.rb +58 -0
  25. data/lib/trailblazer/v2_1/activity/dsl/magnetic/finalizer.rb +51 -0
  26. data/lib/trailblazer/v2_1/activity/dsl/magnetic/generate.rb +62 -0
  27. data/lib/trailblazer/v2_1/activity/dsl/magnetic/merge.rb +16 -0
  28. data/lib/trailblazer/v2_1/activity/dsl/magnetic/process_options.rb +76 -0
  29. data/lib/trailblazer/v2_1/activity/dsl/magnetic/structure/alterations.rb +44 -0
  30. data/lib/trailblazer/v2_1/activity/dsl/magnetic/structure/plus_poles.rb +85 -0
  31. data/lib/trailblazer/v2_1/activity/dsl/magnetic/structure/polarization.rb +23 -0
  32. data/lib/trailblazer/v2_1/activity/dsl/record.rb +11 -0
  33. data/lib/trailblazer/v2_1/activity/dsl/schema/dependencies.rb +46 -0
  34. data/lib/trailblazer/v2_1/activity/dsl/schema/sequence.rb +46 -0
  35. data/lib/trailblazer/v2_1/activity/dsl/strategy/build_state.rb +32 -0
  36. data/lib/trailblazer/v2_1/activity/dsl/strategy/fast_track.rb +24 -0
  37. data/lib/trailblazer/v2_1/activity/dsl/strategy/path.rb +26 -0
  38. data/lib/trailblazer/v2_1/activity/dsl/strategy/plan.rb +36 -0
  39. data/lib/trailblazer/v2_1/activity/dsl/strategy/railway.rb +23 -0
  40. data/lib/trailblazer/v2_1/activity/interface.rb +16 -0
  41. data/lib/trailblazer/v2_1/activity/introspect.rb +167 -0
  42. data/lib/trailblazer/v2_1/activity/present.rb +95 -0
  43. data/lib/trailblazer/v2_1/activity/structures.rb +57 -0
  44. data/lib/trailblazer/v2_1/activity/task_builder.rb +41 -0
  45. data/lib/trailblazer/v2_1/activity/task_wrap.rb +40 -0
  46. data/lib/trailblazer/v2_1/activity/task_wrap/call_task.rb +19 -0
  47. data/lib/trailblazer/v2_1/activity/task_wrap/merge.rb +23 -0
  48. data/lib/trailblazer/v2_1/activity/task_wrap/runner.rb +62 -0
  49. data/lib/trailblazer/v2_1/activity/task_wrap/trace.rb +44 -0
  50. data/lib/trailblazer/v2_1/activity/task_wrap/variable_mapping.rb +162 -0
  51. data/lib/trailblazer/v2_1/activity/testing.rb +32 -0
  52. data/lib/trailblazer/v2_1/activity/trace.rb +97 -0
  53. data/lib/trailblazer/v2_1/circuit.rb +71 -0
  54. data/lib/trailblazer/v2_1/container_chain.rb +45 -0
  55. data/lib/trailblazer/v2_1/context.rb +79 -0
  56. data/lib/trailblazer/v2_1/deprecation/call.rb +46 -0
  57. data/lib/trailblazer/v2_1/deprecation/context.rb +43 -0
  58. data/lib/trailblazer/v2_1/dsl.rb +47 -0
  59. data/lib/trailblazer/v2_1/macro.rb +11 -0
  60. data/lib/trailblazer/v2_1/macro/contract.rb +5 -0
  61. data/lib/trailblazer/v2_1/operation.rb +91 -0
  62. data/lib/trailblazer/v2_1/operation/auto_inject.rb +47 -0
  63. data/lib/trailblazer/v2_1/operation/callable.rb +42 -0
  64. data/lib/trailblazer/v2_1/operation/class_dependencies.rb +25 -0
  65. data/lib/trailblazer/v2_1/operation/contract.rb +61 -0
  66. data/lib/trailblazer/v2_1/operation/deprecated_macro.rb +19 -0
  67. data/lib/trailblazer/v2_1/operation/deprecations.rb +21 -0
  68. data/lib/trailblazer/v2_1/operation/guard.rb +18 -0
  69. data/lib/trailblazer/v2_1/operation/heritage.rb +30 -0
  70. data/lib/trailblazer/v2_1/operation/inject.rb +36 -0
  71. data/lib/trailblazer/v2_1/operation/inspect.rb +79 -0
  72. data/lib/trailblazer/v2_1/operation/model.rb +50 -0
  73. data/lib/trailblazer/v2_1/operation/module.rb +29 -0
  74. data/lib/trailblazer/v2_1/operation/nested.rb +98 -0
  75. data/lib/trailblazer/v2_1/operation/persist.rb +14 -0
  76. data/lib/trailblazer/v2_1/operation/policy.rb +44 -0
  77. data/lib/trailblazer/v2_1/operation/public_call.rb +55 -0
  78. data/lib/trailblazer/v2_1/operation/pundit.rb +38 -0
  79. data/lib/trailblazer/v2_1/operation/railway.rb +32 -0
  80. data/lib/trailblazer/v2_1/operation/railway/fast_track.rb +13 -0
  81. data/lib/trailblazer/v2_1/operation/railway/macaroni.rb +23 -0
  82. data/lib/trailblazer/v2_1/operation/railway/normalizer.rb +58 -0
  83. data/lib/trailblazer/v2_1/operation/railway/task_builder.rb +37 -0
  84. data/lib/trailblazer/v2_1/operation/rescue.rb +42 -0
  85. data/lib/trailblazer/v2_1/operation/responder.rb +18 -0
  86. data/lib/trailblazer/v2_1/operation/result.rb +30 -0
  87. data/lib/trailblazer/v2_1/operation/test.rb +17 -0
  88. data/lib/trailblazer/v2_1/operation/trace.rb +46 -0
  89. data/lib/trailblazer/v2_1/operation/validate.rb +76 -0
  90. data/lib/trailblazer/v2_1/operation/wrap.rb +83 -0
  91. data/lib/trailblazer/v2_1/option.rb +78 -0
  92. data/lib/trailblazer/v2_1/rails.rb +12 -0
  93. data/lib/trailblazer/v2_1/rails/cell.rb +22 -0
  94. data/lib/trailblazer/v2_1/rails/controller.rb +66 -0
  95. data/lib/trailblazer/v2_1/rails/form.rb +21 -0
  96. data/lib/trailblazer/v2_1/rails/railtie.rb +31 -0
  97. data/lib/trailblazer/v2_1/rails/railtie/extend_application_controller.rb +28 -0
  98. data/lib/trailblazer/v2_1/rails/railtie/loader.rb +58 -0
  99. data/lib/trailblazer/v2_1/rails/test/integration.rb +6 -0
  100. data/lib/trailblazer/v2_1/versions.txt +7 -0
  101. data/test/rails5.0/.gitignore +17 -0
  102. data/test/rails5.0/Gemfile +21 -0
  103. data/test/rails5.0/Rakefile +3 -0
  104. data/test/rails5.0/app/concepts/artist/cell/dashboard.rb +7 -0
  105. data/test/rails5.0/app/concepts/artist/cell/show.rb +4 -0
  106. data/test/rails5.0/app/concepts/artist/view/dashboard.erb +4 -0
  107. data/test/rails5.0/app/concepts/artist/view/show.erb +1 -0
  108. data/test/rails5.0/app/concepts/params/operation/with_args.rb +5 -0
  109. data/test/rails5.0/app/concepts/song/contract/form.rb +6 -0
  110. data/test/rails5.0/app/concepts/song/operation/create.rb +15 -0
  111. data/test/rails5.0/app/concepts/song/operation/show.rb +3 -0
  112. data/test/rails5.0/app/concepts/song/operation/update.rb +15 -0
  113. data/test/rails5.0/app/controllers/application_controller.rb +46 -0
  114. data/test/rails5.0/app/controllers/args_controller.rb +5 -0
  115. data/test/rails5.0/app/controllers/artists_controller.rb +24 -0
  116. data/test/rails5.0/app/controllers/params_controller.rb +11 -0
  117. data/test/rails5.0/app/controllers/songs_controller.rb +35 -0
  118. data/test/rails5.0/app/models/artist.rb +2 -0
  119. data/test/rails5.0/app/models/song.rb +2 -0
  120. data/test/rails5.0/app/views/args/with_args.html.erb +1 -0
  121. data/test/rails5.0/app/views/layouts/application.html.erb +2 -0
  122. data/test/rails5.0/app/views/songs/edit.html.erb +4 -0
  123. data/test/rails5.0/app/views/songs/new.html.erb +5 -0
  124. data/test/rails5.0/app/views/songs/new_with_result.html.erb +3 -0
  125. data/test/rails5.0/app/views/songs/show.html.erb +3 -0
  126. data/test/rails5.0/bin/bundle +3 -0
  127. data/test/rails5.0/bin/rails +4 -0
  128. data/test/rails5.0/bin/rake +4 -0
  129. data/test/rails5.0/bin/setup +29 -0
  130. data/test/rails5.0/config.ru +4 -0
  131. data/test/rails5.0/config/application.rb +25 -0
  132. data/test/rails5.0/config/boot.rb +3 -0
  133. data/test/rails5.0/config/database.yml +21 -0
  134. data/test/rails5.0/config/environment.rb +5 -0
  135. data/test/rails5.0/config/environments/development.rb +41 -0
  136. data/test/rails5.0/config/environments/test.rb +42 -0
  137. data/test/rails5.0/config/initializers/assets.rb +11 -0
  138. data/test/rails5.0/config/initializers/backtrace_silencers.rb +7 -0
  139. data/test/rails5.0/config/initializers/cookies_serializer.rb +3 -0
  140. data/test/rails5.0/config/initializers/filter_parameter_logging.rb +4 -0
  141. data/test/rails5.0/config/initializers/inflections.rb +16 -0
  142. data/test/rails5.0/config/initializers/mime_types.rb +4 -0
  143. data/test/rails5.0/config/initializers/session_store.rb +3 -0
  144. data/test/rails5.0/config/initializers/wrap_parameters.rb +14 -0
  145. data/test/rails5.0/config/locales/en.yml +23 -0
  146. data/test/rails5.0/config/routes.rb +15 -0
  147. data/test/rails5.0/config/secrets.yml +17 -0
  148. data/test/rails5.0/db/migrate/20161122145124_create_songs.rb +8 -0
  149. data/test/rails5.0/db/schema.rb +19 -0
  150. data/test/rails5.0/log/.keep +0 -0
  151. data/test/rails5.0/test/concepts/song/operation/create_test.rb +11 -0
  152. data/test/rails5.0/test/concepts/song/operation/update_test.rb +34 -0
  153. data/test/rails5.0/test/integration/.keep +0 -0
  154. data/test/rails5.0/test/integration/args_controller_test.rb +8 -0
  155. data/test/rails5.0/test/integration/artists_controller_test.rb +28 -0
  156. data/test/rails5.0/test/integration/params_controller_test.rb +8 -0
  157. data/test/rails5.0/test/integration/songs_controller_test.rb +40 -0
  158. data/test/rails5.0/test/test_helper.rb +7 -0
  159. data/test/test_helper.rb +5 -0
  160. data/trailblazer-future.gemspec +25 -0
  161. metadata +246 -0
@@ -0,0 +1,97 @@
1
+ module Trailblazer::V2_1
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
+ # Adds the End.failure end to the Path sequence.
14
+ # @return [Adds] list of Adds instances that can be chained or added to an existing sequence.
15
+ def self.InitialAdds(failure_color:raise, failure_end: Activity.End(failure_color), **builder_options)
16
+ path_adds = Path.InitialAdds(**builder_options)
17
+
18
+ end_adds = adds(
19
+ failure_end,
20
+
21
+ Path::EndEventPolarizations(builder_options),
22
+
23
+ {},
24
+ { group: :end },
25
+
26
+ magnetic_to: [failure_color],
27
+ id: "End.#{failure_color}",
28
+ plus_poles: {},
29
+ )
30
+
31
+ path_adds + end_adds
32
+ end
33
+
34
+ # ONLY JOB: magnetic_to and Outputs ("Polarization") via PlusPoles.merge
35
+ def self.StepPolarizations(**options)
36
+ [
37
+ *Path.TaskPolarizations(options),
38
+ StepPolarization.new(options)
39
+ ]
40
+ end
41
+
42
+ def self.PassPolarizations(options)
43
+ [
44
+ Railway::PassPolarization.new( options )
45
+ ]
46
+ end
47
+
48
+ def self.FailPolarizations(options)
49
+ [
50
+ Railway::FailPolarization.new( options )
51
+ ]
52
+ end
53
+
54
+ class StepPolarization
55
+ def initialize(track_color: :success, failure_color: :failure, **o)
56
+ @track_color, @failure_color = track_color, failure_color
57
+ end
58
+
59
+ # Returns the polarization for a DSL call. Takes care of user options such as :magnetic_to.
60
+ def call(magnetic_to, plus_poles, options)
61
+ [
62
+ magnetic_to || default_magnetic_to,
63
+ plus_poles_for(plus_poles, options),
64
+ ]
65
+ end
66
+
67
+ private
68
+
69
+ def plus_poles_for(plus_poles, options)
70
+ plus_poles.reconnect( :failure => @failure_color )
71
+ end
72
+
73
+ def default_magnetic_to
74
+ [@track_color]
75
+ end
76
+ end
77
+
78
+ class PassPolarization < StepPolarization
79
+ def plus_poles_for(plus_poles, options)
80
+ plus_poles.reconnect( :failure => @track_color, :success => @track_color )
81
+ end
82
+ end
83
+
84
+ class FailPolarization < StepPolarization
85
+ def default_magnetic_to
86
+ [@failure_color]
87
+ end
88
+
89
+ def plus_poles_for(plus_poles, options)
90
+ plus_poles.reconnect( :failure => @failure_color, :success => @failure_color )
91
+ end
92
+ end
93
+
94
+ end # Railway
95
+ end # Builder
96
+ end
97
+ end
@@ -0,0 +1,58 @@
1
+ module Trailblazer::V2_1
2
+ class Activity::Magnetic::Builder
3
+ # Maintain Builder instance plus Adds/Process/Outputs as immutable objects.
4
+ module State
5
+ def self.build(builder_class, normalizer, builder_options)
6
+ builder, adds = builder_class.for(normalizer, builder_options) # e.g. Path.for(...) which creates a Builder::Path instance.
7
+
8
+ recompile(builder.freeze, adds.freeze)
9
+ end
10
+
11
+ def self.add(builder, adds, strategy, polarizer, *args, &block)
12
+ new_adds, *returned_options = builder.insert(strategy, polarizer, *args, &block) # TODO: move that out of here.
13
+
14
+ adds = adds + new_adds
15
+
16
+ recompile(builder, adds.freeze, returned_options)
17
+ end
18
+
19
+ private
20
+
21
+ # @return {builder, Adds, Process, outputs}, returned_options
22
+ def self.recompile(builder, adds, *args)
23
+ circuit, outputs = recompile_circuit(adds)
24
+
25
+ return builder, adds, circuit.freeze, outputs.freeze, *args
26
+ end
27
+
28
+ def self.recompile_circuit(adds)
29
+ circuit, outputs = Recompile.( adds )
30
+ end
31
+
32
+ module Recompile
33
+ # Recompile the circuit and outputs from the {ADDS} instance that collects circuit tasks and connections.
34
+ #
35
+ # @return [Process, Hash] The {Process} instance and its outputs hash.
36
+ def self.call(adds)
37
+ circuit, end_events = Finalizer.(adds)
38
+ outputs = recompile_outputs(end_events)
39
+
40
+ return circuit, outputs
41
+ end
42
+
43
+ private
44
+
45
+ def self.recompile_outputs(end_events)
46
+ ary = end_events.collect do |evt|
47
+ [
48
+ semantic = evt.to_h[:semantic],
49
+ Activity::Output(evt, semantic)
50
+ ]
51
+ end
52
+
53
+ ::Hash[ ary ]
54
+ end
55
+ end # Recompile
56
+ end # State
57
+ end
58
+ end
@@ -0,0 +1,51 @@
1
+ module Trailblazer::V2_1
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 Circuit.new(circuit_hash, end_events, start_task: circuit_hash.keys.first), end_events
38
+ end
39
+
40
+ # Find all end events that don't have outgoing connections.
41
+ def self.end_events_for(circuit_hash)
42
+ ary = circuit_hash.collect do |task, connections|
43
+ task.kind_of?(Activity::End) && connections.empty? ? task : nil
44
+ end
45
+
46
+ ary.compact
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,62 @@
1
+ module Trailblazer::V2_1
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
@@ -0,0 +1,16 @@
1
+ class Trailblazer::V2_1::Activity < Module
2
+ module Magnetic
3
+ module Merge
4
+ def merge!(merged)
5
+ merged[:record].each do |key, args|
6
+ dsl_method, *args = args
7
+
8
+ return send( dsl_method, args[0], args[1], &args[2] ) if args[2]
9
+ send( dsl_method, args[0], args[1] )
10
+ end
11
+
12
+ self
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,76 @@
1
+ module Trailblazer::V2_1
2
+ module Activity::Magnetic
3
+ module DSL
4
+ # This module only processes additional "wiring" options from the DSL calls
5
+ # Output(:success) => End("my.new")
6
+ #
7
+ # Returns PlusPoles and additional sequence alterations.
8
+ module ProcessOptions
9
+ module_function
10
+
11
+ # Output => target (End/"id"/:color)
12
+ # @return [PlusPole]
13
+ # @return additional alterations
14
+ #
15
+ # options:
16
+ # { DSL::Output[::Semantic] => target }
17
+ #
18
+ def call(id, options, plus_poles, &block)
19
+ polarization, adds =
20
+ options.
21
+ collect { |key, task|
22
+ # this method call is the only thing that really matters here. # TODO: make this transformation a bit more obvious.
23
+ process_tuple(id, key, task, plus_poles, &block)
24
+ }.
25
+ inject([[],[]]) { |memo, (polarization, adds)| memo[0]<<polarization; memo[1]<<adds; memo }
26
+
27
+ return polarization, adds.flatten(1)
28
+ end
29
+
30
+ def process_tuple(id, output, task, plus_poles, &block)
31
+ output = output_for(output, plus_poles) if output.kind_of?(Activity::DSL::OutputSemantic)
32
+
33
+ if task.kind_of?(Activity::End)
34
+ # raise %{An end event with semantic `#{task.to_h[:semantic]}` has already been added. Please use an ID reference: `=> "End.#{task.to_h[:semantic]}"`} if
35
+ new_edge = "#{id}-#{output.signal}"
36
+
37
+ [
38
+ Polarization.new( output: output, color: new_edge ),
39
+ [ [:add, [task.to_h[:semantic], [ [new_edge], task, [] ], group: :end]] ]
40
+ ]
41
+ # procs come from DSL calls such as `Path() do ... end`.
42
+ elsif task.is_a?(Proc)
43
+ start_color, activity = task.(block)
44
+
45
+ adds = activity.to_h[:adds]
46
+
47
+ [
48
+ Polarization.new( output: output, color: start_color ),
49
+ # TODO: this is a pseudo-"merge" and should be public API at some point.
50
+ # TODO: we also need to merge all the other states such as debug.
51
+ adds[1..-1] # drop start
52
+ ]
53
+ elsif task.is_a?(Activity::DSL::Track) # An additional plus polarization. Example: Output => :success
54
+ [
55
+ Polarization.new( output: output, color: task.color )
56
+ ]
57
+ else # ID: existing step
58
+ new_edge = "#{id}-#{output.signal}-#{task}" # edge from <id> to <target>
59
+
60
+ [
61
+ Polarization.new( output: output, color: new_edge ),
62
+ [[ :magnetic_to, [ task, [new_edge] ] ]], # mark target (`task`) as magnetic to the new edge.
63
+ ]
64
+ end
65
+ end
66
+
67
+ # @param semantic DSL::Output::Semantic
68
+ def output_for(semantic, plus_poles)
69
+ # DISCUSS: review PlusPoles#[]
70
+ output, _ = plus_poles.instance_variable_get(:@plus_poles)[semantic.value]
71
+ output or raise("Couldn't find existing output for `#{semantic.value.inspect}`.")
72
+ end
73
+ end # OptionsProcessing
74
+ end # DSL
75
+ end
76
+ end
@@ -0,0 +1,44 @@
1
+ module Trailblazer::V2_1
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,85 @@
1
+ module Trailblazer::V2_1
2
+ module Activity::Magnetic
3
+ # A plus pole is associating an Output{:signal, :semantic} to a magnetic :color.
4
+ #
5
+ # When it comes to connecting tasks to each other, PlusPoles is the most important object
6
+ # here. When a task is added via the DSL, a PlusPoles is set up, and the DSL adds polarizations
7
+ # from the implementation and from the options (e.g. `Outputs(..) => ..`).
8
+ #
9
+ # These are then finalized and return the effective plus poles
10
+
11
+ # Polarization is one or multiple calls to PlusPoles
12
+
13
+
14
+
15
+
16
+
17
+ # Output(:signal, :semantic) => :color
18
+ # add / merge
19
+ # change existing, => color
20
+ #
21
+ #
22
+ # Produces [ PlusPole, PlusPole, ] via `to_a`.
23
+ #
24
+ # @privat
25
+ # @note This is private until we know what we want.
26
+ class PlusPoles
27
+ def initialize(plus_poles={})
28
+ @plus_poles = plus_poles.freeze
29
+ end
30
+
31
+ # merge( Activity::Magnetic.Output(Right, :success) => :success
32
+ def merge(output_to_color)
33
+ overrides = ::Hash[ output_to_color.collect { |output, color| [ output.semantic, [output, color] ] } ]
34
+ PlusPoles.new(@plus_poles.merge(overrides))
35
+ end
36
+
37
+ def reverse_merge(output_to_color)
38
+ existing_colors = @plus_poles.values.collect { |pole_cfg| pole_cfg.last }
39
+
40
+ # filter all outputs with a color that already exists.
41
+ overrides = output_to_color.find_all { |output, color| !existing_colors.include?(color) }
42
+ merge(overrides)
43
+ end
44
+
45
+ def reconnect(semantic_to_color)
46
+ ary = semantic_to_color.collect do |semantic, color|
47
+ existing_output, _ = @plus_poles[semantic]
48
+
49
+ next unless existing_output
50
+
51
+ [ Activity.Output(existing_output.signal, existing_output.semantic), color ]
52
+ end
53
+
54
+ merge( ::Hash[ary.compact] )
55
+ end
56
+
57
+ # Compile one {PlusPoles} instance from all a sequence of {Polarization}s.
58
+ # This is usually called once per `step` DSL call.
59
+ #
60
+ # @api private
61
+ def self.apply_polarizations(polarizations, magnetic_to, plus_poles, options)
62
+ magnetic_to, plus_poles = polarizations.inject([magnetic_to, plus_poles]) do |args, pol|
63
+ magnetic_to, plus_poles = pol.(*args, options)
64
+ end
65
+
66
+ return magnetic_to, plus_poles.to_a
67
+ end
68
+
69
+ # The DSL is a series of transformations that yield in tasks with several PlusPole instances each.
70
+ #
71
+ # @api private
72
+ def to_a
73
+ @plus_poles.values.collect { |output, color| PlusPole.new(output, color) }
74
+ end
75
+
76
+ # FIXME: should this be a hash or whatever?
77
+ #
78
+ # @return Hash All {Output}s mapped to their (guessed) semantic: `{ Output(Right, :success) => :success }`
79
+ def self.initial(outputs)
80
+ new.merge(Hash[ outputs.collect { |semantic, output| [output, semantic] } ])
81
+ end
82
+ end
83
+ end
84
+ end
85
+