I'm doing a migration where I need to both (a) change a column's data type and (b) preserve the data in that column (ie, convert it to the new data type and keep it in the same column). The new column type requires me to override the getters and setters and this causes the migration to fail.
There is a User table that includes a field for the IP address of the user's connection: ip_addr
. This is currently a string and I want to change it to integer. The up
method of the migration looks like this:
def up
add_column :users, :curip, :integer
User.reset_column_information
User.all.each do |u|
u.update_attribute :curip, User.ip_str_to_int(u.ip_addr)
end
remove_column :users, :ip_addr
rename_column :users, :curip, :ip_addr
end
(User.ip_str_to_int
does the math to convert the IP address quads to an integer.)
I also have methods that override the getter & setter for ip_addr
to call User.ip_str_to_int
and the corresponding method User.ip_int_to_str
. These methods look something like this:
def ip_addr
val = read_attribute(:ip_addr)
User.ip_int_to_str(val)
end
def ip_addr=(val)
write_attribute(:ip_addr, User.ip_str_to_int(val))
end
By now you've probably guessed the problem. When the migration runs, it chokes, because the overridden getter/setters expect the column to contain an integer, but at the time that the migration happens, the column actually contains a string. I've tested the migration and getter/setter methods separately, and they're all fine. For my own development environment, I can simply comment out the getters/setters to run the migration, then put them back in. But this is a kludge and doesn't work for production.
Any suggestions? I know that I wouldn't have this problem if I wasn't trying to keep the same column name, but changing the column name means changing other code.
Environment: sqlite 3; Ruby 2.1.4; Rails 3.2.13.
You could modify the getter method to handle the case of data being a string...
def ip_addr
val = read_attribute(:ip_addr)
return val if val.is_a? String
User.ip_int_to_str(val)
end