trailblazer-future 2.1.0.rc1

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 (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,101 @@
1
+ module Trailblazer::V2_1
2
+ module Activity::Magnetic
3
+
4
+ # TODO: move?
5
+ module Options
6
+ # Produce two hashes, one "local" options with DSL-specific options such as `:fast_track`,
7
+ # one with generic DSL options, for example tuples like `Right=>Output(:failure)`.
8
+ def self.normalize(options, local_keys)
9
+ local, foreign = {}, {}
10
+ options.each { |k,v| local_keys.include?(k) ? local[k] = v : foreign[k] = v }
11
+
12
+ return foreign, local
13
+ end
14
+ end
15
+
16
+ # Called from Path/Railway/FastTrack, creates the specific {Builder} instance.
17
+ def self.Builder(implementation, normalizer, builder_options={})
18
+ builder = implementation.new(normalizer, builder_options.freeze).freeze # e.g. Path.new(...)
19
+
20
+ return builder, implementation.InitialAdds(builder_options)
21
+ end
22
+
23
+ class Builder
24
+ def initialize(normalizer, builder_options)
25
+ @normalizer, @builder_options = normalizer, builder_options
26
+ end
27
+
28
+ def self.merge(activity_adds, merged_adds)
29
+ merged_adds = merged_adds[2..-1] || []
30
+
31
+ activity_adds + merged_adds
32
+ end
33
+
34
+ # DSL method to create a Path within an activity which is embedded.
35
+ #
36
+ # Output(:success) => Path() {}
37
+ def Path(*args)
38
+ Activity::DSL.Path(@normalizer, *args)
39
+ end
40
+
41
+ # Public top-level entry point.
42
+ def insert(strategy, polarizer, task, options, &block)
43
+ normalizer = options[:normalizer] || @normalizer # DISCUSS: do this at a deeper point?
44
+
45
+ task, local_options, connection_options, sequence_options, extension_options = normalizer.(task, options)
46
+
47
+ polarizations = strategy.send(polarizer, @builder_options) # Railway.StepPolarizations( @builder_options )
48
+
49
+ insert_element( polarizations, task, local_options, connection_options, sequence_options, extension_options, &block )
50
+ end
51
+
52
+ private
53
+
54
+ # Internal top-level entry point to add task(s) and connections.
55
+ # High level interface for DSL calls like ::task or ::step.
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
+ end
59
+
60
+ # @return Adds
61
+ def self.adds_for(polarizations, task, local_options, connection_options, sequence_options, extension_options, &block)
62
+ # go through all wiring options such as Output()=>:color.
63
+ polarizations_from_user_options, additional_adds = process_dsl_options(connection_options, local_options, &block)
64
+
65
+ polarizations = polarizations + polarizations_from_user_options
66
+
67
+ result = adds(task, polarizations, local_options, sequence_options, local_options)
68
+
69
+ return result + (local_options[:adds] || []) + additional_adds, task, local_options, connection_options, sequence_options, extension_options
70
+ end
71
+
72
+ def self.process_dsl_options(options, id:nil, plus_poles:, **, &block)
73
+ DSL::ProcessOptions.(id, options, plus_poles, &block)
74
+ end
75
+
76
+ # Low-level interface for DSL calls (e.g. Start, where "you know what you're doing")
77
+ # @private
78
+ def self.adds(task, polarizations, options, sequence_options, magnetic_to:nil, id:nil, plus_poles:, **) # DISCUSS: no :id ?
79
+ magnetic_to, plus_poles = PlusPoles.apply_polarizations(
80
+ polarizations,
81
+ magnetic_to,
82
+ plus_poles,
83
+ options
84
+ )
85
+
86
+ Add( id, task, magnetic_to, plus_poles,
87
+ options, #{ fast_track: true },
88
+ sequence_options #{ group: :main }
89
+ )
90
+ end
91
+
92
+ def self.Add(id, task, magnetic_to, plus_poles, options, sequence_options)
93
+ [
94
+ [ :add, [id, [ magnetic_to, task, plus_poles ], sequence_options] ],
95
+ ]
96
+ end
97
+ end # Builder
98
+ end
99
+ end
100
+
101
+
@@ -0,0 +1,26 @@
1
+ module Trailblazer::V2_1
2
+ module Activity::Magnetic
3
+ # This normalizer only processes basic input and is meant for bootstrapping.
4
+ #
5
+ # task Callable, id: "success", before: "another"
6
+ class DefaultNormalizer
7
+ # Declarative::Variables
8
+ def self.build(plus_poles:, **options)
9
+ return new(plus_poles: plus_poles), options
10
+ end
11
+
12
+ def initialize(**default_options)
13
+ @default_options = default_options
14
+ end
15
+
16
+ # Processes the user arguments from the DSL
17
+ def call(task, options)
18
+ local_options = @default_options.merge(options) # here, we merge default :plus_poles.
19
+
20
+ local_options, sequence_options = Options.normalize( local_options, Activity::Schema::Dependencies.sequence_keywords )
21
+
22
+ return task, local_options, {}, sequence_options, {}
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,118 @@
1
+ # TODO: move me to Operation.
2
+ module Trailblazer::V2_1
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.StepPolarizations(**options)
16
+ [
17
+ *Railway.StepPolarizations(options),
18
+ StepPolarization.new(options)
19
+ ]
20
+ end
21
+
22
+ def self.FailPolarizations(**options)
23
+ [
24
+ *Railway.FailPolarizations(options),
25
+ FailPolarization.new(options)
26
+ ]
27
+ end
28
+
29
+ def self.PassPolarizations(**options)
30
+ [
31
+ *Railway.PassPolarizations(options),
32
+ PassPolarization.new(options)
33
+ ]
34
+ end
35
+
36
+ # pass_fast: true simply means: color my :success Output with :pass_fast color
37
+ class StepPolarization < Railway::StepPolarization
38
+ def call(magnetic_to, plus_poles, options)
39
+ plus_poles = plus_poles.reconnect( :success => :pass_fast ) if options[:pass_fast]
40
+ plus_poles = plus_poles.reconnect( :failure => :fail_fast ) if options[:fail_fast]
41
+
42
+ # add fast track outputs if :fast_track
43
+ plus_poles = plus_poles.reverse_merge(
44
+ Activity.Output(Activity::FastTrack::FailFast, :fail_fast) => :fail_fast,
45
+ Activity.Output(Activity::FastTrack::PassFast, :pass_fast) => :pass_fast
46
+ ) if options[:fast_track]
47
+
48
+ [
49
+ magnetic_to,
50
+ plus_poles
51
+ ]
52
+ end
53
+ end
54
+
55
+ class FailPolarization < Railway::StepPolarization
56
+ def call(magnetic_to, plus_poles, options)
57
+ plus_poles = plus_poles.reconnect( :failure => :fail_fast, :success => :fail_fast ) if options[:fail_fast]
58
+ plus_poles = plus_poles.reverse_merge( Activity.Output(Activity::FastTrack::FailFast, :fail_fast) => :fail_fast, Activity.Output(Activity::FastTrack::PassFast, :pass_fast) => :pass_fast ) if options[:fast_track]
59
+
60
+ [
61
+ magnetic_to,
62
+ plus_poles
63
+ ]
64
+ end
65
+ end
66
+
67
+ class PassPolarization < Railway::StepPolarization
68
+ def call(magnetic_to, plus_poles, options)
69
+ plus_poles = plus_poles.reconnect( :success => :pass_fast, :failure => :pass_fast ) if options[:pass_fast]
70
+ plus_poles = plus_poles.reverse_merge( Activity.Output(Activity::FastTrack::FailFast, :fail_fast) => :fail_fast, Activity.Output(Activity::FastTrack::PassFast, :pass_fast) => :pass_fast ) if options[:fast_track]
71
+
72
+ [
73
+ magnetic_to,
74
+ plus_poles
75
+ ]
76
+ end
77
+ end
78
+
79
+ def self.default_plus_poles(*args)
80
+ Railway.default_plus_poles(*args)
81
+ end
82
+
83
+ # Adds the End.fail_fast and End.pass_fast end to the Railway sequence.
84
+ def self.InitialAdds(pass_fast_end: Activity.End(:pass_fast), fail_fast_end: Activity.End(:fail_fast), **builder_options)
85
+ path_adds = Railway.InitialAdds(**builder_options)
86
+
87
+ ends =
88
+ adds(
89
+ pass_fast_end,
90
+
91
+ Path::EndEventPolarizations(builder_options),
92
+
93
+ {},
94
+ { group: :end },
95
+
96
+ id: "End.pass_fast",
97
+ magnetic_to: [:pass_fast],
98
+ plus_poles: {},
99
+ )+
100
+ adds(
101
+ fail_fast_end,
102
+
103
+ Path::EndEventPolarizations(builder_options),
104
+
105
+ {},
106
+ { group: :end },
107
+
108
+ magnetic_to: [:fail_fast],
109
+ id: "End.fail_fast",
110
+ plus_poles: {},
111
+ )
112
+
113
+ path_adds + ends
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,113 @@
1
+ module Trailblazer::V2_1
2
+ module Activity::Magnetic
3
+ # One {Normalizer} instance is called for every DSL call (step/pass/fail etc.) and normalizes/defaults
4
+ # the user options, such as setting `:id`, connecting the task's outputs or wrapping the user's
5
+ # task via {TaskBuilder::Binary} in order to translate true/false to `Right` or `Left`.
6
+ #
7
+ # The Normalizer sits in the `@builder`, which receives all DSL calls from the Operation subclass.
8
+ class Normalizer
9
+ def self.build(task_builder: Activity::TaskBuilder.method(:Binary), default_outputs: Builder::Path.default_outputs, pipeline: Pipeline, extension:[], **options)
10
+ return new(
11
+ default_outputs: default_outputs,
12
+ extension: extension,
13
+ task_builder: task_builder,
14
+ pipeline: pipeline,
15
+ ), options
16
+ end
17
+
18
+ def initialize(task_builder:, default_outputs:, pipeline:, **options)
19
+ @task_builder = task_builder
20
+ @default_outputs = default_outputs
21
+ @pipeline = pipeline # TODO: test me.
22
+ freeze
23
+ end
24
+
25
+ def call(task, options)
26
+ ctx = {
27
+ task: task,
28
+ options: options,
29
+ task_builder: @task_builder,
30
+ default_outputs: @default_outputs,
31
+ }
32
+
33
+ signal, (ctx, ) = @pipeline.( [ctx], {} )
34
+
35
+ return ctx[:options][:task], ctx[:local_options], ctx[:connection_options], ctx[:sequence_options], ctx[:extension_options]
36
+ end
37
+
38
+ # needs the basic Normalizer
39
+
40
+ # :default_plus_poles is an injectable option.
41
+ module Pipeline
42
+ extend Trailblazer::V2_1::Activity::Path( normalizer_class: DefaultNormalizer, plus_poles: PlusPoles.new.merge( Builder::Path.default_outputs.values ) ) # FIXME: the DefaultNormalizer actually doesn't need Left.
43
+
44
+ def self.split_options( ctx, task:, options:, ** )
45
+ keywords = extract_dsl_keywords(options)
46
+ extensions = extract_extensions(options)
47
+
48
+ # sort through the "original" user DSL options.
49
+ options, extension_options = Options.normalize( options, extensions ) # DISCUSS:
50
+ options, local_options = Options.normalize( options, keywords ) # DISCUSS:
51
+ local_options, sequence_options = Options.normalize( local_options, Activity::Schema::Dependencies.sequence_keywords )
52
+
53
+ ctx[:local_options],
54
+ ctx[:connection_options],
55
+ ctx[:sequence_options],
56
+ ctx[:extension_options] = local_options, options, sequence_options, extension_options
57
+ end
58
+
59
+ # Filter out connections, e.g. `Output(:fail_fast) => :success` and return only the keywords like `:id` or `:replace`.
60
+ def self.extract_dsl_keywords(options, connection_classes = [Activity::Output, Activity::DSL::OutputSemantic])
61
+ options.keys - options.keys.find_all { |k| connection_classes.include?( k.class ) }
62
+ end
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
+
68
+ # FIXME; why don't we use the extensions passed into the initializer?
69
+ def self.initialize_extension_option( ctx, options:, ** )
70
+ ctx[:options] = options.merge( Activity::DSL::Extension.new( Activity::DSL.method(:record) ) => true )
71
+ end
72
+
73
+ # Normalizes ctx[:options] (the user-options via the DSL) into the internal Hash format.
74
+ def self.normalize_for_macro( ctx, task:, options:, task_builder:, ** )
75
+ ctx[:options] =
76
+ if task.is_a?(::Hash) # macro.
77
+ task.merge(options) # Note that the user options are merged over the macro options.
78
+ else # user step
79
+ { id: task }
80
+ .merge(options) # default :id
81
+ .merge( task: task_builder.(task) )
82
+ end
83
+ end
84
+
85
+ # :outputs passed: I know what I want to have connected.
86
+ # no :outputs: use default_outputs
87
+ # ALWAYS connect all outputs to their semantic-color.
88
+
89
+ # Create the `plus_poles: <PlusPoles>` tuple where the PlusPoles instance will act as the interface
90
+ # to rewire or add connections for the DSL.
91
+ def self.initialize_plus_poles( ctx, local_options:, default_outputs:, ** )
92
+ outputs = local_options[:outputs] || default_outputs
93
+
94
+ ctx[:local_options] =
95
+ {
96
+ plus_poles: PlusPoles.initial(outputs),
97
+ }
98
+ .merge(local_options)
99
+ end
100
+
101
+ task Activity::TaskBuilder::Binary( method(:initialize_extension_option) ), id: "initialize_extension_option"
102
+ task Activity::TaskBuilder::Binary( method(:normalize_for_macro) ), id: "normalize_for_macro"
103
+
104
+ task Activity::TaskBuilder::Binary( Activity::TaskWrap::VariableMapping.method(:normalizer_step_for_input_output) )
105
+
106
+ task Activity::TaskBuilder::Binary( method(:split_options) ), id: "split_options"
107
+ task Activity::TaskBuilder::Binary( method(:initialize_plus_poles) ), id: "initialize_plus_poles"
108
+ # task ->((ctx, _), **) { pp ctx; [Activity::Right, [ctx, _]] }
109
+ end
110
+ end # Normalizer
111
+
112
+ end
113
+ end
@@ -0,0 +1,105 @@
1
+ module Trailblazer::V2_1
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
+ # In most cases, a task has a binary signal, which is why we decided to make that
18
+ # the default output set.
19
+ def self.default_outputs
20
+ {
21
+ :success => Activity.Output(Activity::Right, :success),
22
+ :failure => Activity.Output(Activity::Left, :failure)
23
+ }
24
+ end
25
+
26
+ # @return [Adds] list of Adds instances that can be chained or added to an existing sequence.
27
+ def self.InitialAdds(**options)
28
+ StartAdds(**options) + EndAdds(**options)
29
+ end
30
+
31
+ # TODO: make this nicer.
32
+ def self.StartAdds(track_color:, end_semantic:, start_outputs: {success: self.default_outputs[:success]}, **)
33
+ builder_options={ track_color: track_color, end_semantic: end_semantic }
34
+
35
+ adds(
36
+ Activity::Start.new(semantic: :default),
37
+
38
+ TaskPolarizations(builder_options),
39
+
40
+ {}, { group: :start },
41
+
42
+ id: "Start.default",
43
+ magnetic_to: [],
44
+ plus_poles: PlusPoles.initial(start_outputs), # FIXME: this is actually redundant with Normalizer
45
+ )
46
+ end
47
+
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 }
58
+
59
+ adds(
60
+ track_end,
61
+
62
+ EndEventPolarizations(builder_options), # only sets :magnetic_to.
63
+
64
+ {}, { group: :end },
65
+
66
+ id: "End.#{track_color}",
67
+ plus_poles: {},
68
+ magnetic_to: nil,
69
+ )
70
+ end
71
+ end
72
+
73
+ def self.TaskPolarizations(track_color:, **)
74
+ [TaskPolarization.new( track_color: track_color )]
75
+ end
76
+
77
+ def self.EndEventPolarizations(track_color:, **)
78
+ [EndEventPolarization.new( track_color: track_color )]
79
+ end
80
+
81
+ class TaskPolarization
82
+ def initialize(track_color:)
83
+ @track_color = track_color
84
+ end
85
+
86
+ def call(magnetic_to, plus_poles, options)
87
+ [
88
+ magnetic_to || [@track_color],
89
+ plus_poles.reconnect( :success => @track_color )
90
+ ]
91
+ end
92
+ end # TaskPolarization
93
+
94
+ class EndEventPolarization < TaskPolarization
95
+ def call(magnetic_to, plus_poles, options)
96
+ [
97
+ magnetic_to || [@track_color],
98
+ {}
99
+ ]
100
+ end
101
+ end # EndEventPolarization
102
+ end # Path
103
+ end # Builder
104
+ end
105
+ end