First micro post via Oak

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.

Adventures in Buying a Telescope

I wasn’t sure what I wanted for my birthday. My wishlist had myriad small items, but nothing was a stand-out for me[^1].

A few days after my birthday my eight year old son was sharing a bunch of things he’d learned recently about the Solar System: the mass and orbits of the planets, the size of distant stars, and the influence of black holes. That got me to thinking: surely at this point we must be able to get a decent telescope without breaking the bank. I’d never used a good telescope of any kind in my life even though I’ve been a space-nut for decades. So, I went to the first place I always go when I want to buy something: Amazon[^2].

After some searching and sorting for a good combination of price and customer review I settled on a Celestron 127EQ PowerSeeker (spoiler: please do not buy that telescope). Buy now with 1-Click.

2 days later our packages arrived via UPS. Assembly was fairly simple. Now we just had to wait for sunset.

Sunset arrived and Jupiter was already high enough in the sky to have a good look. I spun the scope around, pointed it at Jupiter, and had a look.

I was stunned. Awestruck. Speechless. Ecstatic.

I have never seen anything so instantly moving and amazing as seeing Jupiter hanging there against the emptiness of space, her four Galilean moons sparkling at her sides like a diamond necklace. I leapt, shouted, and gestured to my wife that she had to look. Wow! Then we had the kids look, I can see it! Wow!

We spent some more time observing the moon, tweaking the mount a bit, and looking at a few of the brighter stars in the sky. We tried all the eyepieces. We tried (not very successfully) to take pictures through the scope with our phones. Now it was getting late for the kids, so we packed it up and took everything inside.

The next night we packed everyone up and went out about a half hour from home to a dark site in the desert. The light pollution is still very noticeable, but improved enough that it’s well worth the drive.

We didn’t have a lot of time, and the kids were getting bored rather quickly, so we didn’t get to stay very long. But, we managed more great views of Jupiter and the Moon.

Two viewing sessions, two victories. That’s when things took a wrong turn.

I thought I might be able to adjust the collimation of the telescope to get a sharper image. Nothing we saw was awful, mind you, but I figured we should see if we could dial it in a bit more to get even better results.

I followed the directions I read online and started adjusting the primary and secondary mirrors. It was really hard to know if I was making the right adjustments during the day because 1) the primary mirror doesn’t come with a center mark and 2) I didn’t have a laser collimator or a collimation eyepiece. But, I went slowly, followed the directions, and got things as accurate as I could with the tools I had.

That night I eagerly took it outside after dark and pointed the telescope at a bright star to do a star test.

Utter. Disaster. Everything was out of focus.

I spent hours that night trying to make small adjustments to fix things, but nothing worked. For three nights I fought and fought with the telescope. I bought a laser collimator, tried to mark the center of the primary lens, and even bought a collimation eyepiece. Nothing worked.

Disheartened, I scoured the internet in search of a solution when I found the r/telescopes subreddit. I posted about my problem and a number of people were kind enough to respond with advice. There was one main theme: if you can return this telescope, do it.

It turns out that the Powerseeker 127eq is a Bird-Jones reflector. This is a type of Newtonian reflector design, but it has an extra lens at the base of the focus tube to allow for a much smaller telescope. It allows wide aperture reflectors to be made at much lower cost. The tradeoff is generally image quality (but not so much that it’s a big deal to those who are in this budget range), and difficulty of maintenance (a big deal).

The bottom line was that even if I was able to fix my focus issues I was always going to have to deal with this frustration when the scope became out of focus (which is inevitable because of temperature changes and the occasional bump).

There’s a great buying guide available on reddit that I highly recommend anyone go through before buying a telescope. I wish I had found it before I made my purchase.

After reading the guide and doing some more research I settled on an Orion XT6. It’s a dobsonian with a 150mm (6”) aperture. Then, I packed up the telescope and nearly all the accessories and sent it all back to Amazon[^3].

The XT6 arrived sooner than I expected. Setup as again easy, but the build quality between the two products was instantly noticeable. The XT6 feels much more solid all around. The mount is extremely stable, solid, with great friction coefficients. After the new scope was all put together we took it outside to have a look.

Gorgeous, crisp, clear views of Jupiter, double stars, the whole works.

Sadly, clouds were moving in, and the weather started to get windy, so our evening was cut short. In spite of that, we managed to get a fuzzy picture of Jupiter out of the phone.

I went out again the next night and got my first look at a galaxy: M81 & M82. They were just two dim smudges on this windy and light-polluted night, but it’s thrilling to know that smudge is actually billions of stars.

Those two nights of seeing revealed several other things that I really like about the XT6 vs the Powerseeker:

  1. More light (i.e. bigger aperture) is better, of course. The first night's conditions were pretty awful (partly cloudy, windy), but viewing was still better than the 127 on a good night.
  2. The red dot finder is so much better than the piece of junk that comes with the 127. The 127 finder was always so frustrating. This is easy to see and stays put.
  3. The alt-az mount is exactly what I didn't know I wanted before.

So far I could hardly be happier. I’ve seen my first Deep Sky Objects, I’ve had super-clear viewing of Jupiter, and I have a scope that will be easy to maintain for decades.

It was a long road to find the right scope, but I’m glad I put up with the hassles. The results are absolutely worth it. If you’re considering the Powerseeker 127eq, let me implore you: buy a dobsonian. You won’t regret it.

[^1]: That’s not to say that I actually need anything. Things often end up owning us, rather than the other way round. In any case, gifts for birthdays seems to be the way of things, and I wasn’t going to avoid it altogether.

[^2]: Turns out this was my first mistake.

[^3]: I dropped my boxed off at the UPS store, then went to lunch. Amazon had refunded my money before I got home. That’s pretty great.


Calculating the Dewpoint in Ruby

I needed this today; maybe it will help someone else.

humidity should be 0-100 temp_c is a float

l = Math.log(humidity / 100.0)
m = 17.27 * temp_c 
n = 237.3 + temp_c 
b = (l + (m / n)) / 17.27
dewpoint_c = (237.3 * b) / (1 - b)