trailblazer-activity-dsl-linear 1.0.0 → 1.1.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.
@@ -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.