Module: Trailblazer::Activity::DSL::Linear::Normalizer

Defined in:
lib/trailblazer/activity/dsl/linear/normalizer.rb

Overview

Normalizers are linear activities that process and normalize the options from a DSL call. They’re usually invoked from Strategy#task_for, which is called from Path#step, Railway#pass, etc.

Class Method Summary collapse

Class Method Details

.activity_normalizer(sequence) ⇒ Object

activity_normalizer.([{options:, user_options:, normalizer_options: }])



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 11

def activity_normalizer(sequence)
  seq = Activity::Path::DSL.prepend_to_path( # this doesn't particularly put the steps after the Path steps.
    sequence,

    {
    "activity.normalize_step_interface"       => method(:normalize_step_interface),      # first
    "activity.normalize_for_macro"            => method(:merge_user_options),
    "activity.normalize_normalizer_options"   => method(:merge_normalizer_options),
    "activity.normalize_context"              => method(:normalize_context),
    "activity.normalize_id"                   => method(:normalize_id),
    "activity.normalize_override"             => method(:normalize_override),
    "activity.wrap_task_with_step_interface"  => method(:wrap_task_with_step_interface), # last
    "activity.inherit_option"               => method(:inherit_option),
    },

    Linear::Insert.method(:Append), "Start.default"
  )

  seq = Trailblazer::Activity::Path::DSL.prepend_to_path( # this doesn't particularly put the steps after the Path steps.
    seq,

    {
    "activity.normalize_outputs_from_dsl"     => method(:normalize_outputs_from_dsl),     # Output(Signal, :semantic) => Id()
    "activity.normalize_connections_from_dsl" => method(:normalize_connections_from_dsl),
    "activity.input_output_dsl"               => method(:input_output_dsl), # FIXME: make this optional and allow to dynamically change normalizer steps
    },

    Linear::Insert.method(:Prepend), "path.wirings"
  )

  seq = Trailblazer::Activity::Path::DSL.prepend_to_path( # this doesn't particularly put the steps after the Path steps.
    seq,

    {
    "activity.cleanup_options"     => method(:cleanup_options),
    },

    Linear::Insert.method(:Prepend), "End.success"
  )
# pp seq
  seq
end

.add_end(end_event, magnetic_to:, id:) ⇒ Object

#insert_task options to add another end.



196
197
198
199
200
201
202
203
204
205
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 196

def add_end(end_event, magnetic_to:, id:)

  options = Path::DSL.append_end_options(task: end_event, magnetic_to: magnetic_to, id: id)
  row     = Linear::Sequence.create_row(options)

  {
    row:    row,
    insert: row[3][:sequence_insert]
  }
end

.cleanup_options(ctx, flow_options) ⇒ Object

TODO: make this extendable!



257
258
259
260
261
262
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 257

def cleanup_options((ctx, flow_options), *)
  # new_ctx = ctx.reject { |k, v| [:connections, :outputs, :end_id, :step_interface_builder, :failure_end, :track_name, :sequence].include?(k) }
  new_ctx = ctx.reject { |k, v| [:outputs, :end_id, :step_interface_builder, :failure_end, :track_name, :sequence].include?(k) }

  return Trailblazer::Activity::Right, [new_ctx, flow_options]
end

.compile_wirings(ctx, flow_options) ⇒ Object

Compile the actual Seq::Row‘s wiring. This combines :connections and :outputs



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 131

def compile_wirings((ctx, flow_options), *)
  connections = ctx[:connections] || raise # FIXME
  outputs = ctx[:outputs] || raise # FIXME

  ctx[:wirings] =
    connections.collect do |semantic, (search_strategy_builder, *search_args)|
      output = outputs[semantic] || raise("No `#{semantic}` output found for #{ctx[:id].inspect} and outputs #{outputs.inspect}")

      search_strategy_builder.( # return proc to be called when compiling Seq, e.g. {ById(output, :id)}
        output,
        *search_args
      )
    end

  return Trailblazer::Activity::Right, [ctx, flow_options]
end

.inherit_option(ctx, flow_options) ⇒ Object

Currently, the :inherit option copies over :connections from the original step and merges them with the (prolly) connections passed from the user.



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 240

def inherit_option((ctx, flow_options), *)
  return Trailblazer::Activity::Right, [ctx, flow_options] unless ctx[:inherit]

  sequence = ctx[:sequence]
  id = ctx[:id]

  index = Linear::Insert.find_index(sequence, id)
  row   = sequence[index] # from this row we're inheriting options.

  extensions = (row[3][:extensions]||[]) + (ctx[:extensions]||[]) # FIXME: DEFAULTING, FOR FUCK'S SAKE

  ctx = ctx.merge(connections: row[3][:connections], extensions: extensions) # "inherit"

  return Trailblazer::Activity::Right, [ctx, flow_options]
end

.input_output_dsl(ctx, flow_options) ⇒ Object



226
227
228
229
230
231
232
233
234
235
236
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 226

def input_output_dsl((ctx, flow_options), *)
  config = ctx.select { |k,v| [:input, :output].include?(k) } # TODO: optimize this, we don't have to go through the entire hash.

  return Trailblazer::Activity::Right, [ctx, flow_options] if config.size == 0 # no :input/:output passed.

  new_ctx = {}
  new_ctx[:extensions] = ctx[:extensions] || [] # merge DSL extensions with I/O.
  new_ctx[:extensions] += [Linear.VariableMapping(**config)]

  return Trailblazer::Activity::Right, [ctx.merge(new_ctx), flow_options]
end

.merge_normalizer_options(ctx, flow_options) ⇒ Object

:normalizer_options such as :track_name get overridden by user/macro.



115
116
117
118
119
120
121
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 115

def merge_normalizer_options((ctx, flow_options), *)
  normalizer_options = ctx[:normalizer_options] # either a <#task> or {} from macro

  ctx = ctx.merge(options: normalizer_options.merge(ctx[:options])) #

  return Trailblazer::Activity::Right, [ctx, flow_options]
end

.merge_user_options(ctx, flow_options) ⇒ Object

make ctx the actual ctx



106
107
108
109
110
111
112
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 106

def merge_user_options((ctx, flow_options), *)
  options = ctx[:options] # either a <#task> or {} from macro

  ctx = ctx.merge(options: options.merge(ctx[:user_options])) # Note that the user options are merged over the macro options.

  return Trailblazer::Activity::Right, [ctx, flow_options]
end

.normalize_connections_from_dsl(ctx, flow_options) ⇒ Object

Process => target.



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 149

def normalize_connections_from_dsl((ctx, flow_options), *)
  new_ctx = ctx.reject { |output, cfg| output.kind_of?(Activity::DSL::Linear::OutputSemantic) }
  connections = new_ctx[:connections]
  adds        = new_ctx[:adds]

  # Find all {Output() => Track()/Id()/End()}
  (ctx.keys - new_ctx.keys).each do |output|
    cfg = ctx[output]

    new_connections, add =
      if cfg.is_a?(Activity::DSL::Linear::Track)
        [output_to_track(ctx, output, cfg), cfg.adds]
      elsif cfg.is_a?(Activity::DSL::Linear::Id)
        [output_to_id(ctx, output, cfg.value), []]
      elsif cfg.is_a?(Activity::End)
        _adds = []

        end_id     = Linear.end_id(cfg)
        end_exists = Insert.find_index(ctx[:sequence], end_id)

        _adds      = [add_end(cfg, magnetic_to: end_id, id: end_id)] unless end_exists

        [output_to_id(ctx, output, end_id), _adds]
      else
        raise cfg.inspect
      end

    connections = connections.merge(new_connections)
    adds += add
  end

  new_ctx = new_ctx.merge(connections: connections, adds: adds)

  return Trailblazer::Activity::Right, [new_ctx, flow_options]
end

.normalize_context(ctx, flow_options) ⇒ Object



123
124
125
126
127
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 123

def normalize_context((ctx, flow_options), *)
  ctx = ctx[:options]

  return Trailblazer::Activity::Right, [ctx, flow_options]
end

.normalize_id(ctx, flow_options) ⇒ Object



91
92
93
94
95
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 91

def normalize_id((ctx, flow_options), **)
  id = ctx[:id] || ctx[:task]

  return Trailblazer::Activity::Right, [ctx.merge(id: id), flow_options]
end

.normalize_outputs_from_dsl(ctx, flow_options) ⇒ Object

Output(Signal, :semantic) => Id()



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 208

def normalize_outputs_from_dsl((ctx, flow_options), *)
  new_ctx = ctx.reject { |output, cfg| output.kind_of?(Activity::Output) }

  outputs     = ctx[:outputs]
  dsl_options = {}

  (ctx.keys - new_ctx.keys).collect do |output|
    cfg      = ctx[output] # e.g. Track(:success)

    outputs = outputs.merge(output.semantic => output)
    dsl_options = dsl_options.merge(Linear.Output(output.semantic) => cfg)
  end

  new_ctx = new_ctx.merge(outputs: outputs).merge(dsl_options)

  return Trailblazer::Activity::Right, [new_ctx, flow_options]
end

.normalize_override(ctx, flow_options) ⇒ Object

:override really only makes sense for Macro(), {override: true} where the user_options dictate the overriding.



99
100
101
102
103
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 99

def normalize_override((ctx, flow_options), *)
  ctx = ctx.merge(replace: ctx[:id] || raise) if ctx[:override]

  return Trailblazer::Activity::Right, [ctx, flow_options]
end

.normalize_step_interface(ctx, flow_options) ⇒ Object

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.



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 56

def normalize_step_interface((ctx, flow_options), *)
  options = case ctx[:options]
            when Hash
              # Circuit Interface
              task  = ctx[:options].fetch(:task)
              id    = ctx[:options][:id]

              if task.is_a?(Symbol)
                # step task: :find, id: :load
                { **ctx[:options], id: (id || task), task: Trailblazer::Option( task ) }
              else
                # step task: Callable, ... (Subprocess, Proc, macros etc)
                ctx[:options] # NOOP
              end
            else
              # Step Interface
              # step :find, ...
              # step Callable, ... (Method, Proc etc)
              { task: ctx[:options], wrap_task: true }
            end

  return Trailblazer::Activity::Right, [ctx.merge(options: options), flow_options]
end

.output_to_id(ctx, output, target) ⇒ Object



191
192
193
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 191

def output_to_id(ctx, output, target)
  {output.value => [Linear::Search.method(:ById), target]}
end

.output_to_track(ctx, output, track) ⇒ Object



185
186
187
188
189
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 185

def output_to_track(ctx, output, track)
  search_strategy = track.options[:wrap_around] ? :WrapAround : :Forward

  {output.value => [Linear::Search.method(search_strategy), track.color]}
end

.wrap_task_with_step_interface(ctx, flow_options) ⇒ Object



80
81
82
83
84
85
86
87
88
89
# File 'lib/trailblazer/activity/dsl/linear/normalizer.rb', line 80

def wrap_task_with_step_interface((ctx, flow_options), **)
  return Trailblazer::Activity::Right, [ctx, flow_options] unless ctx[:wrap_task]

  step_interface_builder = ctx[:step_interface_builder] # FIXME: use kw!
  task                   = ctx[:task] # FIXME: use kw!

  wrapped_task = step_interface_builder.(task)

  return Trailblazer::Activity::Right, [ctx.merge(task: wrapped_task), flow_options]
end