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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +6 -4
- data/.github/workflows/ci_jruby.yml +19 -0
- data/.github/workflows/ci_legacy.yml +19 -0
- data/.github/workflows/ci_truffleruby.yml +19 -0
- data/CHANGES.md +28 -0
- data/Gemfile +3 -3
- data/Rakefile +1 -1
- data/lib/trailblazer/activity/dsl/linear/feature/patch.rb +5 -1
- data/lib/trailblazer/activity/dsl/linear/feature/variable_mapping/dsl.rb +242 -147
- data/lib/trailblazer/activity/dsl/linear/feature/variable_mapping/runtime.rb +278 -0
- data/lib/trailblazer/activity/dsl/linear/feature/variable_mapping.rb +65 -211
- data/lib/trailblazer/activity/dsl/linear/helper.rb +17 -5
- data/lib/trailblazer/activity/dsl/linear/normalizer/terminus.rb +1 -1
- data/lib/trailblazer/activity/dsl/linear/normalizer.rb +3 -12
- data/lib/trailblazer/activity/dsl/linear/strategy.rb +41 -2
- data/lib/trailblazer/activity/dsl/linear/version.rb +1 -1
- data/lib/trailblazer/activity/dsl/linear.rb +1 -0
- data/lib/trailblazer/activity/fast_track.rb +5 -13
- data/lib/trailblazer/activity/path.rb +13 -24
- data/lib/trailblazer/activity/railway.rb +4 -12
- data/trailblazer-activity-dsl-linear.gemspec +3 -2
- metadata +32 -8
@@ -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.
|
25
|
-
"activity.
|
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
|
-
|
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
|
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[:
|
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 & [:
|
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
|
-
#
|
86
|
-
|
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
|
-
|
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
|
-
|
120
|
-
#
|
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
|
-
|
60
|
-
|
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:
|
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"
|
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
|
-
#
|
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(
|
83
|
-
|
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.
|