class ImageProcessing::Pipeline

def call(save: true)

Determines the destination and calls the processor.
def call(save: true)
  if save == false
    call_processor
  elsif destination
    handle_destination do
      call_processor(destination: destination)
    end
  else
    create_tempfile do |tempfile|
      call_processor(destination: tempfile.path)
    end
  end
end

def call_processor(**options)

def call_processor(**options)
  processor.call(
    source:     source,
    loader:     loader,
    operations: operations,
    saver:      saver,
    **options
  )
end

def create_tempfile

the file descriptor to get the updated file.
Creates a new tempfile for the destination file, yields it, and refreshes
def create_tempfile
  tempfile = Tempfile.new(["image_processing", ".#{destination_format}"], binmode: true)
  yield tempfile
  tempfile.open
  tempfile
rescue
  tempfile.close! if tempfile
  raise
end

def destination_format

Determines the appropriate destination image format.
def destination_format
  format   = determine_format(destination) if destination
  format ||= self.format
  format ||= determine_format(source_path) if source_path
  format || DEFAULT_FORMAT
end

def determine_format(file_path)

def determine_format(file_path)
  extension = File.extname(file_path)
  extension[1..-1] if extension.size > 1
end

def handle_destination

deleted in case an exception is raised on saving the image.
empty destination file they created, so this method makes sure it is
In case of processing errors, both libvips and imagemagick will leave the
def handle_destination
  destination_existed = File.exist?(destination)
  yield
rescue
  File.delete(destination) if File.exist?(destination) && !destination_existed
  raise
end

def initialize(options)

Initializes the pipeline with all the processing options.
def initialize(options)
  fail Error, "source file is not provided" unless options[:source]
  options.each do |name, value|
    instance_variable_set(:"@#{name}", value)
  end
end

def source

Converts the source image object into a path or the accumulator object.
def source
  if @source.is_a?(String)
    @source
  elsif @source.respond_to?(:path)
    @source.path
  elsif @source.respond_to?(:to_path)
    @source.to_path
  else
    @source
  end
end

def source_path

Retrieves the source path on disk.
def source_path
  source if source.is_a?(String)
end