ActiveRecord and DB Migration Ate My Model Attributes!

on
| 2 min read

Update: You might also want to check out reset_column_information.

So a few days ago we started seeing the following errors on our Jenkins builds (swapped with fictional model and attribute names):

NoMethodError:
  undefined method `attack_power=' for #<Ironman:0x00000008525d20>

attack_power is a new attribute we recently added to the Ironman ActiveRecord model.

I was baffled, as the table column is clearly there but ActiveRecord couldn’t see it.

This weird behaviour is confirmed by debugging the model:

Ironman
# => Ironman(id: integer, created_at: datetime, updated_at: datetime)

Ironman.column_names
# => ["id", "created_at", "updated_at"]

And by debugging the schema:

ActiveRecord::Base.connection.structure_dump
# => "CREATE TABLE `ironmans` (\n `id` int(11) NOT NULL AUTO_INCREMENT,\n `created_at` datetime NOT NULL,\n `updated_at` datetime NOT NULL,\n `attack_power` int(11) NOT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;\n\n"

So apparently somehow ActiveRecord ate our attack_power attribute!

After some struggling and head-scratching, finally I’ve discovered that during the migration if we call the Ironman class, then any subsequent attribute changes to the class will not be recognised by ActiveRecord.

It turns out, because we run our test suites by invoking rake tasks:

Rake::Task['spec:something'].invoke
Rake::Task['spec:something_else'].invoke

And we manually reset our database beforehand too within the same Rakefile:

Rake::Task['db:reset'].invoke

ActiveRecord will cache its attributes as soon as a model class (Ironman) is called during the migration.

The fix? Simple! Simply use another process to run the database reset:

system 'rake db:reset'

Enjoy what you are reading? Sign up for a better experience on Persumi.

Comments