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