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.
- 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.
|