It still gets to my nerves...

Submitted by gwolf on Fri, 07/06/2007 - 21:46
I've gradually become happier and happier with Ruby on Rails. I've got mostly past the constant "WTF?WTF?WTF?" phase that's so frustrating when you try to understand the magic behind the scenes (after all, I like understanding what happens inside a framework - the bang and the bling are not what lured me into Rails).
So I've tried to become a more idiomatic, more complete. In the last project I started, I decided to -gasp- stop declaring my schema structure in SQL, and use migrations instead. All fine until I reached migration 008... The first one including a table modification (adding a admin column for users) instead of a creation. Well, that one went pretty fine, but 009 was also a table modification (adding an order column for tsgs). And I updated my server - with both migrations at the same time.
Bad news: Tsg has_many :users, and every User validates_associated :tsg as it should. And as migration 8 modifies (and saves, of course) a User, this validation was triggered... So, migration 8 died complaining about an undefined method `order' for #<Tsg:0x2b2d2dd8d0d0>. WTF?WTF?WTF? all over again. It took me quite a while to realize.
What's the moral of the story? That a sleepy programmer can and will mess up. That batching up several migrations does not guarantee success, because the model is already up to the newest version. Crap. :-/
Or maybe it will just be safer if I abstain from directly instantiating models in migrations? umh... Does not feel right :-/
( categories: )
gwolf's picture

In case somebody lands here...

Just commenting, as I'm wiser (at least in that regard) than I was in 06/2007. Yes, you can use models in migrations - but you have to keep them as minimal as possible. I don't have the code for the Tsg system handy, but it sounds like I should have done this in my 008 migration:

  1. class User < ActiveRecord::Base; end
  2. class SomeMigration
  3. def self.up
  4. add_column :users, :admin, :boolean
  5. User.find(:all).map {|u| u.admin=false;u.save!}
  6. end
  7. def self.down
  8. remove_column :users, :admin
  9. end
  10. end

That means, User gets all the needed knowledge to work with itself as an ActiveRecord::Base object, without tripping over the validations. Of course, as the application developer, I should be sure this validation results in valid objects.