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
|
module ImageProcessing
# Abstract class inherited by individual processors.
class Processor
def self.call(source:, loader:, operations:, saver:, destination: nil)
unless source.is_a?(String) || source.is_a?(self::ACCUMULATOR_CLASS)
fail Error, "invalid source: #{source.inspect}"
end
if operations.dig(0, 0).to_s.start_with?("resize_") &&
loader.empty? &&
supports_resize_on_load?
accumulator = source
else
accumulator = load_image(source, **loader)
end
operations.each do |operation|
accumulator = apply_operation(accumulator, operation)
end
if destination
save_image(accumulator, destination, **saver)
else
accumulator
end
end
# Use for processor subclasses to specify the name and the class of their
# accumulator object (e.g. MiniMagick::Tool or Vips::Image).
def self.accumulator(name, klass)
define_method(name) { @accumulator }
protected(name)
const_set(:ACCUMULATOR_CLASS, klass)
end
# Delegates to #apply_operation.
def self.apply_operation(accumulator, (name, args, block))
new(accumulator).apply_operation(name, *args, &block)
end
# Whether the processor supports resizing the image upon loading.
def self.supports_resize_on_load?
false
end
def initialize(accumulator = nil)
@accumulator = accumulator
end
# Calls the operation to perform the processing. If the operation is
# defined on the processor (macro), calls the method. Otherwise calls the
# operation directly on the accumulator object. This provides a common
# umbrella above defined macros and direct operations.
def apply_operation(name, *args, &block)
receiver = respond_to?(name) ? self : @accumulator
if args.last.is_a?(Hash)
kwargs = args.pop
receiver.public_send(name, *args, **kwargs, &block)
else
receiver.public_send(name, *args, &block)
end
end
# Calls the given block with the accumulator object. Useful for when you
# want to access the accumulator object directly.
def custom(&block)
(block && block.call(@accumulator)) || @accumulator
end
end
end
|