Active Storage: Lessons Learned

I just finished a project to migrate one of our projects from using Anaconda for file attachments to ActiveStorage. These are things I've learned, mostly for my own reference, but perhaps they can be of help to others.

Note: This is all in the context of Rails 5.2.0

Direct Uploads Can Be Fussy

We're using direct-to-S3 uploads for our files, and there are still times where things just don't work. Almost always everything is fine on a second try, but it feels like the error handling just isn't all there yet. This is especially true for us on pages with multiple uploads.

AWS Checksums Don't Always Work for Us

This is likely our own bug, or a bug in our systems, but we've run into a few instances now where we get 400 errors back from AWS. After contacting support, they're all mismatched checksums on the data. We haven't been able to reproduce this enough to figure out the cause.

Variants Aren't Stored in the DB

When you're working with variants, they aren't stored in the DB. They're checked for existance every time you try to reference them. This has performance implications.

ActiveStorage Is Not Currently Optimized For Things You Want To Cache

In order to get some decent caching of our files, we've had to build our own controller to load and return actual file contents of the variant from S3, with all the cache control headers included. Doing the file-contents-passthru in this way isn't ideal, but right now ActiveStorage doesn't support public acls or non-expiring url generation. There are some hacks to get around this, but I know better than to hack around a core piece of Rails. So, for now, we just have code that looks like this, which works wonderfully (but should be built-in functionality):

raise "Invalid Object/Variant combination" unless valid_variant?

if @variant.nil?
  render plain: "", status: 404

send_data open(@variant.service_url).read, filename: "#{@variant.blob.filename}"

I'm confident these features will be improved in the future, but as of 5.2.0, this is still a pain.

I Think There's Some Kind of Bug in Safari Related to XMLHTTPRequests And Rails ActiveStorage Forms

If you have a form with a file field in it, and that field is using direct upload, and you don't choose a file but then submit the form, Safari sends a bad request. It seems that it fails to send all the data. Nginx reports client prematurely closed stream: only X out of Y bytes of request body received. It all works perfectly in Chrome every time. Maybe this is just a Safari (11.1) bug, but I have not been able to find a fix. For now, our forms all exist in our admin control panel, so we can work around this. I posted on Stack Overflow about this.

Tick, Tock

As I lay upon my bed
With family gathered there around
I found myself transported by
a long-heard faithful sound

A tick and tock I'd heard so long
A sound that never ceased
A friend that with me ever was
From south to north, from west to east

I came to own it second hand
Out of the darkness of great pain
My father, as he breathed his last
Said, "Son, your loss is yet your gain."

With motion slow, with painful breath
He fumbled o'er it with his fingers
But managed to remove the clasp
A moment that in mind still lingers

"I give it to you now my son"
He struggled then to say
And so I put it on my wrist
Where it has ticked until this day.

Thus it came to be my own
Through flood of aching tears
And brave companion it has been
In ventures o'er these many years

We saw the seven wonders and
Discovered many others too
From Greece to Egypt, Amazon
By foot, by horse, by train, canoe

'Twas with me on the day I wed
Tick-tocking at my side
It, e'er faithful, soldiered on
That day I kissed my bride

'Twas with me when the children came
Though it suffered at their small hands
Scratches, dings, and dents came often
And one sad day I broke the band

But even with a broken band
That watch did never stop
I held it up against my ear
I listened close: Tick, Tock

So she took it to the watchmak'r
He brought it back to life
Polished, oiled, the strap re-sewn
A gift, renewed, by my sweet wife.

No battery it e'er required
No charging over night
It's ticked each hour all decades hence
It tocks without an end in sight

Gives no complaint, just does its work
Can there more faithful friend be found?
A steadfast help my life throughout
It needn't even be hand-wound

From highest highs to lowest lows
From summer's beach to winter's sled
All my life this watch has ticked
Which leads us back to this small bed

As memories of the past
Fell to darkness one by one
I fumbled to remove the clasp
And hand it to my own dear son

"Take it son" I tried to say
But little more I did than wheeze
So after no small pause, a breath
The second time, it came with ease

Looking now, he smiled at me
Through tears that trickled fast
"I'll wear it every day," he said
I smiled and knew: my time had passed

It was from then mere seconds hence
My heart could no more effort make
Yet beats continued on his wrist
From spring who's coil no time will break

He's worn it now, for many years
It shows no signs of wearing down
Faithful to him as 'twas to me
From dial to hands, from stem to crown

And one day soon t'will be his time
The watch-clasp to unlock
To place onto another's wrist
The immortal tick and tock

Ulysses Journaling Workflow Script

I've been using Ulysses to journal for about 4 months now. Prior to that I was using Day One, but I've since attemped to move all of my writing into Ulysses.

For a while I was just copy/pasting a previous entry as a starting point for a new post. That was tedious, and I missed the meta data that Day One included in a post by default, primarily location and weather. So, I put together a Workflow script to create a new journal entry in Ulysses for me.

Here's what it looks like on my iPad:

The version that includes weather requires an API key from Dark Sky. While free, not everyone is up for getting an API key just to create a journal entry, so I've created a separate workflow for sharing that doesn't use weather data in the post template.

In order to use either workflow, you'll need a top-level group named "Journal" in Ulysses.

You can grab either workflow here, if they'd be of use to you:

Ulysses Journal Template Workflow (With Weather)

Ulysses Journal Template Workflow (Without Weather)

The Hornsbury Letters

A few months ago I stumbled across an old Royal Mercury typewriter at a local thrift store for $8. I cleaned it up, bought a new ribbon, and got inspired: I sat down in front of it, started typing, and this came out.

The Hornsbury Letters is a fun little project. You can find each page of this epistolary at, or on Facebook, Twitter, and Instagram. New letters are posted a couple of times a week.