Something ago I wrote about the problems which arise when using models in your Rails migrations. Meanwhile I developed a really simple solution to this problem and today I wrapped it up in to a plugin called SafeDataMigrations.
How to use it? Install it in your Rails application:
ruby script/plugin install http://svn.remvee.net/plugins/safe_data_migrations
and apply it in your migration:
class DowncaseUserNames < ActiveRecord::Migration
models :user
def self.up
User.find(:all).each do |user|
user.update_attributes(:names, user.name.downcase)
end
end
Look at the README file for a more elaborate example.
So how does it work? It simply undefines the models your referring to and redefines an empty ActiveRecord class;
Object.send :remove_const, :User rescue nil
class User < ActiveRecord::Base; end
Now you are sure to have a User
model available in your migration without any validations which may make data manipulations impossible. The undefining of an already available model also ensures you don’t need to use ActiveRecord::Base.reset_column_information
before updating new fields, unless you use your model before altering the table of course.
Update: As coderrr points out you don’t need to clobber the global scope model class because a nested model works fine too. I wrongly assumed introducing a new model class in side (!) a migration class would only reopen my original model class and keep validations intact. To illustrate:
class User; def top; end; end
class Migration
class User; def nested; end; end
def self.go
p User.instance_methods - Object.instance_methods
end
end
Migration.go
yields ["nested"]
and not ["top", "nested"]
as I suspected. Apparently I was bitten too hard by a problem which arose when I used an original model class to even try the above. I’ll pull the plugin because it’s pointless.. Bad me, thanks coderrr!