Sunday, January 15, 2012

My Blog Has Moved

I will no longer be posting at this blog. The posts and comments will remain intact, but all new posts will happen at by new site:

Sunday, September 4, 2011

Recreate a single version of a CarrierWave::Uploader file

CarrierWave supports a simple way to recreate all the versions of an uploaded file. This comes in handy when you add a new version to your uploader, or when you change the parameters of one of them. For example, assume you have a Post class, with a CarrierWave mounted uploader on the photo column, and you have version called thumb. If you decide to change the resolution of the thumb version, and need to go back and regenerate all existing thumbs, you can do something like this:

Post.all.each do |post|!

What if you have 5 different versions on this Uploader, and you only changed one of them? The above method will download all the versions of every Post#photo from storage (think S3 or Cloud Files), regenerate all of them - even the ones that haven't changed - and re-upload all of them to storage.

That's a lot of wasted time, CPU, and bandwidth.

To address this, I've created a simple monkey patch for CarrierWave to provide a new method called recreate_version!. This method works just like recreate_versions!, except that it takes a parameter specifying a single version to recreate, rather than recreating all of them.

To use this, first drop this monkey-patch in somewhere that it will get loaded. Above your Uploader class will suffice.

Now your script to recompute all the thumbs can look like this:

Post.all.each do |post|!(:thumb)

This will recreate and re-upload only the thumb versions of your Post#photo uploads.

As an added side benefit, the recreate_version! method cleans up after itself. The recreate_versions! method downloads into a temporary cache dir, but leaves it up to the caller to clean that up. This new method assumes that if it had to download and cache the uploaded file and its versions, then it should clean them up immediately.

This method has turned out to save me a lot of time in regenerating large numbers of uploaded file versions. If there's any interest in this, I might work up a pull request with test cases and try to get it into the mainline CarrierWave gem release.

Tuesday, August 23, 2011

Adding filters to stock Devise controllers

Have some before_filters or after_filters you want to add to some or all of the Devise controller actions without having to subclass all the Devise controllers?

Here's a little trick I use: directly call the before_filter or after_filter class methods in the devise.rb initializer. For example, I have a prepare_for_mobile method that I want to apply as a before_filter to all of the Devise controller actions. This is what I have at the bottom of config/initializers/devise.rb:

Note that this is exactly the same as if you had added the before_filter call directly inside these controllers. That means if you wanted to be more selective, you could just as easily do something like:

IMPORTANT NOTE: This tactic only works reliably in the production environment. That is because in development mode, the Devise controller classes get reloaded on each request, but this config file that we've edited does not.

UPDATED 2/16/2012: See my follow-up post where I revisit this issue with a solution that works in both production and development environments.