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
DISCUSS: should we remove this special case? This handles step task: :instance_method_exposing_circuit_interface.
-
.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
259 260 261 262 263 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 259 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.
300 301 302 303 304 305 306 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 300 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
290 291 292 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 290 def create_add(ctx, row:, sequence_insert:, **) ctx[:add] = {row: row, insert: sequence_insert} end |
.create_adds(ctx, add:, adds:) ⇒ Object
294 295 296 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 294 def create_adds(ctx, add:, adds:, **) ctx[:adds] = [add] + adds end |
.create_row(ctx, task:, wirings:, magnetic_to:, data:) ⇒ Object
286 287 288 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 286 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.
278 279 280 281 282 283 284 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 278 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
DISCUSS: should we remove this special case? This handles
step task: :instance_method_exposing_circuit_interface
151 152 153 154 155 156 157 158 159 160 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 151 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.
203 204 205 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 203 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.
214 215 216 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 214 def (ctx, normalizer_options:, options:, **) ctx[:options] = .merge() end |
.merge_user_options(ctx, options:, user_options:) ⇒ Object
make ctx the actual ctx
208 209 210 211 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 208 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
218 219 220 221 222 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 218 def normalize_context(ctx, ) ctx = ctx[:options] return ctx, end |
.normalize_duplications(ctx, replace: false) ⇒ Object
246 247 248 249 250 251 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 246 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
187 188 189 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 187 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.
268 269 270 271 272 273 274 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 268 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.
194 195 196 197 198 199 200 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 194 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.
226 227 228 229 230 231 232 233 234 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 226 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.
167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 167 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
254 255 256 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 254 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
237 238 239 240 241 242 243 244 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 237 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
181 182 183 184 185 |
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 181 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 |