I'm having some problems with migrations (on rails 1.2.x). I suppose
it is not a rare problem, but my google-foo didn't help me this time.
Briefly, my problem is that when modifying some models I may (and tend
to) do backward-incompatible changes. Method deletion, or signature
changing, for instance. Now, if a past migration used the "old API",
it will surely break when someone do a full migration. I suppose that
this could be solved by modifying old migrations, but (a) I have a gut
feeling against modifying migrations and (b) it won't work when your
backward-incompatible change has something to do with a change of the
database structure, because, when running the old migration the new
structure has not been migrated yet.
Let's put it with a minimal example. I have this model:
class Foo < ActiveRecord::Base
end
class AddAnotherFieldToFoo < ActiveRecord::Migration
def self.up
add_column :foo, :new_column, :string
Foo.reset_column_information
Foo.find(:all).each do |foo|
foo.new_column = some_calculation(foo.another_column)
foo.save!
end
end
end
And later I change the Foo model:
class Foo < ActiveRecord::Base
has_many :bars
before_save :do_something_with_my_bars
def do_something_with_my_bars
...
end
end
And now I have just broke the AddAnotherFieldToFoo migration. This
affects new developers (who will start doing a checkout and a full
migration to get a up-to-date database), existing developers who
hasn't updated they working copy between the two events, and
deployments intro production (as long as they include two incompatible
events as shown on my example).
AFAICS, this is a problem of "keeping in sync" the state of models and
migrations over the time. How do you solve this? Or am I using
migrations in a way they were not intended to be used?
And now I have just broke the AddAnotherFieldToFoo migration. This
affects new developers (who will start doing a checkout and a full
migration to get a up-to-date database), existing developers who
hasn't updated they working copy between the two events, and
deployments intro production (as long as they include two incompatible
events as shown on my example).
They shouldn't be using the migrations. They should be using rake
db:schema:load
AFAICS, this is a problem of "keeping in sync" the state of models and
migrations over the time. How do you solve this? Or am I using
migrations in a way they were not intended to be used?
If you are using models in migrations purely because it beats writing
the sql to insert/change/whatever then it's often wise to redeclare
the model in the migration (just stick class Foo < ActiveRecord::Base;
end somewhere convenient) so that all you get is the activerecord
niceness
> affects new developers (who will start doing a checkout and a full
> migration to get a up-to-date database), existing developers who
> hasn't updated they working copy between the two events, and
> deployments intro production (as long as they include two incompatible
> events as shown on my example).
They shouldn't be using the migrations. They should be using rake
db:schema:load
I'm not sure, at least about existing developers and production
environment.
Using db:schema:load would only migrate schema changes, but not data
change. Think, for example, of a change of a password hashing
algorithm. An even if you could afford loosing data on development
environments, you definitely can't on production.
> AFAICS, this is a problem of "keeping in sync" the state of models and
> migrations over the time. How do you solve this? Or am I using
> migrations in a way they were not intended to be used?
If you are using models in migrations purely because it beats writing
the sql to insert/change/whatever
Not just that. Some data migrations require some logic that is beyond
what plain SQL can offer. Another option would be to use Procedural-
SQL dialects, but I think it's better to do it on Ruby.
then it's often wise to redeclare
the model in the migration (just stick class Foo < ActiveRecord::Base;
end somewhere convenient) so that all you get is the activerecord
niceness
Thanks, that sounds like a good idea!
Of course on real world the model has much more code, and copying it
on the top of the migration might not be elegant. But seems that it
would get the job done. Unless there are dependencies with the API
offered by another, related model(s) inside the copied model. Ugh.