Could someone help me convert this Dragonfly code to Shrine 3?

I have a really old app that I am converting from using Dragonfly 1.0.7 (and Rails 4) to Rails 6 and Shrine 3.latest.

When I initially wrote this app, in Rails 3.0, I followed a demo that had me put a bunch of logic into the routes.rb file:

  match '/thumbs/:width-:height-:x-:y;:scale_x-:scale_y/:id' => Dragonfly.app.endpoint { |params, app|
    #app.fetch(params[:uid]).thumb(Base64.decode64(params[:geometry]))
    geometry = "#{params[:width].to_i(36)}x#{params[:height].to_i(36)}+#{params[:x].to_i(36)}+#{params[:y].to_i(36)}"
    crop = "#{params[:scale_x].to_i(36)}x#{params[:scale_y].to_i(36)}"
    Image.find(params[:id].to_s.split('-').first).file.thumb(geometry).convert("-resize #{crop}")
  }, via: :get
  match '/thumbs/:width-:height-:x-:y/:id' => Dragonfly.app.endpoint { |params, app|
    #app.fetch(params[:uid]).thumb(Base64.decode64(params[:geometry]))
    geometry = "#{params[:width].to_i(36)}x#{params[:height].to_i(36)}+#{params[:x].to_i(36)}+#{params[:y].to_i(36)}"
    Image.find(params[:id].to_s.split('-').first).file.thumb(geometry)
  }, via: :get

So functionally, what this means to me is that I have a lot of legacy database records that have these sorts of image URLs embedded within strings of HTML or Markdown. New images won’t use this sort of URL format any more, but I don’t want to crawl through my existing records and try to update any existing references. I will have references to all of the original image files in S3, and I believe I will be able to translate their storage from the Dragonfly format to Shrine’s.

Can anyone recommend a way that I could translate this routes code into a modern Shrine idiom? I am using the latest everything, getting rid of deprecation warnings as I go, and I’ve not worked extensively with derivatives yet.

Thanks in advance,

Walter

Hi Walter, you could reimplement these endpoints using the derivation_endpoint plugin, as it already has APIs for converting files to responses. Something like this might work:

class ImageUploader  < Shrine
  # the secret key won't be needed, but ATM it's required when loading the plugin
  plugin :derivation_endpoint, secret_key: Rails.application.secrets.secret_key_base

  derivation :thumb do |file, width, height, x, y, scale_x = nil, scale_y = nil|
    magick = ImageProcessing::MiniMagick.source(file)
    magick = magick.crop("#{width}x#{height}+#{x}+y#{y}")
    magick = magick.resize_to_fit(scale_x.to_i, scale_y.to_i) if scale_x && scale_y
    magick.call
  end
end
match '/thumbs/:width-:height-:x-:y;:scale_x-:scale_y/:id', via: :get, to: -> (env) {
  params = Rack::Request.new(env).params

  image = Image.find(params["id"].to_s.split("-").first)
  # this is where you extract the file ID and Shrine storage from the Dragonfly attachment
  uploaded_image = ImageUploader.uploaded_file(id: image.file_id, storage: :store)

  derivation = uploaded_image.derivation :thumb, *params.fetch_values(
    "width",
    "height",
    "x",
    "y",
    "scale_x",
    "scale_y"
  )

  derivation.response(env)
}

You might need to parse out the parameters from the URL directly, I’m not sure whether this is currently done by Rails or Dragonfly. Note that this doesn’t require you to replace the Dragonfly attachment declaration with Shrine’s if that will make the migration easier.

Thanks very much! This gives me something to work from, to be sure. One question (well, two, actually).

  1. In the Dragonfly example, the dimension and scale parameters are given in Base36 encoding, and I had to decode them inline. Where do those bits go in the above? Do you do it in the accessing of the parameters (the match) or in the assignment to the derivation (or does it matter)?
  2. In the beginning of your example, there’s a mention of the secret key. Is that used in the derivation_endpoints plugin to cryptographically entangle the parameters as they are sent?

My only use for this is to support “legacy” image URLs, not to be the working solution going forward. So this is not meant to be a “writeable” format. All I want to do is take these encoded image references and use them as they lay in the existing data.

Thanks again!

Walter

Hi Walter,

  1. In the Dragonfly example, the dimension and scale parameters are given in Base36 encoding, and I had to decode them inline. Where do those bits go in the above? Do you do it in the accessing of the parameters (the match) or in the assignment to the derivation (or does it matter)?

I think it doesn’t matter. I would probably decode them before initializing the derivation, just to decouple derivations from Base36 encoding.

  1. In the beginning of your example, there’s a mention of the secret key. Is that used in the derivation_endpoints plugin to cryptographically entangle the parameters as they are sent?

This is used for signing the derivation endpoint URLs. It prevents the attacker from DoSing your app by issuing requests with changed URL parameters in order to bypass the cache and force your server to process every time. It means only the server can generate a valid derivation URL.