ruby-on-railsshrine

Shared derivatives configuration for multiple Shrine uploaders


I have uploaders for different types of images. Each has the same set of derivatives (large, medium, thumbnail), but different resolutions. But they also share some configuration. For example, each uploader converts the original to jpeg, changes quality and strips metadata.

class BannerUploader < Shrine
  Attacher.derivatives do |original|
    magick = ImageProcessing::MiniMagick.source(original)
      .convert('jpg')
      .saver(quality: 85)
      .strip

    {
      large: magick.resize_to_limit!(1600, 400),
      medium: magick.resize_to_limit!(800, 200),
      thumbnail: magick.resize_to_limit!(400, 100)
    }
  end
end

This one has the same processing rules:

class PhotoUploader < Shrine
  Attacher.derivatives do |original|
    magick = ImageProcessing::MiniMagick.source(original)
      .convert('jpg')
      .saver(quality: 85)
      .strip

    {
      large: magick.resize_to_limit!(1200, 1200),
      medium: magick.resize_to_limit!(600, 600),
      thumbnail: magick.resize_to_limit!(300, 300)
    }
  end
end

Is it possible to extract and share some of the configuration (like .convert('jpg').saver(quality: 85).strip) between those uploaders? Something similar to validations inheritance or a helper.


Solution

  • There isn't anything for sharing processing logic out-of-the-box, but you can create a service object, for example:

    class BannerUploader < Shrine
      Attacher.derivatives do |original|
        Thumbnails.call(original, {
          large: [1600, 400],
          medium: [800, 200],
          thumbnail: [400, 100],
        })
      end
    end
    
    class PhotoUploader < Shrine
      Attacher.derivatives do |original|
        Thumbnails.call(original, {
          large: [1200, 1200],
          medium: [600, 600],
          thumbnail: [300, 300],
        })
      end
    end
    
    class Thumbnails
      def self.call(original, sizes)
        magick = ImageProcessing::MiniMagick.source(original)
          .convert('jpg')
          .saver(quality: 85)
          .strip
    
        thumbnails = {}
    
        sizes.each do |name, (width, height)|
          thumbnails[name] = magick.resize_to_limit!(width, height)
        end
    
        thumbnails
      end
    end