TopicLinkClick: convert 'ip' (bigint) -> 'ip_address' (inet)

When accessed over IPv6, the ip address of the user is a 128-bit number,
too big for PostgreSQL's bigint data type. Since PostgresSQL has the
built-in inet type, which handles both IPv4 and IPv6 addresses, we
should use that instead. Where this is done elsewhere in the codebase,
the column is called ip_address, so we should follow that convention as
well.

This migration uses a SQL command to populate the new field from the old
one, so as not to rely on the TopicLinkClick model class, which should
keep the migration from failing if that class is modified in the future.
This commit is contained in:
Dan Johnson 2013-06-24 18:30:32 -04:00
parent c79ab3fc2e
commit 2e478d8537
4 changed files with 27 additions and 8 deletions

View file

@ -5,10 +5,8 @@ class TopicLinkClick < ActiveRecord::Base
belongs_to :topic_link, counter_cache: :clicks belongs_to :topic_link, counter_cache: :clicks
belongs_to :user belongs_to :user
has_ip_address :ip
validates_presence_of :topic_link_id validates_presence_of :topic_link_id
validates_presence_of :ip validates_presence_of :ip_address
# Create a click from a URL and post_id # Create a click from a URL and post_id
def self.create_from(args={}) def self.create_from(args={})
@ -28,7 +26,7 @@ class TopicLinkClick < ActiveRecord::Base
rate_key = "link-clicks:#{link.id}:#{args[:user_id] || args[:ip]}" rate_key = "link-clicks:#{link.id}:#{args[:user_id] || args[:ip]}"
if $redis.setnx(rate_key, "1") if $redis.setnx(rate_key, "1")
$redis.expire(rate_key, 1.day.to_i) $redis.expire(rate_key, 1.day.to_i)
create!(topic_link_id: link.id, user_id: args[:user_id], ip: args[:ip]) create!(topic_link_id: link.id, user_id: args[:user_id], ip_address: args[:ip])
end end
args[:url] args[:url]
@ -43,9 +41,9 @@ end
# id :integer not null, primary key # id :integer not null, primary key
# topic_link_id :integer not null # topic_link_id :integer not null
# user_id :integer # user_id :integer
# ip :integer not null
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# ip_address :string not null
# #
# Indexes # Indexes
# #

View file

@ -0,0 +1,21 @@
require 'ipaddr'
class ChangeIpToInetInTopicLinkClicks < ActiveRecord::Migration
def up
add_column :topic_link_clicks, :ip_address, :inet
execute "UPDATE topic_link_clicks SET ip_address = inet(
(ip >> 24 & 255) || '.' ||
(ip >> 16 & 255) || '.' ||
(ip >> 8 & 255) || '.' ||
(ip >> 0 & 255)
);"
change_column :topic_link_clicks, :ip_address, :inet, { :null => false }
remove_column :topic_link_clicks, :ip
end
def down
raise ActiveRecord::IrreversibleMigration
end
end

View file

@ -26,7 +26,7 @@ describe TopicLinkClick do
context 'create' do context 'create' do
before do before do
TopicLinkClick.create(topic_link: @topic_link, ip: '192.168.1.1') TopicLinkClick.create(topic_link: @topic_link, ip_address: '192.168.1.1')
end end
it 'creates the forum topic link click' do it 'creates the forum topic link click' do
@ -39,7 +39,7 @@ describe TopicLinkClick do
end end
it 'serializes and deserializes the IP' do it 'serializes and deserializes the IP' do
TopicLinkClick.first.ip.to_s.should == '192.168.1.1' TopicLinkClick.first.ip_address.to_s.should == '192.168.1.1'
end end
end end

View file

@ -236,7 +236,7 @@ describe TopicLink do
it 'has the correct results' do it 'has the correct results' do
TopicLink.extract_from(post) TopicLink.extract_from(post)
topic_link = post.topic.topic_links.first topic_link = post.topic.topic_links.first
TopicLinkClick.create(topic_link: topic_link, ip: '192.168.1.1') TopicLinkClick.create(topic_link: topic_link, ip_address: '192.168.1.1')
counts_for[post.id].should be_present counts_for[post.id].should be_present
counts_for[post.id].find {|l| l[:url] == 'http://google.com'}[:clicks].should == 0 counts_for[post.id].find {|l| l[:url] == 'http://google.com'}[:clicks].should == 0