Skip to content

Instantly share code, notes, and snippets.

@digitalex
Created March 12, 2013 15:50
Show Gist options
  • Save digitalex/5144043 to your computer and use it in GitHub Desktop.
Save digitalex/5144043 to your computer and use it in GitHub Desktop.
Based on a list of IP ranges and country (format: "<from> <to> <country>", look up where an IP is located.
class IPLocator
attr_accessor :ranges
def initialize(filename)
self.ranges = []
File.open(filename).each do |line|
if not line.start_with? '#'
start, stop, country = line.split
self.ranges << IPRange.new(start.to_i, stop.to_i, country)
end
end
end
def lookup(ip, fallback = 'ZZ')
ipn = ip.is_a?(String) ? self.class.parse_ip(ip) : ip.to_i
index = self.class.bisect_right(self.ranges, IPRange.new(ipn, nil, nil)) - 1
range = self.ranges[index]
range && range.includes?(ipn) ? range.country.gsub(/ZZ/, fallback) : nil
rescue => ex
Airbrake.notify(ex)
nil
end
def self.parse_ip(ip)
ip.split('.').collect(&:to_i).inject { |memo,x| x + (memo << 8) }
end
def self.bisect_right(a, x)
lo, hi = 0, a.size
while lo < hi
mid = (lo+hi) / 2
if x < a[mid]
hi = mid
else
lo = mid + 1
end
end
return lo
end
class IPRange
include Comparable
attr_accessor :start, :stop, :country
def initialize(start, stop, country)
self.start = start
self.stop = stop
self.country = country
end
def <=>(another)
self.start <=> another.start
end
def includes?(ip)
ip.between?(self.start, self.stop)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment