The #attach method can be tricky to work with when the file doesn’t come from a browser, so after struggling a bit with it, I’m writing this down!

Peculiar image of a paper clip attaching a woman to a door.
class MyModel < ApplicationRecord
  has_one_attached :file
end

class DownloadRemoteImageJob
  def perform(my_model)
    ...
    response = http_conn.get(my_model.url) # remote file
    pathname = Pathname.new(my_model.url)
    mime = Rack::Mime.mime_type(pathname.extname) # this is a poor way of getting the mime-type

    # store response in tempfile
    tmpfile = Tempfile.new(pathname.basename.to_s)
    tmpfile.binmode
    tmpfile.write(response.body)
    tmpfile.rewind

    my_model.file.attach(
      io: tmpfile,
      filename: pathname.basename.to_s,
      content_type: mime
    )
    my_model.save!
  ensure
    tmpfile.close! if defined?(tmpfile)
  end
end

Credits