0

I have a users table that contains a bunch of IPS in string format, now we decided to start storing IPS as integers, so I added this piece of code to my user.rb model :

class User < ActiveRecord::Base
  [:current_sign_in_ip, :last_sign_in_ip].each do |field|
    define_method(field) do
      ip = read_attribute(field)
      return nil unless ip
      ip += 4_294_967_296 if ip < 0 # Convert from 2's complement
      "#{(ip & 0xFF000000) >> 24}.#{(ip & 0x00FF0000) >> 16}.#{(ip & 0x0000FF00) >> 8}.#{ip & 0x000000FF}"
    end

    define_method("#{field}=") do |value|
      quads = value.split('.')
      if quads.length == 4
        as_int = (quads[0].to_i * (2**24)) + (quads[1].to_i * (2**16)) + (quads[2].to_i * (2**8)) + quads[3].to_i
        as_int -= 4_294_967_296 if as_int > 2147483647 # Convert to 2's complement
      else
        as_int = nil
      end
      write_attribute(field, as_int)
    end

  end
end

This code works fine, when a new user registers, his/her IP will be stored as integer. Now I need to create a migration that will change the type of the IP columns from string to integer :

class ConvertStringIpsToIntegers < ActiveRecord::Migration

def up

  change_column :users, :current_sign_in_ip, :integer
  change_column :users, :last_sign_in_ip,    :integer
end

def down
  change_column :users, :current_sign_in_ip, :string
  change_column :users, :last_sign_in_ip,    :string
end

end

However this migration will destroy the old IPS that were stored as strings "127.0.0.1" will become 127 after the migration. Any idea how I can convert all existing IPS from strings to integers before running the migration?

Thanks

Ahmad Al-kheat
  • 1,805
  • 2
  • 16
  • 25
  • this was answered here I believe http://stackoverflow.com/questions/13244792/convert-ip-address-to-32-bit-integer-in-ruby Edit: ok I can't comment yet. You can migrate the field as string first. Convert the IP to number and then do a second migration from string field to numeric – webmastersupport.com Mar 21 '15 at 22:14
  • No sorry, my problem is different. I know how to convert I just don't know where to put the code. – Ahmad Al-kheat Mar 21 '15 at 22:15
  • @webmastersupport.com if I migrate to strings the IPS will be lost. – Ahmad Al-kheat Mar 21 '15 at 22:25

1 Answers1

1

You can include your conversion code in a migration. Do the actual conversion from string into integer in a separate conversion.

def up
  User.all.each do
    [:current_sign_in_ip, :last_sign_in_ip].each do |field|
      quads = user.read_attribute(field).split('.')
      if quads.length == 4
        as_int = (quads[0].to_i * (2**24)) + (quads[1].to_i * (2**16)) + (quads[2].to_i * (2**8)) + quads[3].to_i
        as_int -= 4_294_967_296 if as_int > 2147483647 # Convert to 2's complement
      else
        as_int = nil
      end
      user.write_attribute(field, "#{as_int}")
    end
  end
end

def down
  User.all.each do |user|
    [:current_sign_in_ip, :last_sign_in_ip].each do |field|
      ip = user.read_attribute(field).to_i
      return nil unless ip
      ip += 4_294_967_296 if ip < 0 # Convert from 2's complement
      user.write_attribute(field, "#{(ip & 0xFF000000) >> 24}.#{(ip & 0x00FF0000) >> 16}.#{(ip & 0x0000FF00) >> 8}.#{ip & 0x000000FF}")
    end
  end
end
SteveTurczyn
  • 36,057
  • 6
  • 41
  • 53