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
end
Notice how our two classes were namespaced into the migration class, so Vendor
becomes AddCurrentToVendor::Vendor
.