trailblazer-activity-dsl-linear 1.0.0 → 1.2.0

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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -3
  3. data/CHANGES.md +100 -0
  4. data/Gemfile +7 -4
  5. data/Rakefile +1 -1
  6. data/lib/trailblazer/activity/dsl/linear/feature/merge.rb +2 -2
  7. data/lib/trailblazer/activity/dsl/linear/feature/patch.rb +9 -5
  8. data/lib/trailblazer/activity/dsl/linear/feature/variable_mapping/dsl.rb +241 -156
  9. data/lib/trailblazer/activity/dsl/linear/feature/variable_mapping/runtime.rb +276 -0
  10. data/lib/trailblazer/activity/dsl/linear/feature/variable_mapping.rb +70 -226
  11. data/lib/trailblazer/activity/dsl/linear/helper/path.rb +37 -18
  12. data/lib/trailblazer/activity/dsl/linear/helper.rb +38 -17
  13. data/lib/trailblazer/activity/dsl/linear/normalizer/extensions.rb +63 -0
  14. data/lib/trailblazer/activity/dsl/linear/normalizer/inherit.rb +90 -0
  15. data/lib/trailblazer/activity/dsl/linear/normalizer/output_tuples.rb +160 -0
  16. data/lib/trailblazer/activity/dsl/linear/normalizer/terminus.rb +26 -29
  17. data/lib/trailblazer/activity/dsl/linear/normalizer.rb +99 -160
  18. data/lib/trailblazer/activity/dsl/linear/sequence/builder.rb +3 -2
  19. data/lib/trailblazer/activity/dsl/linear/sequence/compiler.rb +21 -17
  20. data/lib/trailblazer/activity/dsl/linear/sequence/search.rb +2 -8
  21. data/lib/trailblazer/activity/dsl/linear/strategy.rb +56 -17
  22. data/lib/trailblazer/activity/dsl/linear/version.rb +1 -1
  23. data/lib/trailblazer/activity/dsl/linear.rb +13 -1
  24. data/lib/trailblazer/activity/fast_track.rb +96 -67
  25. data/lib/trailblazer/activity/path.rb +35 -53
  26. data/lib/trailblazer/activity/railway.rb +63 -65
  27. data/trailblazer-activity-dsl-linear.gemspec +8 -8
  28. metadata +27 -18
  29. data/lib/trailblazer/activity/dsl/linear/feature/variable_mapping/inherit.rb +0 -38
@@ -0,0 +1,276 @@
1
+ module Trailblazer
2
+ class Activity
3
+ module DSL::Linear
4
+ module VariableMapping
5
+ module_function
6
+
7
+ # Runtime classes
8
+
9
+ # These objects are created via the DSL, keep all i/o steps in a Pipeline
10
+ # and run the latter when being `call`ed.
11
+ module Pipe
12
+ class Input
13
+ def initialize(pipe, id: :vm_original_ctx)
14
+ @pipe = pipe
15
+ @id = id
16
+ end
17
+
18
+ def call(wrap_ctx, original_args)
19
+ (original_ctx, original_flow_options), original_circuit_options = original_args
20
+
21
+ # let user compute new ctx for the wrapped task.
22
+ pipe_ctx, _ = @pipe.({original_ctx: original_ctx, aggregate: {}}, original_args)
23
+ ctx_from_input = pipe_ctx[:input_ctx]
24
+
25
+ wrap_ctx = wrap_ctx.merge(@id => original_ctx) # remember the original ctx under the key {@id}.
26
+
27
+ # instead of the original Context, pass on the filtered `ctx_from_input` in the wrap.
28
+ return wrap_ctx, [[ctx_from_input, original_flow_options], original_circuit_options]
29
+ end
30
+ end
31
+
32
+ # API in VariableMapping::Output:
33
+ # output_ctx = @filter.(returned_ctx, [original_ctx, returned_flow_options], **original_circuit_options)
34
+ # Returns {output_ctx} that is used after taskWrap finished.
35
+ class Output < Input
36
+ def call(wrap_ctx, original_args)
37
+ returned_ctx, returned_flow_options = wrap_ctx[:return_args] # this is the Context returned from {call}ing the wrapped user task.
38
+ original_ctx = wrap_ctx[@id] # grab the original ctx from before which was set in the {:input} filter.
39
+ _, original_circuit_options = original_args
40
+
41
+ # let user compute the output.
42
+ pipe_ctx, _ = @pipe.({original_ctx: original_ctx, returned_ctx: returned_ctx, aggregate: {}}, [[original_ctx, returned_flow_options], original_circuit_options])
43
+ ctx_from_output = pipe_ctx[:aggregate]
44
+
45
+ wrap_ctx = wrap_ctx.merge(return_args: [ctx_from_output, returned_flow_options]) # DISCUSS: this won't allow tracing in the taskWrap as we're returning {returned_flow_options} from above.
46
+
47
+ return wrap_ctx, original_args
48
+ end
49
+ end
50
+ end
51
+
52
+ # Merge all original ctx variables into the new input_ctx.
53
+ # This happens when no {:input} is provided.
54
+ def default_input_ctx(wrap_ctx, original_args)
55
+ default_ctx = wrap_ctx[:original_ctx]
56
+
57
+ merge_variables(default_ctx, wrap_ctx, original_args)
58
+ end
59
+
60
+ # Write one particular variable to the {aggregate} using {aggregate[:name] = (value)}.
61
+ #
62
+ # This is much faster than merging a hash, and provides better overriding semantics. (to be done!)
63
+ #
64
+ # @param filter Any circuit-step compatible callable that exposes {#call(args, **circuit_options)}
65
+ # and returns [value, new_ctx]
66
+ #
67
+
68
+ # Filter
69
+ class VariableFromCtx
70
+ def initialize(variable_name:)
71
+ @variable_name = variable_name
72
+ end
73
+
74
+ # Grab @variable_name from {ctx}.
75
+ def call((ctx, _), **) # Circuit-step interface
76
+ return ctx[@variable_name], ctx
77
+ end
78
+ end
79
+
80
+ # Filter
81
+ class VariablePresent < VariableFromCtx
82
+ # Grab @variable_name from {ctx} if it's there.
83
+ def call((ctx, _), **) # Circuit-step interface
84
+ return ctx.key?(@variable_name), ctx
85
+ end
86
+ end
87
+
88
+ # TODO: * ALL FILTERS and conditions expose circuit-step interface.
89
+ # @param name Identifier for the pipeline
90
+ # Call {user_filter} and set return value as variable on aggregate.
91
+ class SetVariable
92
+ def initialize(write_name:, filter:, user_filter:, name:, **)
93
+ @write_name = write_name
94
+ @filter = filter
95
+ @name = name
96
+ end
97
+
98
+ attr_reader :name # TODO: used when adding to pipeline, change to to_h
99
+
100
+ def call(wrap_ctx, original_args, filter = @filter)
101
+ wrap_ctx = self.class.set_variable_for_filter(filter, @write_name, wrap_ctx, original_args)
102
+
103
+ return wrap_ctx, original_args
104
+ end
105
+
106
+ def self.set_variable_for_filter(filter, write_name, wrap_ctx, original_args)
107
+ value = call_filter(filter, wrap_ctx, original_args)
108
+ wrap_ctx = set_variable(value, write_name, wrap_ctx, original_args)
109
+
110
+ wrap_ctx
111
+ end
112
+
113
+ # Call a filter with a Circuit-Step interface.
114
+ def self.call_filter(filter, wrap_ctx, (args, circuit_options))
115
+ value, _ = filter.(args, **circuit_options) # circuit-step interface
116
+ value
117
+ end
118
+
119
+ def self.set_variable(value, write_name, wrap_ctx, original_args)
120
+ wrap_ctx[:aggregate][write_name] = value # yes, we're mutating, but this is ok as we're on some private hash.
121
+ wrap_ctx # DISCUSS: could be omitted.
122
+ end
123
+
124
+ # Set variable on ctx if {condition} is true.
125
+ class Conditioned < SetVariable
126
+ def initialize(condition:, **options)
127
+ super(**options)
128
+
129
+ @condition = condition # DISCUSS: adding this as an "optional" step in a "Railway"
130
+ end
131
+
132
+ def call(wrap_ctx, original_args)
133
+ decision, _ = SetVariable.call_filter(@condition, wrap_ctx, original_args)
134
+
135
+ return super if decision
136
+ return wrap_ctx, original_args
137
+ end
138
+ end
139
+
140
+ # Set variable on ctx if {condition} is true.
141
+ # Otherwise, set default_filter variable on ctx.
142
+ class Default < SetVariable
143
+ def initialize(default_filter:, condition:, **options)
144
+ super(**options)
145
+
146
+ @default_filter = default_filter
147
+ @condition = condition
148
+ end
149
+
150
+ def call(wrap_ctx, original_args)
151
+ # FIXME: redundant with Conditioned.
152
+ decision, _ = SetVariable.call_filter(@condition, wrap_ctx, original_args)
153
+
154
+ filter = decision ? @filter : @default_filter
155
+
156
+ super(wrap_ctx, original_args, filter)
157
+ end
158
+ end # Default
159
+
160
+ # TODO: we don't have Out(:variable), yet!
161
+ class Output < SetVariable
162
+ # Call a filter with a Circuit-Step interface.
163
+ def self.call_filter(filter, wrap_ctx, original_args)
164
+ new_ctx = wrap_ctx[:returned_ctx]
165
+
166
+ call_filter_with_ctx(filter, new_ctx, wrap_ctx, original_args)
167
+ end
168
+
169
+ def self.call_filter_with_ctx(filter, ctx, wrap_ctx, ((_, flow_options), circuit_options))
170
+ SetVariable.call_filter(filter, wrap_ctx, [[ctx, flow_options], circuit_options])
171
+ end
172
+ end
173
+
174
+ # Do everything SetVariable does but read from {aggregate}, not from {ctx}.
175
+ # TODO: it would be cool to have this also for AddVariables.
176
+ class ReadFromAggregate < SetVariable
177
+ def self.call_filter(filter, wrap_ctx, original_args)
178
+ new_ctx = wrap_ctx[:aggregate]
179
+
180
+ Output.call_filter_with_ctx(filter, new_ctx, wrap_ctx, original_args)
181
+ end
182
+ end
183
+
184
+ # @private
185
+ # Always deletes from {:aggregate}.
186
+ class Delete < SetVariable
187
+ def call(wrap_ctx, original_args)
188
+ wrap_ctx[:aggregate].delete(@write_name) # FIXME: we're mutating a hash here!
189
+
190
+ return wrap_ctx, original_args
191
+ end
192
+ end
193
+ end # SetVariable
194
+
195
+ # AddVariables: I call something with an Option-interface and run the return value through merge_variables().
196
+ # works on {:aggregate} by (usually) producing a hash fragment that is merged with the existing {:aggregate}
197
+
198
+ # Add a hash of variables to aggregate after running a filter (which returns a hash!).
199
+ # Note that we only use those for "old-style" callables that produce hashes.
200
+ class AddVariables < SetVariable
201
+ def self.set_variable(variables, write_name, wrap_ctx, original_args)
202
+ wrap_ctx, _ = VariableMapping.merge_variables(variables, wrap_ctx, original_args)
203
+ wrap_ctx
204
+ end
205
+
206
+ # Merge hash of Out into aggregate.
207
+ # TODO: deprecate and remove.
208
+ class Output < SetVariable::Output
209
+ def self.set_variable(*args)
210
+ AddVariables.set_variable(*args)
211
+ end
212
+
213
+ # Pass {inner_ctx, outer_ctx, **inner_ctx}
214
+ class WithOuterContext_Deprecated < Output
215
+ def self.call_filter(filter, wrap_ctx, ((original_ctx, _), circuit_options))
216
+ new_ctx = wrap_ctx[:returned_ctx] # FIXME: redundant.
217
+
218
+ # Here, due to a stupid API decision, we have to call an Option with two positional args.
219
+ filter.(new_ctx, original_ctx, keyword_arguments: new_ctx.to_hash, **circuit_options)
220
+ end
221
+ end
222
+
223
+ class WithOuterContext < Output
224
+ def self.call_filter(filter, wrap_ctx, ((original_ctx, flow_options), circuit_options))
225
+ new_ctx = wrap_ctx[:returned_ctx]
226
+ new_ctx = new_ctx.merge(outer_ctx: original_ctx)
227
+
228
+ Output.call_filter_with_ctx(filter, new_ctx, wrap_ctx, [[original_ctx, flow_options], circuit_options])
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+ def merge_variables(variables, wrap_ctx, original_args, receiver = wrap_ctx[:aggregate])
235
+ wrap_ctx[:aggregate] = receiver.merge(variables)
236
+
237
+ return wrap_ctx, original_args
238
+ end
239
+
240
+ # Finally, create a new input ctx from all the
241
+ # collected input variables.
242
+ # This goes into the step/nested OP.
243
+ def scope(wrap_ctx, original_args)
244
+ ((_, flow_options), _) = original_args
245
+
246
+ # this is the actual context passed into the step.
247
+ wrap_ctx[:input_ctx] = Trailblazer::Context(
248
+ wrap_ctx[:aggregate],
249
+ {}, # mutable variables
250
+ flow_options[:context_options]
251
+ )
252
+
253
+ return wrap_ctx, original_args
254
+ end
255
+
256
+ # @private
257
+ # The default {:output} filter only returns the "mutable" part of the inner ctx.
258
+ # This means only variables added using {inner_ctx[..]=} are merged on the outside.
259
+ def default_output_ctx(wrap_ctx, original_args)
260
+ new_ctx = wrap_ctx[:returned_ctx]
261
+
262
+ _wrapped, mutable = new_ctx.decompose # `_wrapped` is what the `:input` filter returned, `mutable` is what the task wrote to `scoped`.
263
+
264
+ merge_variables(mutable, wrap_ctx, original_args)
265
+ end
266
+
267
+ def merge_with_original(wrap_ctx, original_args)
268
+ original_ctx = wrap_ctx[:original_ctx] # outer ctx
269
+ output_variables = wrap_ctx[:aggregate]
270
+
271
+ merge_variables(output_variables, wrap_ctx, original_args, original_ctx)
272
+ end
273
+ end # VariableMapping
274
+ end
275
+ end
276
+ end
@@ -5,11 +5,10 @@ module Trailblazer
5
5
  # Normalizer-steps to implement {:input} and {:output}
6
6
  # Returns an Extension instance to be thrown into the `step` DSL arguments.
7
7
  def self.VariableMapping(input_id: "task_wrap.input", output_id: "task_wrap.output", **options)
8
- input, output, normalizer_options, non_symbol_options = VariableMapping.merge_instructions_from_dsl(**options)
8
+ input, output = VariableMapping.merge_instructions_from_dsl(**options)
9
+ extension = VariableMapping.Extension(input, output)
9
10
 
10
- extension = VariableMapping.Extension(input, output)
11
-
12
- return TaskWrap::Extension::WrapStatic.new(extension: extension), normalizer_options, non_symbol_options
11
+ TaskWrap::Extension::WrapStatic.new(extension: extension)
13
12
  end
14
13
 
15
14
  module VariableMapping
@@ -20,9 +19,10 @@ module Trailblazer
20
19
  normalizer,
21
20
  "activity.wirings",
22
21
  {
23
- # In(), Out(), {:input}, Inject() feature
24
- "activity.normalize_input_output_filters" => Linear::Normalizer.Task(VariableMapping::Normalizer.method(:normalize_input_output_filters)),
25
- "activity.input_output_dsl" => Linear::Normalizer.Task(VariableMapping::Normalizer.method(:input_output_dsl)),
22
+ # In(), Out(), {:input}, Inject() feature
23
+ "activity.convert_symbol_options" => Linear::Normalizer.Task(VariableMapping::Normalizer.method(:convert_symbol_options)),
24
+ "activity.normalize_input_output_filters" => Linear::Normalizer.Task(VariableMapping::Normalizer.method(:normalize_input_output_filters)),
25
+ "activity.input_output_dsl" => Linear::Normalizer.Task(VariableMapping::Normalizer.method(:input_output_dsl)),
26
26
  }
27
27
  )
28
28
  end
@@ -37,259 +37,103 @@ module Trailblazer
37
37
 
38
38
  # Steps that are added to the DSL normalizer.
39
39
  module Normalizer
40
- # Process {In() => [:model], Inject() => [:current_user], Out() => [:model]}
41
- def self.normalize_input_output_filters(ctx, non_symbol_options:, **)
42
- input_exts = non_symbol_options.find_all { |k,v| k.is_a?(VariableMapping::DSL::In) }
43
- output_exts = non_symbol_options.find_all { |k,v| k.is_a?(VariableMapping::DSL::Out) }
44
- inject_exts = non_symbol_options.find_all { |k,v| k.is_a?(VariableMapping::DSL::Inject) }
45
-
46
- return unless input_exts.any? || output_exts.any? || inject_exts.any?
47
-
48
- ctx[:inject_filters] = inject_exts
49
- ctx[:in_filters] = input_exts
50
- ctx[:out_filters] = output_exts
51
- end
52
-
53
- def self.input_output_dsl(ctx, extensions: [], **options)
54
- # no :input/:output/:inject/Input()/Output() passed.
55
- return if (options.keys & [:input, :output, :inject, :inject_filters, :in_filters, :output_filters]).empty?
56
-
57
- extension, normalizer_options, non_symbol_options = Linear.VariableMapping(**options)
58
-
59
- ctx[:extensions] = extensions + [extension] # FIXME: allow {Extension() => extension}
60
- ctx.merge!(**normalizer_options) # DISCUSS: is there another way of merging variables into ctx?
61
- ctx[:non_symbol_options].merge!(non_symbol_options)
62
- end
63
- end
64
-
65
- module_function
66
-
67
- # For the input filter we
68
- # 1. create a separate {Pipeline} instance {pipe}. Depending on the user's options, this might have up to four steps.
69
- # 2. The {pipe} is run in a lamdba {input}, the lambda returns the pipe's ctx[:input_ctx].
70
- # 3. The {input} filter in turn is wrapped into an {Activity::TaskWrap::Input} object via {#merge_instructions_for}.
71
- # 4. The {TaskWrap::Input} instance is then finally placed into the taskWrap as {"task_wrap.input"}.
72
- #
73
- # @private
74
- #
75
-
76
- # default_input
77
- # <or>
78
- # oldway
79
- # :input
80
- # :inject
81
- # newway(initial_input_pipeline)
82
- # In,Inject
83
- # => input_pipe
84
- def merge_instructions_from_dsl(**options)
85
- # The overriding {:input} option is set.
86
- pipeline, has_mono_options, _ = DSL.pipe_for_mono_input(**options)
87
-
88
- if ! has_mono_options
89
- pipeline = DSL.pipe_for_composable_input(**options) # FIXME: rename filters consistently
90
- end
91
-
92
- # gets wrapped by {VariableMapping::Input} and called there.
93
- # API: @filter.([ctx, original_flow_options], **original_circuit_options)
94
- # input = Trailblazer::Option(->(original_ctx, **) { })
95
- input = Pipe::Input.new(pipeline)
96
-
97
-
98
- output_pipeline, has_mono_options, _ = DSL.pipe_for_mono_output(**options)
99
-
100
- if ! has_mono_options
101
- output_pipeline = DSL.pipe_for_composable_output(**options)
102
- end
103
-
104
- output = Pipe::Output.new(output_pipeline)
40
+ # TODO: remove me once {:input} API is removed.
41
+ # Convert {:input}, {:output} and {:inject} to In() and friends.
42
+ def self.convert_symbol_options(ctx, non_symbol_options:, output_with_outer_ctx: nil, **)
43
+ input, output, inject = ctx.delete(:input), ctx.delete(:output), ctx.delete(:inject)
44
+ return unless input || output || inject
105
45
 
106
- return input, output,
107
- # normalizer_options:
108
- {
109
- variable_mapping_pipelines: [pipeline, output_pipeline],
110
- },
111
- # non_symbol_options:
112
- {
113
- Linear::Strategy.DataVariable() => :variable_mapping_pipelines # we want to store {:variable_mapping_pipelines} in {Row.data} for later reference.
114
- }
115
- # DISCUSS: should we remember the pure pipelines or get it from the compiled extension?
116
- # store pipe in the extension (via TW::Extension.data)?
117
- end
46
+ dsl_options = {}
118
47
 
119
- # < AddVariables
120
- # Option
121
- # filter
122
- # merge_variables
48
+ # TODO: warn, deprecate etc
49
+ dsl_options.merge!(VariableMapping::DSL.In() => input) if input
123
50
 
124
- # Runtime classes
125
- Filter = Struct.new(:aggregate_step, :filter, :name, :add_variables_class)
51
+ if output
52
+ options = {}
53
+ options = options.merge(with_outer_ctx: output_with_outer_ctx) unless output_with_outer_ctx.nil?
126
54
 
127
- # These objects are created via the DSL, keep all i/o steps in a Pipeline
128
- # and run the latter when being `call`ed.
129
- module Pipe
130
- class Input
131
- def initialize(pipe, id: :vm_original_ctx)
132
- @pipe = pipe
133
- @id = id # DISCUSS: untested.
55
+ dsl_options.merge!(VariableMapping::DSL.Out(**options) => output)
134
56
  end
135
57
 
136
- def call(wrap_ctx, original_args)
137
- (original_ctx, original_flow_options), original_circuit_options = original_args
138
-
139
- # let user compute new ctx for the wrapped task.
140
- pipe_ctx, _ = @pipe.({original_ctx: original_ctx}, [[original_ctx, original_flow_options], original_circuit_options])
141
- input_ctx = pipe_ctx[:input_ctx]
142
-
143
- wrap_ctx = wrap_ctx.merge(@id => original_ctx) # remember the original ctx under the key {:vm_original_ctx}.
58
+ if inject
59
+ inject.collect do |filter|
60
+ filter = filter.is_a?(Symbol) ? [filter] : filter
144
61
 
145
- # instead of the original Context, pass on the filtered `input_ctx` in the wrap.
146
- return wrap_ctx, [[input_ctx, original_flow_options], original_circuit_options]
62
+ dsl_options.merge!(VariableMapping::DSL.Inject() => filter)
63
+ end
147
64
  end
148
- end
149
-
150
- # API in VariableMapping::Output:
151
- # output_ctx = @filter.(returned_ctx, [original_ctx, returned_flow_options], **original_circuit_options)
152
- # Returns {output_ctx} that is used after taskWrap finished.
153
- class Output < Input
154
- # def call(returned_ctx, (original_ctx, returned_flow_options), **original_circuit_options)
155
- def call(wrap_ctx, original_args)
156
- returned_ctx, returned_flow_options = wrap_ctx[:return_args] # this is the Context returned from {call}ing the wrapped user task.
157
- original_ctx = wrap_ctx[@id] # grab the original ctx from before which was set in the {:input} filter.
158
- _, original_circuit_options = original_args
159
-
160
- # let user compute the output.
161
- pipe_ctx, _ = @pipe.({original_ctx: original_ctx, returned_ctx: returned_ctx}, [[original_ctx, returned_flow_options], original_circuit_options])
162
-
163
- output_ctx = pipe_ctx[:aggregate]
164
65
 
165
- wrap_ctx = wrap_ctx.merge(return_args: [output_ctx, returned_flow_options]) # DISCUSS: this won't allow tracing in the taskWrap as we're returning {returned_flow_options} from above.
166
-
167
- return wrap_ctx, original_args
168
- end
66
+ ctx.merge!(
67
+ non_symbol_options: non_symbol_options.merge(dsl_options),
68
+ input_output_inject_options: [{input: input, output: output, inject: inject}, dsl_options], # yes, there were {:input} options.
69
+ )
169
70
  end
170
- end
171
-
172
- # DISCUSS: improvable sections such as merge vs hash[]=
173
- def initial_aggregate(wrap_ctx, original_args)
174
- wrap_ctx = wrap_ctx.merge(aggregate: {})
175
71
 
176
- return wrap_ctx, original_args
177
- end
72
+ # Process {In() => [:model], Inject() => [:current_user], Out() => [:model]}
73
+ def self.normalize_input_output_filters(ctx, non_symbol_options:, input_output_inject_options: [], **)
74
+ in_exts = non_symbol_options.find_all { |k, v| k.is_a?(VariableMapping::DSL::In) || k.is_a?(VariableMapping::DSL::Inject) }
75
+ output_exts = non_symbol_options.find_all { |k, v| k.is_a?(VariableMapping::DSL::Out) }
178
76
 
179
- # Merge all original ctx variables into the new input_ctx.
180
- # This happens when no {:input} is provided.
181
- def default_input_ctx(wrap_ctx, original_args)
182
- default_ctx = wrap_ctx[:original_ctx]
77
+ return unless in_exts.any? || output_exts.any?
183
78
 
184
- merge_variables(default_ctx, wrap_ctx, original_args)
185
- end
79
+ deprecate_input_output_inject_option(input_output_inject_options, in_exts, output_exts)
186
80
 
187
- # Input/output Pipeline step that runs the user's {filter} and adds
188
- # variables to the computed ctx.
189
- #
190
- # Basically implements {:input}.
191
- #
192
- # AddVariables: I call something with an Option-interface and run the return value through merge_variables().
193
- # works on {:aggregate} by (usually) producing a hash fragment that is merged with the existing {:aggregate}
194
- class AddVariables
195
- def initialize(filter, user_filter)
196
- @filter = filter # The users input/output filter.
197
- @user_filter = user_filter # this is for introspection.
81
+ ctx[:in_filters] = in_exts
82
+ ctx[:out_filters] = output_exts
198
83
  end
199
84
 
200
- def call(wrap_ctx, original_args)
201
- ((original_ctx, _), circuit_options) = original_args
202
- # puts "@@@@@ #{wrap_ctx[:returned_ctx].inspect}"
85
+ def self.input_output_dsl(ctx, non_symbol_options:, in_filters: nil, out_filters: nil, **options)
86
+ # no :input/:output/:inject/Input()/Output() passed.
87
+ return unless in_filters || out_filters
203
88
 
204
- # this is the actual logic.
205
- variables = call_filter(wrap_ctx, original_ctx, circuit_options, original_args)
89
+ extension = Linear.VariableMapping(in_filters: in_filters, out_filters: out_filters, **options)
206
90
 
207
- VariableMapping.merge_variables(variables, wrap_ctx, original_args)
208
- end
91
+ record = Linear::Normalizer::Inherit.Record((in_filters + out_filters).to_h, type: :variable_mapping)
209
92
 
210
- def call_filter(wrap_ctx, original_ctx, circuit_options, original_args)
211
- _variables = @filter.(original_ctx, keyword_arguments: original_ctx.to_hash, **circuit_options)
212
- end
93
+ non_symbol_options = non_symbol_options.merge(record)
94
+ non_symbol_options = non_symbol_options.merge(Linear::Strategy.Extension(is_generic: true) => extension)
213
95
 
214
- class ReadFromAggregate < AddVariables # FIXME: REFACTOR
215
- def call_filter(wrap_ctx, original_ctx, circuit_options, original_args)
216
- new_ctx = wrap_ctx[:aggregate]
217
-
218
- _variables = @filter.(new_ctx, keyword_arguments: new_ctx.to_hash, **circuit_options)
219
- end
96
+ ctx.merge!(
97
+ non_symbol_options: non_symbol_options
98
+ )
220
99
  end
221
100
 
222
- class Output < AddVariables
223
- def call_filter(wrap_ctx, original_ctx, circuit_options, original_args)
224
- new_ctx = wrap_ctx[:returned_ctx]
225
-
226
- @filter.(new_ctx, keyword_arguments: new_ctx.to_hash, **circuit_options)
227
- end
101
+ # TODO: remove for TRB 2.2.
102
+ def self.deprecate_input_output_inject_option(input_output_inject_options, *composable_options)
103
+ return unless input_output_inject_options.any?
104
+ options, _dsl_options = input_output_inject_options
228
105
 
229
- # Pass {inner_ctx, outer_ctx, **inner_ctx}
230
- class WithOuterContext < Output
231
- def call_filter(wrap_ctx, original_ctx, circuit_options, original_args)
232
- new_ctx = wrap_ctx[:returned_ctx]
233
-
234
- @filter.(new_ctx, original_ctx, keyword_arguments: new_ctx.to_hash, **circuit_options)
235
- end
236
- end
106
+ deprecated_options_count = options.find_all { |(name, option)| option }.count + (options[:inject] ? options[:inject].count - 1 : 0)
107
+ composable_options_count = composable_options.collect { |options| options.size }.sum
237
108
 
238
- # Always deletes from {:aggregate}.
239
- class Delete < AddVariables
240
- def call(wrap_ctx, original_args)
241
- @filter.collect do |name|
242
- wrap_ctx[:aggregate].delete(name) # FIXME: we're mutating a hash here!
243
- end
109
+ return if composable_options_count == deprecated_options_count
244
110
 
245
- return wrap_ctx, original_args
246
- end
247
- end
111
+ Activity::Deprecate.warn Linear::Deprecate.dsl_caller_location, %(You are mixing #{options.inspect} with In(), Out() and Inject().\n#{VariableMapping.deprecation_link})
248
112
  end
249
113
  end
250
114
 
251
- # Finally, create a new input ctx from all the
252
- # collected input variables.
253
- # This goes into the step/nested OP.
254
- def scope(wrap_ctx, original_args)
255
- ((_, flow_options), _) = original_args
256
-
257
- # this is the actual context passed into the step.
258
- wrap_ctx[:input_ctx] = Trailblazer::Context(
259
- wrap_ctx[:aggregate],
260
- {}, # mutable variables
261
- flow_options[:context_options]
262
- )
263
-
264
- return wrap_ctx, original_args
265
- end
266
-
267
- # Last call in every step. Currently replaces {:input_ctx} by adding variables using {#merge}.
268
- # DISCUSS: improve here?
269
- def merge_variables(variables, wrap_ctx, original_args)
270
- wrap_ctx[:aggregate] = wrap_ctx[:aggregate].merge(variables)
271
-
272
- return wrap_ctx, original_args
273
- end
115
+ module_function
274
116
 
117
+ # For the input filter we
118
+ # 1. create a separate {Pipeline} instance {pipe}. Depending on the user's options, this might have up to four steps.
119
+ # 2. The {pipe} is run in a lamdba {input}, the lambda returns the pipe's ctx[:input_ctx].
120
+ # 3. The {input} filter in turn is wrapped into an {Activity::TaskWrap::Input} object via {#merge_instructions_for}.
121
+ # 4. The {TaskWrap::Input} instance is then finally placed into the taskWrap as {"task_wrap.input"}.
122
+ #
275
123
  # @private
276
- # The default {:output} filter only returns the "mutable" part of the inner ctx.
277
- # This means only variables added using {inner_ctx[..]=} are merged on the outside.
278
- def default_output_ctx(wrap_ctx, original_args)
279
- new_ctx = wrap_ctx[:returned_ctx]
124
+ #
125
+ def merge_instructions_from_dsl(**options)
126
+ pipeline = DSL.pipe_for_composable_input(**options) # FIXME: rename filters consistently
127
+ input = Pipe::Input.new(pipeline)
280
128
 
281
- _wrapped, mutable = new_ctx.decompose # `_wrapped` is what the `:input` filter returned, `mutable` is what the task wrote to `scoped`.
129
+ output_pipeline = DSL.pipe_for_composable_output(**options)
130
+ output = Pipe::Output.new(output_pipeline)
282
131
 
283
- merge_variables(mutable, wrap_ctx, original_args)
132
+ return input, output
284
133
  end
285
134
 
286
- def merge_with_original(wrap_ctx, original_args)
287
- original_ctx = wrap_ctx[:original_ctx] # outer ctx
288
- output_variables = wrap_ctx[:aggregate]
289
-
290
- wrap_ctx[:aggregate] = original_ctx.merge(output_variables) # FIXME: use merge_variables()
291
- # pp wrap_ctx
292
- return wrap_ctx, original_args
135
+ def deprecation_link
136
+ %(Please refer to https://trailblazer.to/2.1/docs/activity.html#activity-variable-mapping-deprecation-notes and have a nice day.)
293
137
  end
294
138
  end # VariableMapping
295
139
  end