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:
To create a copy of your S3 bucket, run
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)
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.
becomes
owner.save
or, simply
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!):
- Backup your production database
- Upload just your rake task to your server
- 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
- Upload your migration to the live server
- Run your migration
- Run your normal deployment to get the new files on the server
- Congratulations! You’ve done it.
Rolling back
Should anything go wrong, you simply do the following:
- 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 :-))
- Restore your production database
- Restore your previous code
Resources
- S3 rake tasks - http://gist.github.com/42523
- Model migration - http://gist.github.com/42524
- Model code diff - http://gist.github.com/42525
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.
