SuperPumpup (dot com)
General awesomeness may be found here.

10 December 2013

Paperclip and The Legend of Zelda

Figuring out where a Paperclip attachment is a bit like playing the Legend of Zelda sometimes.

These are things you need to figure out, from three different sources:

1 - The filename of the actual file (face.png) - source your database

1a - The name of the model and model id that you have attached this to. This is different information, but generally in the DB

2 - The pathing strategy that Paperclip uses - this is typically in a global paperclip config file (maybe config/initializers/paperclip.rb)

3 - Where the eff in the cloud it is - this is the biggest pain in the butt there. In a sloppily-configured prod setup, you'll be lucky enough to have this in a file like s3.yml or maybe paperclip.yml. But with good configuration-management, that file will only live on the server it's supposed to because, you know, security.

So once you have all three of these pieces of information from different sources, and have rescued the princess, what can you build? A string. The information to construct a string that's like 100 characters long is scattered across the corners of your application's world and that's just nuts.

WORSE! If someone ever decides to touch corner 2 of the triforce there and change anything about how paths are calculated, your application just forgets where to find its attachments. All your links are broken, and you're off trying to figure out how to set up "compatibility modes" for some of the files. Because apparently that string changed? Wut? The location of this file is mutable all of a sudden? (facepalm)

This came up for me as I'm building a Persistence as a Service module for an application and I started putting in Carrierwave (since it's a touch saner than Paperclip) and then realized, "Self, wtf are you doing?" Shouldn't this just be holding a string? And maybe (maybe?!) aren't you using a modern database that will let you have a field hold an array of strings? And can't that array of strings hold all of the immutable data?

Yep. So that's what I've got now. If someone wants the Foo object to hold some attachments, they send something like this:

"foo": {
  "name": "The ultimate Foo",
  "attachments": [
    {
      "type": "image",
      "image_content_type": "image/png",
      "small_url": "some_absolute_path_on_s3/picture_small.png",
      "medium_url": "some_absolute_path_on_s3/picture_medium.png",
      "large_url": "some_absolute_path_on_s3/picture_large.png",
      "original_url": "some_absolute_path_on_s3/picture_small.png"
    }
  ]
}

So there we go. That's at that end, but why can't that level of treatment of the location of these files as immutable be pushed down into the actual image processing library? What if I decide to start saving new pictures in a different bucket? Can't my database just point two different things at two different buckets? What if I want to push some of the files immediately out to a CDN and only reference that location of theirs? Piece of cake! It's just strings! URI's are just strings. The only thing your application is ever going to care about in most cases is "what is the URI of this file." So keep those concerns separate.

Categories: Ruby on Rails