How to migrate a Rails app from using Attachment_fu to Paperclip

I’ve just had to migrate a Rails app from using Attachment_fu to Paperclip and I decided I’d better document the process.

Most importantly, the app has a couple of thousand photos and I’m going to have to make the live transition as smooth as possible so it’s important that I can do all the development without affecting the live app and then I need to move all the Amazon S3 files using access to the original Attachment_fu model information and then go live with the Paperclip code.

Creating a copy of the Amazon S3 bucket

First step is to create a copy of the live S3 bucket which is important for testing. I have written a set of rake tasks to do most of the heavy lifting.
All of the S3 work is done using right_aws which is needed by Paperclip and can be installed by running:

gem install right_aws

To create a copy of your S3 bucket, run

rake utils:attachments:copy_s3_bucket FROM=production_bucket_name TO=backup_bucket_name

Moving the Amazon S3 keys from Attachment_fu scheme to Paperclips

Paperclip supports a completely configurable naming scheme while Attachment_fu is prescribed.

I wrote a rake task to physically move the keys around so that they matched my required Paperclip scheme. Initially I performed this task on the backup bucket so that I could begin writing code against the backup bucket.

The paperclip naming scheme I’m using is :id/:style.:extension

The migration is also handled by a rake task utils:attachments:migrate_attachment_fu_to_paperclip

This rake task is specific to the code being refactored and you will need to do some editing.

  • Line 63 - Change Klass to the class of your model, in my case it is Photo
  • Lines 75 & 83 - Change the format of the new_key_name if you want to use a naming scheme other than :id/:style.:extension

The rake task must then be run (do it first on your backup bucket)

rake utils:attachments:migrate_attachment_fu_to_paperclip BUCKET=backup_bucket_name

Changing the code

Now time to change the code itself. There are links to various gist files in the resources section at the end of this article.

Migration

First we’ll need a migration to change the model table format. The migration is specific to my Photo model so you’ll have to change both the model and table references to your specific project details.

The migration first renames the re-usable columns such as filename, content_type and size and then removes the columns that paperclip doesn’t use/support. Finally the migration removes all of Attachment_fu’s child entries from the database table.

Model

This is where everything gets tricky and we have to be careful not to mess up the order.

We have created a backup of our S3 bucket and now we have run a migration on our development database. The migration uses the Attachment_fu model information which we are now going to change. If you try to run the migration after you’ve changed the model, things won’t work so remember this for when the changes go live.

View the diff of my changes here: http://gist.github.com/42525

Controller

In your controller, Attachment_fu relies on a single attribute uploaded_data where Paperclip relies on an attribute the same as your attachment, in my case photo.

owner.photo.create(params[:uploaded_data])

becomes

owner.photo = params[:owner][:photo]
owner.save

or, simply

owner = Owner.create(params[:owner])

Going live

OK, so you’ve finished working on development and you’re happy that everything works as intended against your backup S3 bucket. Now it’s time for the nail-biting push to live.

Do everything in the following order (Please don’t try to  do any of these steps at the same time - I did and it bit me!):

  1. Backup your production database
  2. Upload just your rake task to your server
  3. Run the following rake tasks:
    • rake utils:attachments:clear_s3_bucket BUCKET=backup_bucket_name - Clears out all the files you’ve been testing with.
    • rake utils:attachments:copy_s3_bucket FROM=production_bucket_name TO=backup_bucket_name - Re-backs up the production files (just in case)
    • rake utils:attachments:migrate_attachment_fu_to_paperclip BUCKET=production_bucket_name - Does the migration on the production S3 bucket
  4. Upload your migration to the live server
  5. Run your migration
  6. Run your normal deployment to get the new files on the server
  7. Congratulations! You’ve done it.

Rolling back

Should anything go wrong, you simply do the following:

  1. Run the rake tasks
    • rake utils:attachments:clear_s3_bucket BUCKET=production_bucket_name - Clears out all the files that are in the wrong format.
    • rake utils:attachments:copy_s3_bucket FROM=backup_bucket_name TO=production_bucket_name - Restores the production files from the backup bucket (this is why we did all that work :-))
  2. Restore your production database
  3. Restore your previous code

Resources

Final words

This was quite tricky and I only hope that my code and examples help a little. If you have any questions, feel free to leave a comment and I’ll help where I can.