Skip to content

Instantly share code, notes, and snippets.

@bramswenson
Created May 6, 2014 03:52
Show Gist options
  • Save bramswenson/b7157f18b6baa85d3b2c to your computer and use it in GitHub Desktop.
Save bramswenson/b7157f18b6baa85d3b2c to your computer and use it in GitHub Desktop.
module Geoip
def self.geocode(ip)
Geoip::Block.geocode(ip)
rescue Mongoid::Errors::DocumentNotFound
false
end
class Block
include Mongoid::Document
field :start_ip, type: IPAddr
field :end_ip, type: IPAddr
belongs_to :location
def to_geocode_hash(ip=nil)
{ ip: ip.present? ? ip.to_s : self.start_ip.to_s }.merge(self.location.attributes)
end
def self.geocode(ip)
ip = IPAddr.new(ip).to_i
block = where(:start_ip.lte => ip, :end_ip.gte => ip).limit(1).includes(:location).first
block.try(:to_geocode_hash)
end
end
class Location
include Mongoid::Document
field :city, type: String
field :region, type: String
field :postal, type: String
field :country, type: String
field :latitude, type: Float
field :longitude, type: Float
field :metro_code, type: String
field :area_code, type: String
has_many :blocks
end
module MongoizableIPAddr
def self.included(base)
base.send :extend, ClassMethods
end
def mongoize
self.to_i
end
module ClassMethods
def demongoize(object)
IPAddr.new(object.to_i, Socket::AF_INET)
end
def mongoize(object)
case object
when IPAddr
object.mongoize
when Integer
object.to_i
when String
IPAddr.new(object).mongoize
else
raise ArgumentError.new("cannot mongoize object: #{object.inspect}")
end
end
def evolve(object)
case object
when IPAddr then object.mongoize
when Integer then object.to_i
when String then IPAddr.new(object).mongoize
else raise ArgumentError.new("cannot evolve object: #{object.inspect}")
end
end
end
end
end
IPAddr.send :include, Geoip::MongoizableIPAddr
require 'spec_helper'
describe Geoip::Block, type: :model do
subject { FactoryGirl.create(:geoip_block) }
describe 'schema' do
it { should have_field(:start_ip).of_type(IPAddr) }
it { should have_field(:end_ip).of_type(IPAddr) }
it { should belong_to :location }
end
describe '::geocode' do
let!(:block) { FactoryGirl.create(:geoip_block) }
context 'when the given ip is within a known block' do
it 'is the geocode_hash for the found block' do
expect(Geoip::Block.geocode(block.start_ip.to_s)).to be == block.to_geocode_hash
end
end
context 'when the given ip is not within a known block' do
it 'is nil' do
expect(Geoip::Block.geocode('127.0.0.1')).to be_nil
end
end
end
describe '#to_geocode_hash' do
describe 'given an ip argument' do
let(:ip) { '8.8.8.8' }
it 'it a hash with the location data to includes the ip' do
expect(subject.to_geocode_hash(ip)[:ip]).to be == ip
expect(subject.to_geocode_hash(ip)).to have_key 'city'
expect(subject.to_geocode_hash(ip)).to have_key 'region'
expect(subject.to_geocode_hash(ip)).to have_key 'postal'
expect(subject.to_geocode_hash(ip)).to have_key 'country'
expect(subject.to_geocode_hash(ip)).to have_key 'latitude'
expect(subject.to_geocode_hash(ip)).to have_key 'longitude'
end
end
describe 'without an ip argument' do
it 'it a hash with the location data' do
expect(subject.to_geocode_hash[:ip]).to be == subject.start_ip.to_s
expect(subject.to_geocode_hash).to have_key 'city'
expect(subject.to_geocode_hash).to have_key 'region'
expect(subject.to_geocode_hash).to have_key 'postal'
expect(subject.to_geocode_hash).to have_key 'country'
expect(subject.to_geocode_hash).to have_key 'latitude'
expect(subject.to_geocode_hash).to have_key 'longitude'
end
end
end
end
require 'spec_helper'
require 'geoip'
describe Geoip do
describe '::geocode' do
context 'when the given ip is in the database' do
let(:start_ip) { IPAddr.new('50.156.80.0') }
let(:end_ip) { IPAddr.new('50.156.80.255') }
let(:ip) { '50.156.80.254' }
let!(:block) { FactoryGirl.create(:geoip_block, start_ip: start_ip, end_ip: end_ip) }
it 'is a hash of location information' do
expect(Geoip.geocode(ip)).to be == block.to_geocode_hash
end
end
context 'when the given ip is not in the database' do
let(:ip) { '127.0.0.1' }
it 'is false' do
expect(Geoip.geocode(ip)).to be_false
end
end
end
end
require 'spec_helper'
describe Geoip::Location, type: :model do
describe 'schema' do
it { should have_field(:city).of_type(String) }
it { should have_field(:region).of_type(String) }
it { should have_field(:postal).of_type(String) }
it { should have_field(:country).of_type(String) }
it { should have_field(:latitude).of_type(Float) }
it { should have_field(:longitude).of_type(Float) }
it { should have_field(:metro_code).of_type(String) }
it { should have_field(:area_code).of_type(String) }
it { should have_many :blocks }
end
end
require 'spec_helper'
describe Geoip::MongoizableIPAddr do
subject { IPAddr.new('8.8.8.8') }
it 'is included in IPAddr' do
expect(subject.class).to include Geoip::MongoizableIPAddr
end
describe '#mongoize' do
it 'casts the IPAddr to an Integer' do
expect(subject.mongoize).to be == subject.to_i
end
end
describe '::demongoize' do
it 'casts the object to an IPAddr' do
expect(IPAddr.demongoize(subject.mongoize)).to be == subject
end
end
describe '::mongoize' do
context 'when the given object is an IPAddr' do
it 'casts the IPAddr to an Integer' do
expect(IPAddr.mongoize(subject)).to be == subject.to_i
end
end
context 'when the given object is an Integer' do
it 'casts the Integer to an Integer' do
expect(IPAddr.mongoize(subject.to_i)).to be == subject.to_i
end
end
context 'when the given object is an String' do
it 'casts the String to an Integer' do
expect(IPAddr.mongoize(subject.to_s)).to be == subject.to_i
end
end
end
describe '::evolve' do
context 'when the given object is an IPAddr' do
it 'casts the IPAddr to an Integer' do
expect(IPAddr.evolve(subject)).to be == subject.to_i
end
end
context 'when the given object is an Integer' do
it 'casts the Integer to an Integer' do
expect(IPAddr.evolve(subject.to_i)).to be == subject.to_i
end
end
context 'when the given object is an String' do
it 'casts the String to an Integer' do
expect(IPAddr.evolve(subject.to_s)).to be == subject.to_i
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment