1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
|
require "tempfile"
module ImageProcessing
class Pipeline
DEFAULT_FORMAT = "jpg"
attr_reader :loader, :saver, :format, :operations, :processor, :destination
# 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
# 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
# Retrieves the source path on disk.
def source_path
source if source.is_a?(String)
end
# Determines the appropriate destination image format.
def destination_format
format = File.extname(destination)[1..-1] if destination
format ||= self.format
format ||= File.extname(source_path)[1..-1] if source_path
format || DEFAULT_FORMAT
end
private
def call_processor(**options)
processor.call(
source: source,
loader: loader,
operations: operations,
saver: saver,
**options
)
end
# Creates a new tempfile for the destination file, yields it, and refreshes
# the file descriptor to get the updated file.
def create_tempfile
tempfile = Tempfile.new(["image_processing", ".#{destination_format}"], binmode: true)
yield tempfile
tempfile.open
tempfile
rescue
tempfile.close! if tempfile
raise
end
# In case of processing errors, both libvips and imagemagick will leave the
# empty destination file they created, so this method makes sure it is
# deleted in case an exception is raised on saving the image.
def handle_destination
destination_existed = File.exist?(destination)
yield
rescue
File.delete(destination) if File.exist?(destination) && !destination_existed
raise
end
# 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
end
end
|