How to use models in your migrations (without killing kittens)
Sometimes a migration needs to do more than add or remove a column. When you must convert existing data, migrations can get nasty because SQL is all you can use. You can never call models in a migration because your models will evolve away from your migrations and everyone will die. Seriously, don't do that. Think of the children!
Fortunately, there is a way to use ActiveRecord magic in your migrations and still get the girl. By inlining classes you decouple your migration from any future changes in your models. Here is an example:
Say you have two classes Article and Vendor. Each Article has_many :vendors. Now you want to introduce a boolean flag current to your Vendor class, which determines whether the article is currently being procured from that vendor.
But what about the 14000 articles already in the database? You call up your client and decide on the following migration rule: The first vendor created for an article is to be flagged as "current".
While you can express that rule in pure SQL, you'd much rather use vanilla Ruby. So you write your migration like this:
class AddCurrentToVendor < ActiveRecord::Migration
class Vendor < ActiveRecord::Base
end
class Article < ActiveRecord::Base
has_many :vendors, :class_name => 'AddCurrentToVendor::Vendor', :order => 'created_at'
end
def self.up
add_column :vendors, :current, :boolean
Article.all.each do |article|
article.vendors.first.andand.update_attribute(:current, true)
end
end
def self.down
remove_column :vendors, :current
end
endNotice how our two classes were namespaced into the migration class, so Vendor becomes AddCurrentToVendor::Vendor.
You can follow any response to this post through the Atom feed.


