module Trailblazer # Running a Circuit instance will run all tasks sequentially depending on the former's result. # Each task is called and retrieves the former task's return values. # # Note: Please use #Activity as a public circuit builder. # # @param map [Hash] Defines the wiring. # @param stop_events [Array] Tasks that stop execution of the circuit. # # result = circuit.(start_at, *args) # # @see Activity # @api semi-private class Circuit def initialize(map, stop_events) @map = map @stop_events = stop_events end # @param args [Array] all arguments to be passed to the task's `call` # @param task [callable] task to call Run = ->(task, args, **circuit_options) { task.(args, **circuit_options) } # Runs the circuit until we hit a stop event. # # This method throws exceptions when the returned value of a task doesn't match # any wiring. # # @param task An event or task of this circuit from where to start # @param options anything you want to pass to the first task # @param flow_options Library-specific flow control data # @return [last_signal, options, flow_options, *args] # # DISCUSS: returned circuit_options are ignored when calling the runner. def call(args, task: raise, runner: Run, **circuit_options) loop do last_signal, args, _ignored_circuit_options = runner.( task, args, circuit_options.merge( runner: runner ) # options for runner, to be discarded. ) # Stop execution of the circuit when we hit a stop event (< End). This could be an task's End or Suspend. return [ last_signal, args ] if @stop_events.include?(task) # DISCUSS: return circuit_options here? task = next_for(task, last_signal) do |next_task, in_map| raise IllegalInputError.new("#{task}") unless in_map raise IllegalOutputSignalError.new("from : `#{task}`===>[ #{last_signal.inspect} ]") unless next_task end end end # Returns the circuit's components. def to_fields [ @map, @stop_events ] end private def next_for(last_task, emitted_signal) # p @map in_map = false cfg = @map.keys.find { |t| t == last_task } and in_map = true cfg = @map[cfg] if cfg cfg ||= {} next_task = cfg[emitted_signal] yield next_task, in_map next_task end class IllegalInputError < RuntimeError end class IllegalOutputSignalError < RuntimeError end end end