Follow the Brass

lib/production_run.rb

76 lines · ruby

Project files
# frozen_string_literal: true

require_relative "batch_record"
require_relative "halted_run"
require_relative "run_result"
require_relative "work_in_progress"

class ProductionRun
  def initialize(classifier:, gate:, line:, final_inspection:)
    @classifier = classifier
    @gate = gate
    @press = line.press
    @recovery = line.recovery
    @final_inspection = final_inspection
  end

  def call(items, provenance:)
    classification = @classifier.classify(items)
    admitted, rejected = partition_admissions(classification.accepted)
    execute(admitted, classification, rejected, provenance)
  end

  private

  def execute(admitted, classification, rejected, provenance)
    fault = run_through_press(admitted)
    return halted_result(fault, classification, rejected) if fault

    completed_result(classification, rejected, provenance)
  end

  def partition_admissions(cases)
    assessed = cases.map { |kase| [kase, @gate.assess(kase)] }
    admitted = assessed.select { |_, admission| admission.admitted? }.map(&:first)
    [admitted, assessed.reject { |_, admission| admission.admitted? }]
  end

  def run_through_press(cases)
    queue = cases.map { |kase| WorkInProgress.new(cartridge_case: kase) }
    until queue.empty? && @press.empty?
      cycle = run_cycle(queue)
      return cycle unless cycle.advanced?
    end
  end

  def run_cycle(queue)
    @press.feed(queue.shift) unless queue.empty?
    @press.cycle
  end

  def inspect_completed
    assessed = @press.completed.map { |work| [work, @final_inspection.assess(work)] }
    released = assessed.select { |_, admission| admission.admitted? }.map(&:first)
    [released, assessed.reject { |_, admission| admission.admitted? }]
  end

  def record_for(released, provenance)
    BatchRecord.new(cartridge_count: released.length, **provenance)
  end

  def completed_result(classification, rejected, provenance)
    released, quarantined = inspect_completed
    RunResult.new(released: released, record: record_for(released, provenance),
                  wrong_context: classification.wrong_context,
                  foreign: classification.foreign,
                  rejected: rejected, quarantined: quarantined)
  end

  def halted_result(cycle, classification, rejected)
    positions = @press.positions
    HaltedRun.new(faults: cycle.outcomes.select { |outcome| outcome.status == :faulted },
                  positions: positions, recovery_plan: @recovery.assess(positions),
                  wrong_context: classification.wrong_context,
                  foreign: classification.foreign, rejected: rejected)
  end
end

Read along in the manual: The Reloading Lab · Download the project (zip)