Module: Trailblazer::Activity::DSL::Linear::Normalizer
- Defined in:
- lib/trailblazer/activity/dsl/linear/normalizer.rb,
lib/trailblazer/activity/dsl/linear/normalizer/inherit.rb,
lib/trailblazer/activity/dsl/linear/normalizer/terminus.rb,
lib/trailblazer/activity/dsl/linear/normalizer/extensions.rb,
lib/trailblazer/activity/dsl/linear/normalizer/output_tuples.rb
Overview
Normalizers are linear activities that process and normalize the options from a specific DSL call, such as ‘#step` or `#pass`. All defaulting should happen through the normalizer. An invoked normalizer produces an options hash that has to contain an [:adds] key with a ADDS structure usable for Sequence.apply_adds.
They’re usually invoked from Strategy#invoke_normalizer_for!, which is called from Path#step, Railway#pass, etc.
Most parts of Normalizer are documented: trailblazer.to/2.1/docs/internals.html#internals-dsl-normalizer
Defined Under Namespace
Modules: Extensions, Inherit, OutputTuples, Terminus Classes: Normalizers
Class Method Summary collapse
- .clone_duplicate_activity(ctx, task:, sequence:) ⇒ Object
-
.compile_data(ctx, non_symbol_options:, default_variables_for_data: [:id, :dsl_track, :extensions]) ⇒ Object
TODO: document DataVariable() => :name Compile data that goes into the sequence row.
- .create_add(ctx, row:, sequence_insert:) ⇒ Object
- .create_adds(ctx, add:, adds:) ⇒ Object
- .create_row(ctx, task:, wirings:, magnetic_to:, data:) ⇒ Object
-
.extend!(activity_class, *step_methods) ⇒ Object
Extend a particular normalizer with new steps and save it on the activity.
-
.id_with_inherit_and_replace(ctx, id: nil, replace: nil, inherit: nil) ⇒ Object
Whenever :replace and :inherit are passed, automatically assign :id.
-
.macro_options_with_symbol_task(ctx, options:) ⇒ Object
TODO: remove this! it doesn’t receive correct ciruit_options.
-
.merge_library_options(ctx, options:, library_options:) ⇒ Object
:library_options such as :sequence, :dsl_track, etc.
-
.merge_normalizer_options(ctx, normalizer_options:, options:) ⇒ Object
:normalizer_options such as :track_name get overridden by user/macro.
-
.merge_user_options(ctx, options:, user_options:) ⇒ Object
make ctx the actual ctx.
- .normalize_context(ctx, flow_options) ⇒ Object
- .normalize_duplications(ctx, replace: false) ⇒ Object
- .normalize_id(ctx, task:, id: false) ⇒ Object
-
.normalize_non_symbol_options(ctx, options:) ⇒ Object
Move DSL user options such as => Track(:found) to a new key :non_symbol_options.
-
.normalize_override(ctx, id:, override: false) ⇒ Object
TODO: remove #normalize_override in 1.2.0 (Used in macro-contract tests).
-
.normalize_sequence_insert(ctx, end_id:) ⇒ Object
Processes :before,:after,:replace,:delete options and defaults to “End.success” which, yeah.
-
.normalize_step_interface(ctx, options:) ⇒ Object
After this step, options is always a hash.
-
.Normalizer(prepend_to_default_outputs: []) ⇒ Object
The generic normalizer not tied to ‘step` or friends.
-
.prepend_to(pipe, insertion_id, insertion) ⇒ Object
Helper for normalizers.
- .raise_on_duplicate_id(ctx, id:, sequence:) ⇒ Object
-
.replace(pipe, insertion_id, id, task) ⇒ Object
Helper for normalizers.
- .sequence_insert_options ⇒ Object
-
.Task(user_step) ⇒ Object
Wrap user’s normalizer task in a Pipeline::TaskAdapter so it executes with convenient kw args.
- .wrap_task_with_step_interface(ctx, step_interface_builder:, task:, wrap_task: false) ⇒ Object
Class Method Details
.clone_duplicate_activity(ctx, task:, sequence:) ⇒ Object
260 261 262 263 264 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 260 def clone_duplicate_activity(ctx, task:, sequence:, **) return unless task.is_a?(Class) ctx[:task] = task.clone if sequence.find { |row| row[1] == task } end |
.compile_data(ctx, non_symbol_options:, default_variables_for_data: [:id, :dsl_track, :extensions]) ⇒ Object
TODO: document DataVariable() => :name Compile data that goes into the sequence row.
301 302 303 304 305 306 307 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 301 def compile_data(ctx, non_symbol_options:, default_variables_for_data: [:id, :dsl_track, :extensions], **) variables_for_data = .find_all { |k, v| k.instance_of?(Linear::DataVariableName) } .flat_map { |k, v| Array(v) } ctx[:data] = (default_variables_for_data + variables_for_data).collect { |key| [key, ctx[key]] }.to_h end |
.create_add(ctx, row:, sequence_insert:) ⇒ Object
291 292 293 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 291 def create_add(ctx, row:, sequence_insert:, **) ctx[:add] = {row: row, insert: sequence_insert} end |
.create_adds(ctx, add:, adds:) ⇒ Object
295 296 297 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 295 def create_adds(ctx, add:, adds:, **) ctx[:adds] = [add] + adds end |
.create_row(ctx, task:, wirings:, magnetic_to:, data:) ⇒ Object
287 288 289 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 287 def create_row(ctx, task:, wirings:, magnetic_to:, data:, **) ctx[:row] = Sequence.create_row(task: task, magnetic_to: magnetic_to, wirings: wirings, **data) end |
.extend!(activity_class, *step_methods) ⇒ Object
Extend a particular normalizer with new steps and save it on the activity.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 52 def self.extend!(activity_class, *step_methods) activity_class.instance_variable_get(:@state).update!(:normalizers) do |normalizers| hsh = normalizers.instance_variable_get(:@normalizers) # TODO: introduce {#to_h}. new_normalizers = # {step: #<..>, pass: #<..>} step_methods.collect do |name| extended_normalizer = hsh.fetch(name) # grab existing normalizer. new_normalizer = yield(extended_normalizer) # and let the user block change it. [name, new_normalizer] end.to_h Normalizers.new(**hsh.merge(new_normalizers)) end end |
.id_with_inherit_and_replace(ctx, id: nil, replace: nil, inherit: nil) ⇒ Object
Whenever :replace and :inherit are passed, automatically assign :id. DISCUSS: this step could be nested in inherit_option.
279 280 281 282 283 284 285 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 279 def id_with_inherit_and_replace(ctx, id: nil, replace: nil, inherit: nil, **) return if id return unless inherit # inherit: true and inherit: [] both work. return unless replace ctx[:id] = replace end |
.macro_options_with_symbol_task(ctx, options:) ⇒ Object
TODO: remove this! it doesn’t receive correct ciruit_options. DISCUSS: should we remove this special case? This handles
step task: :instance_method_exposing_circuit_interface
152 153 154 155 156 157 158 159 160 161 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 152 def (ctx, options:, **) return if [:wrap_task] return unless [:task].is_a?(Symbol) ctx[:options] = { **, wrap_task: true, step_interface_builder: ->(task) { Trailblazer::Option(task) } # only wrap in Option, not {TaskAdapter}. } end |
.merge_library_options(ctx, options:, library_options:) ⇒ Object
:library_options such as :sequence, :dsl_track, etc.
204 205 206 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 204 def (ctx, options:, library_options:, **) ctx[:options] = .merge() end |
.merge_normalizer_options(ctx, normalizer_options:, options:) ⇒ Object
:normalizer_options such as :track_name get overridden by user/macro.
215 216 217 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 215 def (ctx, normalizer_options:, options:, **) ctx[:options] = .merge() end |
.merge_user_options(ctx, options:, user_options:) ⇒ Object
make ctx the actual ctx
209 210 211 212 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 209 def (ctx, options:, user_options:, **) # {options} are either a <#task> or {} from macro ctx[:options] = .merge() # Note that the user options are merged over the macro options. end |
.normalize_context(ctx, flow_options) ⇒ Object
219 220 221 222 223 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 219 def normalize_context(ctx, ) ctx = ctx[:options] return ctx, end |
.normalize_duplications(ctx, replace: false) ⇒ Object
247 248 249 250 251 252 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 247 def normalize_duplications(ctx, replace: false, **) return if replace raise_on_duplicate_id(ctx, **ctx) clone_duplicate_activity(ctx, **ctx) # DISCUSS: mutates {ctx}. end |
.normalize_id(ctx, task:, id: false) ⇒ Object
188 189 190 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 188 def normalize_id(ctx, task:, id: false, **) ctx[:id] = id || task end |
.normalize_non_symbol_options(ctx, options:) ⇒ Object
Move DSL user options such as => Track(:found) to a new key :non_symbol_options. This allows using options as a **ctx-able hash in Ruby 2.6 and 3.0.
269 270 271 272 273 274 275 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 269 def (ctx, options:, **) = .find_all { |k, v| k.is_a?(Symbol) }.to_h = .slice(*(.keys - .keys)) # raise unless (symbol_options.size+non_symbol_options.size) == options.size ctx[:options] = .merge(non_symbol_options: ) end |
.normalize_override(ctx, id:, override: false) ⇒ Object
TODO: remove #normalize_override in 1.2.0 (Used in macro-contract tests). :override really only makes sense for Macro(), {override: true} where the user_options dictate the overriding.
195 196 197 198 199 200 201 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 195 def normalize_override(ctx, id:, override: false, **) return unless override Activity::Deprecate.warn Linear::Deprecate.dsl_caller_location, "The :override option is deprecated and will be removed. Please use :replace instead." ctx[:replace] = (id || raise) end |
.normalize_sequence_insert(ctx, end_id:) ⇒ Object
Processes :before,:after,:replace,:delete options and defaults to “End.success” which, yeah.
227 228 229 230 231 232 233 234 235 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 227 def normalize_sequence_insert(ctx, end_id:, **) insertion = ctx.keys & .keys insertion = insertion[0] || :before target = ctx[insertion] || end_id insertion_method = [insertion] ctx[:sequence_insert] = [Activity::Adds::Insert.method(insertion_method), target] end |
.normalize_step_interface(ctx, options:) ⇒ Object
After this step, options is always a hash.
Specific to the “step DSL”: if the first argument is a callable, wrap it in a step_interface_builder since its interface expects the step interface, but the circuit will call it with circuit interface.
168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 168 def normalize_step_interface(ctx, options:, **) return if .is_a?(Hash) # Step Interface # step :find, ... # step Callable, ... (Method, Proc etc) ctx[:options] = { task: , wrap_task: true # task exposes step interface. } end |
.Normalizer(prepend_to_default_outputs: []) ⇒ Object
The generic normalizer not tied to ‘step` or friends.
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 80 def Normalizer(prepend_to_default_outputs: []) # Adding steps to the output pipeline means they are only called when there # are no :outputs set already. outputs_pipeline = TaskWrap::Pipeline.new([]) prepend_to_default_outputs.each do |hsh| outputs_pipeline = Linear::Normalizer.prepend_to(outputs_pipeline, nil, hsh) # DISCUSS: does it matter if we prepend FastTrack to Railway, etc? end # Call the prepend_to_outputs pipeline only if {:outputs} is not set (by Subprocess). # too bad we don't have nesting here, yet. defaults_for_outputs = ->(ctx, args) do return [ctx, args] if ctx.key?(:outputs) outputs_pipeline.(ctx, args) end TaskWrap::Pipeline.new( { "activity.normalize_step_interface" => Normalizer.Task(method(:normalize_step_interface)), # Makes sure {:options} is always a hash. "activity.macro_options_with_symbol_task" => Normalizer.Task(method(:macro_options_with_symbol_task)), # DISCUSS: we might deprecate {task: :instance_method} "activity.merge_library_options" => Normalizer.Task(method(:merge_library_options)), # Merge "macro"/user options over library options. "activity.normalize_for_macro" => Normalizer.Task(method(:merge_user_options)), # Merge user_options over "macro" options. "activity.normalize_normalizer_options" => Normalizer.Task(method(:merge_normalizer_options)), # Merge user_options over normalizer_options. "activity.normalize_non_symbol_options" => Normalizer.Task(method(:normalize_non_symbol_options)), "activity.path_helper.forward_block" => Normalizer.Task(Helper::Path::Normalizer.method(:forward_block_for_path_branch)), # forward the "global" block "activity.normalize_context" => method(:normalize_context), "activity.id_with_inherit_and_replace" => Normalizer.Task(method(:id_with_inherit_and_replace)), "activity.normalize_id" => Normalizer.Task(method(:normalize_id)), "activity.normalize_override" => Normalizer.Task(method(:normalize_override)), # TODO: remove! "activity.wrap_task_with_step_interface" => Normalizer.Task(method(:wrap_task_with_step_interface)), # Nested pipeline: "activity.default_outputs" => defaults_for_outputs, # only {if :outputs.nil?} "extensions.convert_extensions_option_to_tuples" => Normalizer.Task(Extensions.method(:convert_extensions_option_to_tuples)), "inherit.recall_recorded_options" => Normalizer.Task(Inherit.method(:recall_recorded_options)), "activity.sequence_insert" => Normalizer.Task(method(:normalize_sequence_insert)), "activity.normalize_duplications" => Normalizer.Task(method(:normalize_duplications)), "activity.path_helper.path_to_track" => Normalizer.Task(Helper::Path::Normalizer.method(:convert_paths_to_tracks)), "output_tuples.normalize_output_tuples" => Normalizer.Task(OutputTuples.method(:normalize_output_tuples)), # Output(Signal, :semantic) => Id() "output_tuples.remember_custom_output_tuples" => Normalizer.Task(OutputTuples.method(:remember_custom_output_tuples)), # Output(Signal, :semantic) => Id() "output_tuples.register_additional_outputs" => Normalizer.Task(OutputTuples.method(:register_additional_outputs)), # Output(Signal, :semantic) => Id() "output_tuples.filter_inherited_output_tuples" => Normalizer.Task(OutputTuples.method(:filter_inherited_output_tuples)), "activity.wirings" => Normalizer.Task(OutputTuples::Connections.method(:compile_wirings)), "extensions.compile_extensions" => Normalizer.Task(Extensions.method(:compile_extensions)), "extensions.compile_recorded_extensions" => Normalizer.Task(Extensions.method(:compile_recorded_extensions)), # DISCUSS: make this configurable? maybe lots of folks don't want {:inherit}? "inherit.compile_recorded_options" => Normalizer.Task(Inherit.method(:compile_recorded_options)), # TODO: make this a "Subprocess": "activity.compile_data" => Normalizer.Task(method(:compile_data)), "activity.create_row" => Normalizer.Task(method(:create_row)), "activity.create_add" => Normalizer.Task(method(:create_add)), "activity.create_adds" => Normalizer.Task(method(:create_adds)), } .collect { |id, task| TaskWrap::Pipeline.Row(id, task) } ) end |
.prepend_to(pipe, insertion_id, insertion) ⇒ Object
Helper for normalizers. To be applied on Pipeline instances.
34 35 36 37 38 39 40 41 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 34 def self.prepend_to(pipe, insertion_id, insertion) adds = insertion.collect do |id, task| {insert: [Adds::Insert.method(:Prepend), insertion_id], row: Activity::TaskWrap::Pipeline.Row(id, task)} end Adds.apply_adds(pipe, insertion_id ? adds : adds.reverse) end |
.raise_on_duplicate_id(ctx, id:, sequence:) ⇒ Object
255 256 257 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 255 def raise_on_duplicate_id(ctx, id:, sequence:, **) raise "ID #{id} is already taken. Please specify an `:id`." if sequence.find { |row| row.id == id } end |
.replace(pipe, insertion_id, id, task) ⇒ Object
Helper for normalizers.
44 45 46 47 48 49 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 44 def self.replace(pipe, insertion_id, (id, task)) Adds.apply_adds( pipe, [{insert: [Adds::Insert.method(:Replace), insertion_id], row: Activity::TaskWrap::Pipeline.Row(id, task)}] ) end |
.sequence_insert_options ⇒ Object
238 239 240 241 242 243 244 245 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 238 def { before: :Prepend, after: :Append, replace: :Replace, delete: :Delete } end |
.Task(user_step) ⇒ Object
Wrap user’s normalizer task in a Pipeline::TaskAdapter so it executes with convenient kw args.
Example
normalizer_task = Normalizer.Task(method(:normalize_id))
# will call {normalizer_task} and pass ctx variables as kwargs, as follows
def normalize_id(ctx, id: false, task:, **)
75 76 77 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 75 def Task(user_step) Activity::TaskWrap::Pipeline::TaskAdapter.for_step(user_step, option: false) # we don't need Option as we don't have ciruit_options here, and no {:exec_context} end |
.wrap_task_with_step_interface(ctx, step_interface_builder:, task:, wrap_task: false) ⇒ Object
182 183 184 185 186 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 182 def wrap_task_with_step_interface(ctx, step_interface_builder:, task:, wrap_task: false, **) return unless wrap_task ctx[:task] = step_interface_builder.(task) end |