Caching thumbnailer for middleman
In my ruby on rails projects I always use the dragonfly gem. It’s a great gem that gives you the desired flexibility to generate thumbnails on the fly, and not having to pregenerate the thumbnails aforehand.
Lately I have been tinkering around with middleman, a gem for compiling static sites, which really caught my eye. It’s written by the guys over at thoughtbot. It’s a great toolkit that gives you many of the tools from the ruby on rails stack, and from the padrino framework.
For generating thumbnails with dragonfly in middleman, I started using this gem: https://github.com/scarypine/middleman-dragonfly_thumbnailer
It’s a great extension, that allows you to display thumbnails seamlessly in development and production mode in middleman. It uses dragonfly and a similar syntax that ruby on rails developers will recognize from their normal workflow. Thanks to the developer I could use dragonfly together with middleman. The only thing that bugged me about this extension, is that it generates the thumbnails on every request (in development mode) and on every build (in production). That makes for slower requests in development and optionally a lengthy build process in production.
So i decided to clone this extension and build my own version, that keeps your thumbnails in a seperate directory. Caching them to disk to save time when generating the site in development or building in production. And when i say ‘caching’, of course I am NOT talking about http caching. Caching to disk.
It stores the thumbnails in the thumbs
dir at the root of your project. If you disagree with this way of working, you can easily change that, by changing the thumbs_path
function. When the thumbnail exists, it uses it, otherwise it is generated and saved to the thumbs
dir. If you want to regenerate the thumbnails from scratch, you just delete the thumbnails in question from the thumbs
dir. In development the raw b64 code is used in the src attribute of the images, and when building, the thumbs are copied to the build dir, and the src attribute uses the path to the image.
By reusing the thumbnails you save an incredible amount of time when building and the request become way faster in development. YAY! The only caveat is that - as mentioned above - you have to delete the thumbnails by hand, if you want to regenerate them. Seems like a good trade-off to me. And of course request or build will be slow because all thumbnails have to be generated.
NB: The extension uses the dragonfly gem, so you have to add this gem to your Gemfile.
To use the extension, put the code below in the file lib/caching_thumbnailer.rb
. If the lib
dir does not exist in your project, create it.
require 'dragonfly'
module Middleman
module CachingThumbnailer
class Extension < Middleman::Extension
attr_accessor :images
def initialize(app, options_hash = {}, &block)
super
@images = []
configure_dragonfly
FileUtils.mkdir(thumbs_path) unless File.directory?(thumbs_path)
end
def identifier(path, geometry)
"#{path}@#{geometry}"
end
def absolute_source_path(path)
File.join(app.config[:source], app.config[:images_dir], path)
end
def build_path(path, geometry)
dir = File.dirname(path)
subdir = geometry.gsub(/[^a-zA-Z0-9\-]/, '')
File.join(dir, subdir, File.basename(path))
end
def thumbs_path
"thumbs"
end
def absolute_thumb_path(path, geometry)
File.join(thumbs_path, build_path(path, geometry))
end
def absolute_build_path(path, geometry)
File.join(app.config[:build_dir], app.config[:images_dir], build_path(path, geometry))
end
def thumb(path, geometry)
src_path = absolute_source_path(path)
thumb_path = absolute_thumb_path(path, geometry)
return unless File.exist?(src_path)
unless File.exist?(thumb_path)
image = ::Dragonfly.app.fetch_file(src_path)
image = image.thumb(geometry)
image.to_file(thumb_path).close
else
image = ::Dragonfly.app.fetch_file(thumb_path)
end
id = identifier(path, geometry)
images << id unless images.include?(id)
image
end
def after_build(builder)
images.each do |image|
path, geometry = image.split("@")
src_path = absolute_thumb_path(path, geometry)
dest_path = absolute_build_path(path, geometry)
builder.say_status :create, dest_path
dir_path = File.dirname(dest_path)
FileUtils.mkdir_p(dir_path)
FileUtils.copy(src_path, dest_path)
end
end
helpers do
def thumb_tag(path, geometry, options = {})
image = extensions[:caching_thumbnailer].thumb(path, geometry)
return unless image
if environment == :development
url = image.b64_data
else
url = extensions[:caching_thumbnailer].build_path(path, geometry)
end
image_tag(url, options)
end
end
private
def configure_dragonfly
::Dragonfly.app.configure do
datastore :memory
plugin :imagemagick
end
end
end
end
end
::Middleman::Extensions.register(
:caching_thumbnailer,
Middleman::CachingThumbnailer::Extension
)
Then in your config.rb
add this line (at the top):
require 'lib/caching_thumbnailer'
and also this (to activate the extension):
activate :caching_thumbnailer
Have fun with middleman and dragonfly!