Forum: Ruby on Rails Avoiding reflection errors in migrations

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
A158d537e62cea11c9415df699136ac7?d=identicon&s=25 andrew chase (Guest)
on 2007-07-03 23:46
(Received via mailing list)
I've hit an issue a couple times now with migrations that I've solved
by hacking around, but now I'm interested in finding a more robust
solution.

The problem comes when a migration contains some logic that requires
the use of some class. However, within that class a new dependency has
been added that relies on a migration that comes after the current
one. Here's a simple exmaple of what I mean:

--- test_class.rb
class Test < ActiveRecord::Base
  has_many :foo
  def self.some_method
    # do something useful
  end
end

---- 002_update_test_class.rb
class UpdateTest < ActiveRecord::Migration
  def self.up
     add_column :tests, :my_column, :integer
     Test.some_method
  end
end

--- 003_update_test_again.rb
class UpdateTestAgain < ActiveRecord::Migration
  def self.up
    create_table(:foos) do
      # stuff
    end
  end
end


Running all migrations would result in the following error:
Mysql::Error: #42S02Table 'my_table.foos' doesn't exist: SHOW FIELDS
FROM foos

So you see, the Test class is in an invalid state (it's dependencies
don't exist yet) when I'm calling it from UpdateTest migration, and it
will not be valid until the create_table happens in the next
migration.

Have other folks come across this? I'd be interested in hearing what
solutions others have employed. One thing I've been thinking is that
migrations could be separate out into two separate pieces: schema
changes and content changes. So you'd have something like "up_logic"
as well as the standard "up" and after running through the "up"s for
all migrations rake would then make a second pass running all the
up_logic methods. Stupid? Useful? Misguided?

Thanks,
Andrew
1a3664ebe4628cc6a92d3777b7a9ffd1?d=identicon&s=25 cheriot@gmail.com (Guest)
on 2007-07-04 03:23
(Received via mailing list)
As a general rule, once I have a migration and the associated code
working I commit my changes. Doing that, you could simply revert your
working copy to the checkout required by the migration. Obviously that
might not be what you're looking for if you will be migrating back and
forth often. If there's another way, I'm not aware of it.

Out of curiosity, what are you doing with your model that's required
for the database to reach a correct state?
971708ecf1c0b3cada6cf98ea58611fd?d=identicon&s=25 Val (Guest)
on 2007-07-04 15:28
(Received via mailing list)
If you drastically change a model, the suggested approach with schema/
logic separation might still fail since the logic might be not valid
anymore. As for the actual solution for that specific problem, it all
depends on which stage you have those migrations and what
transformations do you do in them. For example, if Test.some_method is
harmless enough to run the second time, you can delete it from the
original migration and add a new migration with the transformation
code only right after UpdateTestAgain. If that is not the case, your
options could go from creation of a special model containing the old
model code (similar to the retired model described here
http://revolutiononrails.blogspot.com/2007/05/movi...)
or, if the original migration has been applied to production,
replacing the code with straight SQL loading (see the migration
consolidation here
http://revolutiononrails.blogspot.com/2007/01/db-m...)

Hope it helps.

Val
Ef3aa7f7e577ea8cd620462724ddf73b?d=identicon&s=25 Rob Biedenharn (Guest)
on 2007-07-04 16:22
(Received via mailing list)
On Jul 3, 2007, at 5:45 PM, andrew chase wrote:
> class Test < ActiveRecord::Base
>   has_many :foo
>   def self.some_method
>     # do something useful
>   end
> end
>
> ---- 002_update_test_class.rb
> class UpdateTest < ActiveRecord::Migration

   # You need to add a copy of the Test model
   # right here (yes, inside the migration class).

   class Test < ActiveRecord::Base
     def self.some_method
       # do something useful
     end
   end

>       # stuff
> don't exist yet) when I'm calling it from UpdateTest migration, and it
>
> Thanks,
> Andrew

Doing the migration this way future-proofs it.  You could migrate up
and down by returning all your code to the same point in time, but
the whole point of migrations is that you can drop a project into a
clean environment, create the database, run a db:migrate, and be
ready to go.  I tend to put the minimal bits from the model class
into the migration and then ONLY when the model class actually needs
to be used.  (Often this is to initialize the newly added table/
column.)  You might also need to call Model.reset_column_information
between the time you alter the database table and try to use the
model for something so it knows about the right schema for the table.

-Rob

Rob Biedenharn    http://agileconsultingllc.com
Rob@AgileConsultingLLC.com
This topic is locked and can not be replied to.