-
While preparing some chocolate chip cookies, E asked, “there’s one left from the last batch, shall I KitKat it?”
-
I completed my first successful escape attempt from Hades thanks to repeatedly watching Haelian’s videos, particularly the top 6 easiest builds.
-
After receiving some bad news, I went to make bread and discovered our kitchen scales didn’t work. Sitting on a flat worktop with nothing on the pan, the reading jumped around of its own accord.
With a ratchet screwdriver in one hand and some WD-40® Specialist® Fast Drying Contact Cleaner in the other, I took the whole thing apart.
Prying up the circuit board with a plectrum and discovering water pooled underneath the screen, I thought about my 92 year old grandfather who was the subject of the morning’s bad news. Taken ill in hospital, my occasionally overzealous sense of DIY comes entirely from him.
Always one to be fixing or building something throughout my childhood (or, on one occasion, inexplicably sawing the base off a beloved bin of my mother’s), he would invite me to help with whatever contraption he was working on. Even in his eighties, he drilled a hole in his walking stick so he could thread a loop through and secure it to his wrist, preventing it from ever falling from his grasp.
-
After rain broke our car gate again, I bought myself a four-way utility key which could lock and unlock the gate arms properly (rather than struggling with a pair of needle nose pliers). There’s something strangely satisfying about having a very specific tool for a job.
This might be why I own a spanner designed specifically for removing bath taps.
-
I finally had justification to migrate from Thoughtbot’s long-deprecated Paperclip to Rails’ Active Storage for storing uploads in a web application.
While Thoughtbot maintain a migration guide, I wanted to make sure I really understood the changes required before making them. Especially since I’m using Amazon S3 and Paperclip has a bug that can truncate your uploads when using S3.
While Paperclip concerns itself with processing uploads and storing them on disk or in a service like S3, it expects you to handle serving them by storing them somewhere publicly accessible (e.g. by storing files with a
public-read
ACL by default). As such, all my current uploads are stored in a public S3 bucket and I serve them via a CloudFront distribution.This notion of both storage and serving are entwined: uploads are stored with relatively human-readable paths, e.g.
/system/books/full_pdfs/00/00/01/original/MyPDF.pdf
, and are used to render any links, images, etc.Looking at Active Storage, I was initially confused not to see any support for serving uploads via a CDN. When doing some test uploads to my existing S3 bucket, I was even more confused to see that a file like
MyPDF.pdf
was uploaded to the top-level of the bucket with a base58 filename like77TMHrHJFvFDwodq8w7Ev2m7
.This is because I was still thinking of storage and serving as being one and the same. Diving into how Active Storage handles linking to uploads, things started making a little more sense.
Instead of serving uploads using their “real” location in your storage service, e.g. a random key in an S3 bucket, Active Storage serves all uploads through its own controller with its own URLs. This way, the URL can contain the original filename, e.g.
/rails/blobs/lengthy-signed-id-here/MyPDF.pdf
and generate short-lived, signed URLs to uploads in an otherwise private S3 bucket. What’s more, the implementation of this controller for Rails 6.0 (and its replacement in the upcoming Rails 6.1) is fantastically simple, effectively boiling down to:def show @blob = ActiveStorage::Blob.find_signed!(params[:signed_id]) expires_in ActiveStorage.service_urls_expire_in redirect_to @blob.service_url(disposition: params[:disposition]) # or @blob.url(...) for Rails 6.1 end
The official documentation explains this design decision:
Upon access, a redirect to the actual service endpoint is returned. This indirection decouples the public URL from the actual one, and allows, for example, mirroring attachments in different services for high-availability.
This decoupling means you rely on Rails to maintain your public URLs rather than using the underlying storage (as they rely on signed IDs to records in your database) but affords you the flexibility of moving storage services.
The difficult bit now is figuring out how to migrate all my existing Paperclip attachments to Active Storage without anyone noticing.
One useful thing I’ve discovered is that it is relatively easy to get a reference to a Paperclip attachment for a record even once you’ve replaced your call to
has_attached_file
withhas_one_attached
:class Book < ApplicationRecord has_one_attached :full_pdf # used to be has_attached_file :full_pdf end book = Book.first book.full_pdf # => #<ActiveStorage::Attached::One:0xfeedface> paperclip_attachment = Paperclip::Attachment.new(:full_pdf, book) # => #<Paperclip::Attachment:0xdecafbad>
You can then convert from a Paperclip attachment to an Active Storage one (albeit by downloading and re-uploading the original file) like so:
book.full_pdf.attach( io: URI.open(paperclip_attachment.url), filename: paperclip_attachment.original_filename, content_type: paperclip_attachment.content_type )
My hope is that I can write a custom Rake task that will copy all existing attachments to Active Storage before actually switching the application code over to using
has_one_attached
so that the database and S3 bucket are fully populated before the switch, e.g. something like the following:# The real implementation still uses Paperclip so override for the purposes of this task Book = Class.new(ApplicationRecord) { has_one_attached :full_pdf } Book.find_each do |book| paperclip_attachment = Paperclip::Attachment.new(:full_pdf, book) next unless paperclip_attachment.exists? book.full_pdf.attach( io: URI.open(paperclip_attachment.url), filename: paperclip_attachment.original_filename, content_type: paperclip_attachment.content_type ) end
-
On a related note, I was wondering how easy it would be to run your own S3-compatible storage service given that quite a few competing services offer interoperability. This led me to MinIO which is an open source tool you can point at a location on disk and then interact with it via the AWS CLI.
-
Despite writing weeknotes for over a year, I only just discovered that Jekyll has “Live Reload” and “Open URL” serve command options. No more will I manually open my browser to
localhost:4000
or repeatedly mash ⌘R:$ jekyll s -lo
Weeknotes 58
By Paul Mucur,
on