lib/trailblazer/macro/nested.rb in trailblazer-macro-2.1.9 vs lib/trailblazer/macro/nested.rb in trailblazer-macro-2.1.10.beta1
- old
+ new
@@ -7,28 +7,30 @@
caller_location = caller_locations(2, 1)[0]
warn "[Trailblazer]#{caller_location.absolute_path}: " \
"Using the `Nested()` macro with operations and activities is deprecated. " \
"Replace `Nested(#{callable})` with `Subprocess(#{callable})`."
- return Nested.operation_class.Subprocess(callable)
+ return Activity::Railway.Subprocess(callable)
end
- # dynamic
- task = Nested::Dynamic.new(callable, auto_wire: auto_wire)
+ task, outputs, compute_legacy_return_signal = Nested.Dynamic(callable, auto_wire: auto_wire)
merge = [
- [Activity::TaskWrap::Pipeline.method(:insert_before), "task_wrap.call_task", ["Nested.compute_nested_activity", task.method(:compute_nested_activity)]],
- [Activity::TaskWrap::Pipeline.method(:insert_after), "task_wrap.call_task", ["Nested.compute_return_signal", task.method(:compute_return_signal)]],
+ [task, id: "Nested.compute_nested_activity", prepend: "task_wrap.call_task"],
]
- task_wrap_extension = Activity::TaskWrap::Extension(merge: merge)
+ if compute_legacy_return_signal
+ merge << [compute_legacy_return_signal, id: "Nested.compute_return_signal", append: "task_wrap.call_task"]
+ end
+ task_wrap_extension = Activity::TaskWrap::Extension::WrapStatic.new(extension: Activity::TaskWrap::Extension(*merge))
+
{
task: task,
id: id,
extensions: [task_wrap_extension],
- outputs: task.outputs,
+ outputs: outputs,
}
end
# @private
module Nested
@@ -43,60 +45,67 @@
# So by default, it only connects good old success/failure ends. But it is also
# possible to connect all the ends of all possible dynamic activities
# by passing their list to {:auto_wire} option.
#
# step Nested(:compute_nested, auto_wire: [Create, Update])
- class Dynamic
- STATIC_OUTPUTS = {
- :success => Activity::Output(Activity::Railway::End::Success.new(semantic: :success), :success),
- :failure => Activity::Output(Activity::Railway::End::Failure.new(semantic: :failure), :failure),
- }
-
- def initialize(nested_activity_decider, auto_wire: [])
- @nested_activity_decider = Trailblazer::Option(nested_activity_decider)
- @known_activities = Array(auto_wire)
- @outputs = compute_task_outputs
+ def self.Dynamic(nested_activity_decider, auto_wire:)
+ if auto_wire.empty?
+ is_legacy = true # no auto_wire means we need to compute the legacy return signal.
+ auto_wire = [Class.new(Activity::Railway)]
end
- attr_reader :outputs
+ outputs = outputs_for(auto_wire)
+ task = Dynamic.new(nested_activity_decider)
+ compute_legacy_return_signal = Dynamic::ComputeLegacyReturnSignal.new(outputs) if is_legacy
+ return task, outputs, compute_legacy_return_signal
+ end
+
+ # Go through the list of all possible nested activities and compile the total sum of possible outputs.
+ # FIXME: WHAT IF WE HAVE TWO IDENTICALLY NAMED OUTPUTS?
+ # @private
+ def self.outputs_for(activities)
+ activities.map do |activity|
+ Activity::Railway.Subprocess(activity)[:outputs]
+ end.inject(:merge)
+ end
+
+ class Dynamic
+ def initialize(nested_activity_decider)
+ @nested_activity_decider = Trailblazer::Option(nested_activity_decider)
+ end
+
# TaskWrap step.
- def compute_nested_activity(wrap_ctx, original_args)
+ def call(wrap_ctx, original_args)
(ctx, _), original_circuit_options = original_args
# TODO: evaluate the option to get the actual "object" to call.
activity = @nested_activity_decider.(ctx, keyword_arguments: ctx.to_hash, **original_circuit_options)
# Overwrite :task so task_wrap.call_task will call this activity.
- # This is a trick so we don't have to repeat logic from #call_task here.
+ # This is a taskWrap trick so we don't have to repeat logic from #call_task here.
wrap_ctx[:task] = activity
return wrap_ctx, original_args
end
- def compute_return_signal(wrap_ctx, original_args)
- # NOOP when @known_activities are present as all possible signals have been registered already.
- if @known_activities.empty?
- # Translate the genuine nested signal to the generic Dynamic end (success/failure, only).
- # Note that here we lose information about what specific event was emitted.
- wrap_ctx[:return_signal] = wrap_ctx[:return_signal].kind_of?(Activity::Railway::End::Success) ?
- @outputs[:success].signal : @outputs[:failure].signal
+ # TODO: remove me when we make {:auto_wire} mandatory.
+ class ComputeLegacyReturnSignal
+ SUCCESS_SEMANTICS = [:success, :pass_fast] # TODO: make this injectable/or get it from operation.
+
+ def initialize(outputs)
+ @outputs = outputs # not needed for auto_wire!
end
- return wrap_ctx, original_args
- end
+ def call(wrap_ctx, original_args)
+ actual_semantic = wrap_ctx[:return_signal].to_h[:semantic]
+ applied_semantic = SUCCESS_SEMANTICS.include?(actual_semantic) ? :success : :failure
- private def compute_task_outputs
- # If :auto_wire is empty, we map outputs to :success and :failure only, for backward compatibility.
- # This is what {Nested} in 2.0 used to do, where the outcome could only be true/false (or success/failure).
- return STATIC_OUTPUTS if @known_activities.empty?
+ wrap_ctx[:return_signal] = @outputs.fetch(applied_semantic).signal
- # Merge activity#outputs from all given auto_wirable activities to wire up for this dynamic task.
- @known_activities.map do |activity|
- # TODO: Replace this when it's helper gets added.
- Hash[activity.to_h[:outputs].collect{ |output| [output.semantic, output] }]
- end.inject(:merge)
- end
+ return wrap_ctx, original_args
+ end
+ end # ComputeLegacyReturnSignal
end
end
end
end