How do you make remove_column reversible?

user1454117 picture user1454117 · Jul 2, 2014 · Viewed 15.8k times · Source

I have a migration that removes a column:

def change
  remove_column :foos, :bar, :boolean
end

When I try to rake db:rollback that migration, I get the following error:

remove_column is only reversible if given a type.

The ActiveRecord::Migration documentation says that the following is the signature for remove_column:

remove_column(table_name, column_name, type, options)

So my type in this case should be :boolean, and I expect that migration to be reversible. What am I missing?

I can certainly break this out into an up and down migration to avoid this problem, but I'd like to understand why the change syntax isn't working in this case.

Answer

pdobb picture pdobb · Jul 2, 2014

Simply adding the 3rd argument (the column's :type) to the remove_column method makes that migration reversible. So the OP's original code actually did work, as in:

remove_column :foos, :bar, :boolean

The rest of this answer was an attempt to discover why this method would not have been working, but the OP ended up getting it to work.


I see somewhat contrary info in the documentation for ActiveRecord::Migration:

Some commands like remove_column cannot be reversed. If you care to define how to move up and down in these cases, you should define the up and down methods as before.

For a list of commands that are reversible, please see ActiveRecord::Migration::CommandRecorder.

And this from ActiveRecord::Migration::CommandRecorder:

ActiveRecord::Migration::CommandRecorder records commands done during a migration and knows how to reverse those commands. The CommandRecorder knows how to invert the following commands:

add_column

add_index

add_timestamps

create_table

create_join_table

remove_timestamps

rename_column

rename_index

rename_table

Anyway, it appears that this documentation is out of date... Digging into the source on github:

The method that's giving you grief is:

def invert_remove_column(args)
  raise ActiveRecord::IrreversibleMigration, "remove_column is only reversible if given a type." if args.size <= 2
  super
end

I gave this a shot... setup a migration on my Rails 4.1.2 app and the migration worked both ways -- up and down. Here was my migration:

class TestRemoveColumn < ActiveRecord::Migration
  def change
    remove_column :contacts, :test, :boolean
  end
end

I also tried with the :boolean argument missing and got the same error as you're talking about. Are you sure you're on the final version of Rails 4.1.2 -- not one of the release candidates? If you are, I'd suggest putting a binding.pry into the Rails source for the invert_remove_column method to inspect the arguments list and see what's going on. To do so, just run bundle open activerecord and then explore to: lib/active_record/migration/command_recorder.rb:128.