trailblazer-activity-dsl-linear 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,278 @@
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
+
102
+ wrap_ctx = self.class.set_variable_for_filter(filter, @write_name, wrap_ctx, original_args)
103
+
104
+ return wrap_ctx, original_args
105
+ end
106
+
107
+ def self.set_variable_for_filter(filter, write_name, wrap_ctx, original_args)
108
+ value = call_filter(filter, wrap_ctx, original_args)
109
+ wrap_ctx = set_variable(value, write_name, wrap_ctx, original_args)
110
+
111
+ wrap_ctx
112
+ end
113
+
114
+ # Call a filter with a Circuit-Step interface.
115
+ def self.call_filter(filter, wrap_ctx, (args, circuit_options))
116
+ value, _ = filter.(args, **circuit_options) # circuit-step interface
117
+ value
118
+ end
119
+
120
+ def self.set_variable(value, write_name, wrap_ctx, original_args)
121
+ wrap_ctx[:aggregate][write_name] = value # yes, we're mutating, but this is ok as we're on some private hash.
122
+ wrap_ctx # DISCUSS: could be omitted.
123
+ end
124
+
125
+ # Set variable on ctx if {condition} is true.
126
+ class Conditioned < SetVariable
127
+ def initialize(condition:, **options)
128
+ super(**options)
129
+
130
+ @condition = condition # DISCUSS: adding this as an "optional" step in a "Railway"
131
+ end
132
+
133
+ def call(wrap_ctx, original_args)
134
+ decision, _ = SetVariable.call_filter(@condition, wrap_ctx, original_args)
135
+
136
+ return super if decision
137
+ return wrap_ctx, original_args
138
+ end
139
+ end
140
+
141
+ # Set variable on ctx if {condition} is true.
142
+ # Otherwise, set default_filter variable on ctx.
143
+ class Default < SetVariable
144
+ def initialize(default_filter:, condition:, **options)
145
+ super(**options)
146
+
147
+ @default_filter = default_filter
148
+ @condition = condition
149
+ end
150
+
151
+ def call(wrap_ctx, original_args)
152
+ # FIXME: redundant with Conditioned.
153
+ decision, _ = SetVariable.call_filter(@condition, wrap_ctx, original_args)
154
+
155
+ filter = decision ? @filter : @default_filter
156
+
157
+ super(wrap_ctx, original_args, filter)
158
+ end
159
+ end # Default
160
+
161
+ # TODO: we don't have Out(:variable), yet!
162
+ class Output < SetVariable
163
+ # Call a filter with a Circuit-Step interface.
164
+ def self.call_filter(filter, wrap_ctx, original_args)
165
+ new_ctx = wrap_ctx[:returned_ctx]
166
+
167
+ call_filter_with_ctx(filter, new_ctx, wrap_ctx, original_args)
168
+ end
169
+
170
+ def self.call_filter_with_ctx(filter, ctx, wrap_ctx, ((_, flow_options), circuit_options))
171
+ SetVariable.call_filter(filter, wrap_ctx, [[ctx, flow_options], circuit_options])
172
+ end
173
+ end
174
+
175
+ # Do everything SetVariable does but read from {aggregate}, not from {ctx}.
176
+ # TODO: it would be cool to have this also for AddVariables.
177
+ class ReadFromAggregate < SetVariable
178
+ def self.call_filter(filter, wrap_ctx, original_args)
179
+ new_ctx = wrap_ctx[:aggregate]
180
+
181
+ Output.call_filter_with_ctx(filter, new_ctx, wrap_ctx, original_args)
182
+ end
183
+ end
184
+
185
+ # @private
186
+ # Always deletes from {:aggregate}.
187
+ class Delete < SetVariable
188
+ def call(wrap_ctx, original_args)
189
+ wrap_ctx[:aggregate].delete(@write_name) # FIXME: we're mutating a hash here!
190
+
191
+ return wrap_ctx, original_args
192
+ end
193
+ end
194
+ end # SetVariable
195
+
196
+
197
+ # AddVariables: I call something with an Option-interface and run the return value through merge_variables().
198
+ # works on {:aggregate} by (usually) producing a hash fragment that is merged with the existing {:aggregate}
199
+
200
+ # Add a hash of variables to aggregate after running a filter (which returns a hash!).
201
+ # Note that we only use those for "old-style" callables that produce hashes.
202
+ class AddVariables < SetVariable
203
+ def self.set_variable(variables, write_name, wrap_ctx, original_args)
204
+ wrap_ctx, _ = VariableMapping.merge_variables(variables, wrap_ctx, original_args)
205
+ wrap_ctx
206
+ end
207
+
208
+ # Merge hash of Out into aggregate.
209
+ # TODO: deprecate and remove.
210
+ class Output < SetVariable::Output
211
+ def self.set_variable(*args)
212
+ AddVariables.set_variable(*args)
213
+ end
214
+
215
+ # Pass {inner_ctx, outer_ctx, **inner_ctx}
216
+ class WithOuterContext_Deprecated < Output
217
+ def self.call_filter(filter, wrap_ctx, ((original_ctx, _), circuit_options))
218
+ new_ctx = wrap_ctx[:returned_ctx] # FIXME: redundant.
219
+
220
+ # Here, due to a stupid API decision, we have to call an Option with two positional args.
221
+ filter.(new_ctx, original_ctx, keyword_arguments: new_ctx.to_hash, **circuit_options)
222
+ end
223
+ end
224
+
225
+ class WithOuterContext < Output
226
+ def self.call_filter(filter, wrap_ctx, ((original_ctx, flow_options), circuit_options))
227
+ new_ctx = wrap_ctx[:returned_ctx]
228
+ new_ctx = new_ctx.merge(outer_ctx: original_ctx)
229
+
230
+ Output.call_filter_with_ctx(filter, new_ctx, wrap_ctx, [[original_ctx, flow_options], circuit_options])
231
+ end
232
+ end
233
+ end
234
+ end
235
+
236
+ def merge_variables(variables, wrap_ctx, original_args, receiver = wrap_ctx[:aggregate])
237
+ wrap_ctx[:aggregate] = receiver.merge(variables)
238
+
239
+ return wrap_ctx, original_args
240
+ end
241
+
242
+ # Finally, create a new input ctx from all the
243
+ # collected input variables.
244
+ # This goes into the step/nested OP.
245
+ def scope(wrap_ctx, original_args)
246
+ ((_, flow_options), _) = original_args
247
+
248
+ # this is the actual context passed into the step.
249
+ wrap_ctx[:input_ctx] = Trailblazer::Context(
250
+ wrap_ctx[:aggregate],
251
+ {}, # mutable variables
252
+ flow_options[:context_options]
253
+ )
254
+
255
+ return wrap_ctx, original_args
256
+ end
257
+
258
+ # @private
259
+ # The default {:output} filter only returns the "mutable" part of the inner ctx.
260
+ # This means only variables added using {inner_ctx[..]=} are merged on the outside.
261
+ def default_output_ctx(wrap_ctx, original_args)
262
+ new_ctx = wrap_ctx[:returned_ctx]
263
+
264
+ _wrapped, mutable = new_ctx.decompose # `_wrapped` is what the `:input` filter returned, `mutable` is what the task wrote to `scoped`.
265
+
266
+ merge_variables(mutable, wrap_ctx, original_args)
267
+ end
268
+
269
+ def merge_with_original(wrap_ctx, original_args)
270
+ original_ctx = wrap_ctx[:original_ctx] # outer ctx
271
+ output_variables = wrap_ctx[:aggregate]
272
+
273
+ merge_variables(output_variables, wrap_ctx, original_args, original_ctx)
274
+ end
275
+ end # VariableMapping
276
+ end
277
+ end
278
+ end
@@ -21,8 +21,9 @@ module Trailblazer
21
21
  "activity.wirings",
22
22
  {
23
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)),
24
+ "activity.convert_symbol_options" => Linear::Normalizer.Task(VariableMapping::Normalizer.method(:convert_symbol_options)),
25
+ "activity.normalize_input_output_filters" => Linear::Normalizer.Task(VariableMapping::Normalizer.method(:normalize_input_output_filters)),
26
+ "activity.input_output_dsl" => Linear::Normalizer.Task(VariableMapping::Normalizer.method(:input_output_dsl)),
26
27
  }
27
28
  )
28
29
  end
@@ -37,22 +38,54 @@ module Trailblazer
37
38
 
38
39
  # Steps that are added to the DSL normalizer.
39
40
  module Normalizer
41
+ # TODO: remove me once {:input} API is removed.
42
+ # Convert {:input}, {:output} and {:inject} to In() and friends.
43
+ def self.convert_symbol_options(ctx, non_symbol_options:, output_with_outer_ctx: nil, **)
44
+ input, output, inject = ctx.delete(:input), ctx.delete(:output), ctx.delete(:inject)
45
+ return unless input || output || inject
46
+
47
+ dsl_options = {}
48
+
49
+ # TODO: warn, deprecate etc
50
+ dsl_options.merge!(VariableMapping::DSL.In() => input) if input
51
+
52
+ if output
53
+ options = {}
54
+ options = options.merge(with_outer_ctx: output_with_outer_ctx) unless output_with_outer_ctx.nil?
55
+
56
+ dsl_options.merge!(VariableMapping::DSL.Out(**options) => output)
57
+ end
58
+
59
+ if inject
60
+ inject.collect do |filter|
61
+ filter = filter.is_a?(Symbol) ? [filter] : filter
62
+
63
+ dsl_options.merge!(VariableMapping::DSL.Inject() => filter)
64
+ end
65
+ end
66
+
67
+ ctx.merge!(
68
+ non_symbol_options: non_symbol_options.merge(dsl_options),
69
+ input_output_inject_options: [{input: input, output: output, inject: inject}, dsl_options], # yes, there were {:input} options.
70
+ )
71
+ end
72
+
40
73
  # 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) }
74
+ def self.normalize_input_output_filters(ctx, non_symbol_options:, input_output_inject_options: [], **)
75
+ in_exts = non_symbol_options.find_all { |k,v| k.is_a?(VariableMapping::DSL::In) || k.is_a?(VariableMapping::DSL::Inject) }
43
76
  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
77
 
46
- return unless input_exts.any? || output_exts.any? || inject_exts.any?
78
+ return unless in_exts.any? || output_exts.any?
79
+
80
+ deprecate_input_output_inject_option(input_output_inject_options, in_exts, output_exts)
47
81
 
48
- ctx[:inject_filters] = inject_exts
49
- ctx[:in_filters] = input_exts
82
+ ctx[:in_filters] = in_exts
50
83
  ctx[:out_filters] = output_exts
51
84
  end
52
85
 
53
86
  def self.input_output_dsl(ctx, extensions: [], **options)
54
87
  # no :input/:output/:inject/Input()/Output() passed.
55
- return if (options.keys & [:input, :output, :inject, :inject_filters, :in_filters, :output_filters]).empty?
88
+ return if (options.keys & [:in_filters, :output_filters]).empty?
56
89
 
57
90
  extension, normalizer_options, non_symbol_options = Linear.VariableMapping(**options)
58
91
 
@@ -60,6 +93,23 @@ module Trailblazer
60
93
  ctx.merge!(**normalizer_options) # DISCUSS: is there another way of merging variables into ctx?
61
94
  ctx[:non_symbol_options].merge!(non_symbol_options)
62
95
  end
96
+
97
+ # TODO: remove for TRB 2.2.
98
+ def self.deprecate_input_output_inject_option(input_output_inject_options, *composable_options)
99
+ return unless input_output_inject_options.any?
100
+ options, dsl_options = input_output_inject_options
101
+
102
+ deprecated_options_count = options.find_all { |(name, option)| option }.count + (options[:inject] ? options[:inject].count-1 : 0)
103
+ composable_options_count = composable_options.collect { |options| options.size }.sum
104
+
105
+ return if composable_options_count == deprecated_options_count
106
+
107
+ # for deprecation warnings, guess the location if {:input} from the stack.
108
+ caller_index = caller_locations.find_index { |location| location.to_s =~ /recompile_activity_for/ }
109
+ caller_location = caller_index ? caller_locations[caller_index+2] : caller_locations[0]
110
+
111
+ Activity::Deprecate.warn caller_location, %{You are mixing #{options.inspect} with In(), Out() and Inject().\n#{VariableMapping.deprecation_link}}
112
+ end
63
113
  end
64
114
 
65
115
  module_function
@@ -72,36 +122,12 @@ module Trailblazer
72
122
  #
73
123
  # @private
74
124
  #
75
-
76
- # default_input
77
- # <or>
78
- # oldway
79
- # :input
80
- # :inject
81
- # newway(initial_input_pipeline)
82
- # In,Inject
83
- # => input_pipe
84
125
  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)
126
+ pipeline = DSL.pipe_for_composable_input(**options) # FIXME: rename filters consistently
127
+ input = Pipe::Input.new(pipeline)
96
128
 
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)
129
+ output_pipeline = DSL.pipe_for_composable_output(**options)
130
+ output = Pipe::Output.new(output_pipeline)
105
131
 
106
132
  return input, output,
107
133
  # normalizer_options:
@@ -116,180 +142,8 @@ module Trailblazer
116
142
  # store pipe in the extension (via TW::Extension.data)?
117
143
  end
118
144
 
119
- # < AddVariables
120
- # Option
121
- # filter
122
- # merge_variables
123
-
124
- # Runtime classes
125
- Filter = Struct.new(:aggregate_step, :filter, :name, :add_variables_class)
126
-
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.
134
- end
135
-
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}.
144
-
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]
147
- 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
-
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
169
- 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
-
176
- return wrap_ctx, original_args
177
- end
178
-
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]
183
-
184
- merge_variables(default_ctx, wrap_ctx, original_args)
185
- end
186
-
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.
198
- end
199
-
200
- def call(wrap_ctx, original_args)
201
- ((original_ctx, _), circuit_options) = original_args
202
- # puts "@@@@@ #{wrap_ctx[:returned_ctx].inspect}"
203
-
204
- # this is the actual logic.
205
- variables = call_filter(wrap_ctx, original_ctx, circuit_options, original_args)
206
-
207
- VariableMapping.merge_variables(variables, wrap_ctx, original_args)
208
- end
209
-
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
213
-
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
220
- end
221
-
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
228
-
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
237
-
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
244
-
245
- return wrap_ctx, original_args
246
- end
247
- end
248
- end
249
- end
250
-
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
274
-
275
- # @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]
280
-
281
- _wrapped, mutable = new_ctx.decompose # `_wrapped` is what the `:input` filter returned, `mutable` is what the task wrote to `scoped`.
282
-
283
- merge_variables(mutable, wrap_ctx, original_args)
284
- end
285
-
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
145
+ def deprecation_link
146
+ %{Please refer to https://trailblazer.to/2.1/docs/activity.html#activity-variable-mapping-deprecation-notes and have a nice day.}
293
147
  end
294
148
  end # VariableMapping
295
149
  end
@@ -56,18 +56,30 @@ module Trailblazer
56
56
  end
57
57
 
58
58
  # Computes the {:outputs} options for {activity}.
59
- def Subprocess(activity, patch: {})
60
- activity = Patch.customize(activity, options: patch)
59
+ # @param :strict If true, all outputs of {activity} will be wired to the track named after the
60
+ # output's semantic.
61
+ def Subprocess(activity, patch: {}, strict: false)
62
+ activity = Linear.Patch(activity, patch)
63
+
64
+ outputs = activity.to_h[:outputs]
65
+ options = {}
66
+
67
+ if strict
68
+ options.merge!(
69
+ outputs.collect { |output| [Output(output.semantic), Track(output.semantic)] }.to_h
70
+ )
71
+ end
61
72
 
62
73
  {
63
74
  task: activity,
64
- outputs: Hash[activity.to_h[:outputs].collect { |output| [output.semantic, output] }]
65
- }
75
+ outputs: outputs.collect { |output| [output.semantic, output] }.to_h,
76
+ }.
77
+ merge(options)
66
78
  end
67
79
 
68
80
  def In(**kws); VariableMapping::DSL::In(**kws); end
69
81
  def Out(**kws); VariableMapping::DSL::Out(**kws); end
70
- def Inject(**kws); VariableMapping::DSL::Inject(**kws); end
82
+ def Inject(*args, **kws); VariableMapping::DSL::Inject(*args, **kws); end
71
83
 
72
84
  def DataVariable
73
85
  DataVariableName.new
@@ -16,7 +16,7 @@ module Trailblazer
16
16
  "activity.normalize_normalizer_options" => Normalizer.Task(Normalizer.method(:merge_normalizer_options)),
17
17
  "activity.normalize_non_symbol_options" => Normalizer.Task(Normalizer.method(:normalize_non_symbol_options)),
18
18
  "activity.normalize_context" => Normalizer.method(:normalize_context),
19
- "terminus.normalize_task" => Normalizer.Task(Terminus.method(:normalize_task)),
19
+ "terminus.normalize_task" => Normalizer.Task(Terminus.method(:normalize_task)),
20
20
  "terminus.normalize_id" => Normalizer.Task(method(:normalize_id)),
21
21
  "terminus.normalize_magnetic_to" => Normalizer.Task(Terminus.method(:normalize_magnetic_to)),
22
22
  "terminus.append_end" => Normalizer.Task(Terminus.method(:append_end)),
@@ -62,16 +62,7 @@ module Trailblazer
62
62
  end
63
63
  end
64
64
 
65
- # @private
66
- class Task < TaskBuilder::Task
67
- def call(wrap_ctx, flow_options={})
68
- _result = call_option(@task, [wrap_ctx, flow_options]) # DISCUSS: this mutates {ctx}.
69
-
70
- return wrap_ctx, flow_options
71
- end
72
- end
73
-
74
- # Wrap user's normalizer task with {Trailblazer::Option} and thus execute it with
65
+ # Wrap user's normalizer task in a {Pipeline::TaskAdapter} so it executes with
75
66
  # convenient kw args.
76
67
  #
77
68
  # Example
@@ -79,8 +70,8 @@ module Trailblazer
79
70
  #
80
71
  # # will call {normalizer_task} and pass ctx variables as kwargs, as follows
81
72
  # def normalize_id(ctx, id: false, task:, **)
82
- def Task(user_proc)
83
- Normalizer::Task.new(Trailblazer::Option(user_proc), user_proc)
73
+ def Task(user_step)
74
+ 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}
84
75
  end
85
76
 
86
77
  # The generic normalizer not tied to `step` or friends.