Rack::Thumb: Drop-in image thumbnailing for your Rack stack

Rack::Thumb is drop-in dynamic thumbnailing middleware for Rack-based applications, featuring simple configuration, optional security (via url-signing), and maximum flexibility.

Getting Started

You will need ImageMagick and the Mapel gem (github.com/akdubya/mapel).

gem install rack-thumb

# rackup.ru
require 'myapp'
require 'rack/thumb'

use Rack::Thumb
use Rack::Static, :urls => ["/media"]

run MyApp.new

Rack::Thumb is file-server agnostic to provide maximum deployment flexibility. Simply set it up in front of any application that’s capable of serving source images (I’m using it with an app that serves images from CouchDB).

See the example directory for more Rack configurations. Because thumbnailing is an expensive operation, you should run Rack::Thumb behind a cache, such as the excellent Rack::Cache.

Rendering Options

Rack::Thumb intercepts requests for images that have urls of the form /path/to/image_{metadata}.ext and returns rendered thumbnails. Rendering options include width, height and gravity. If both width and height are supplied, images are cropped and resized to fit the aspect ratio.

Link to thumbnails from your templates as follows:

/media/foobar_50x50.jpg     # => Crop and resize to 50x50
/media/foobar_50x50-nw.jpg  # => Crop and resize with northwest gravity
/media/foobar_50x.jpg       # => Resize to a width of 50, preserving AR
/media/foobar_x50.jpg       # => Resize to a height of 50, preserving AR

URL Signing

To prevent pesky end-users and bots from flooding your application with render requests you can set up Rack::Thumb to check for a SHA-1 signature that is unique to every url. Using this option, only thumbnails requested by your templates will be valid. Example:

use Rack::Thumb, {
  :secret => "My secret",   # => Don't tell anyone!
  :keylength => "16"        # => Only use 16 digits of the SHA-1 key
}

You can then use your secret to generate secure links in your templates using Ruby’s built-in Digest::SHA1 library:

/media/foobar_50x100-sw-a267c193a7eff046.jpg  # => Successful
/media/foobar_120x250-a267c193a7eff046.jpg    # => Returns a bad request error

There are no helper modules just yet but it’s easy enough to roll your own.

Deep Thoughts

Rack::Thumb respects any extra headers you set in your downstream app. You are free to set caching policies, etc. however you like. Incoming HEAD requests skip the rendering step.

There are a decent number of specs but the middleware isn’t very strict at checking setup options at the moment, and I’m sure there are a few edge cases that need to be looked into. Comments, suggestions and bug reports are welcome.

Meta

Written by Aleks Williams (github.com/akdubya)

Credit goes to the repoze.bitblt (pypi.python.org/pypi/repoze.bitblt) team for the clever url-signing implementation. My original security scheme was stupidly complex.

Released under the MIT License: www.opensource.org/licenses/mit-license.php

github.com/akdubya/rack-thumb