diff --git a/lib/geocoder/lookups/maxmind.rb b/lib/geocoder/lookups/maxmind.rb index e02750336b21c41f6ad223522fe019d1729ef2f6..0be2495cce438bcfb8afe1cc7ae1bb15c0deefc8 100644 --- a/lib/geocoder/lookups/maxmind.rb +++ b/lib/geocoder/lookups/maxmind.rb @@ -10,21 +10,45 @@ module Geocoder::Lookup end def query_url(query) - "#{protocol}://geoip3.maxmind.com/f?" + url_query_string(query) + "#{protocol}://geoip.maxmind.com/#{service_code}?" + url_query_string(query) end private # --------------------------------------------------------------- + def service_code + if s = configuration[:service] and services.keys.include?(s) + services[s] + else + raise( + Geocoder::ConfigurationError, + "When using MaxMind you MUST specify a service name: " + + "Geocoder.configure(:maxmind => {:service => ...}), " + + "where '...' is one of: #{services.keys.inspect}" + ) + end + end + + ## + # Service names mapped to code used in URL. + # + def services + { + :country => "a", + :city => "b", + :city_isp_org => "f", + :omni => "e" + } + end + def results(query) # don't look up a loopback address, just return the stored result return [reserved_result] if query.loopback_ip_address? doc = fetch_data(query) if doc and doc.is_a?(Array) - if doc.size == 10 + if doc.last.nil? return [doc] - elsif doc.size > 10 and doc[10] == "INVALID_LICENSE_KEY" - raise_error(Geocoder::InvalidApiKey) || - warn("Invalid MaxMind API key.") + elsif doc.last == "INVALID_LICENSE_KEY" + raise_error(Geocoder::InvalidApiKey) || warn("Invalid MaxMind API key.") end end return [] @@ -35,7 +59,7 @@ module Geocoder::Lookup end def reserved_result - ",,,,0,0,0,0,," + ",,,,0,0,0,0,,," end def query_url_params(query) diff --git a/lib/geocoder/results/maxmind.rb b/lib/geocoder/results/maxmind.rb index d787ab77f1e64a6a123edd16ecb759033d785ce0..2cc45beba872469199bdb372feb38529ad75fe01 100644 --- a/lib/geocoder/results/maxmind.rb +++ b/lib/geocoder/results/maxmind.rb @@ -3,53 +3,134 @@ require 'geocoder/results/base' module Geocoder::Result class Maxmind < Base - def address(format = :full) - s = state_code.to_s == "" ? "" : ", #{state_code}" - "#{city}#{s} #{postal_code}, #{country_code}".sub(/^[ ,]*/, "") - end + ## + # Hash mapping service names to names of returned fields. + # + def self.field_names + { + :country => [ + :country_code, + :error + ], - def country_code - @data[0] + :city => [ + :country_code, + :region_code, + :city_name, + :latitude, + :longitude, + :error + ], + + :city_isp_org => [ + :country_code, + :region_code, + :city_name, + :postal_code, + :latitude, + :longitude, + :metro_code, + :area_code, + :isp_name, + :organization_name, + :error + ], + + :omni => [ + :country_code, + :country_name, + :region_code, + :region_name, + :city_name, + :latitude, + :longitude, + :metro_code, + :area_code, + :time_zone, + :continent_code, + :postal_code, + :isp_name, + :organization_name, + :domain, + :as_number, + :netspeed, + :user_type, + :accuracy_radius, + :country_confidence_factor, + :city_confidence_factor, + :region_confidence_factor, + :postal_confidence_factor, + :error + ] + } end - def state_code - @data[1] + ## + # Name of the MaxMind service being used. + # Inferred from format of @data. + # + def service_name + self.class.field_names.to_a.each do |n,f| + return n if f.size == @data.size + end + nil end - def city - @data[2] + def field_names + self.class.field_names[service_name] end - def postal_code - @data[3] + def data_hash + @data_hash ||= Hash[*field_names.zip(@data).flatten] end def coordinates - [@data[4].to_f, @data[5].to_f] + [data_hash[:latitude].to_f, data_hash[:longitude].to_f] end - def metrocode - @data[6] + def address(format = :full) + s = state_code.to_s == "" ? "" : ", #{state_code}" + "#{city}#{s} #{postal_code}, #{country_code}".sub(/^[ ,]*/, "") end - def area_code - @data[7] + def city + data_hash[:city_name] end - def isp - @data[8] + def state # not given by MaxMind + data_hash[:region_name] || data_hash[:region_code] end - def organization - @data[9] + def state_code + data_hash[:region_code] end def country #not given by MaxMind - country_code + data_hash[:country_name] || data_hash[:country_code] + end + + def country_code + data_hash[:country_code] + end + + def postal_code + data_hash[:postal_code] + end + + def method_missing(method, *args, &block) + if field_names.include?(method) + data_hash[method] + else + super + end end - def state #not given by MaxMind - state_code + def respond_to?(method) + if field_names.include?(method) + true + else + super + end end end end diff --git a/test/fixtures/maxmind_24_24_24_21 b/test/fixtures/maxmind_24_24_24_21 new file mode 100644 index 0000000000000000000000000000000000000000..3fde71520e063734d3ab96fc09afb5c8d3e7aa3f --- /dev/null +++ b/test/fixtures/maxmind_24_24_24_21 @@ -0,0 +1 @@ +US, diff --git a/test/fixtures/maxmind_24_24_24_22 b/test/fixtures/maxmind_24_24_24_22 new file mode 100644 index 0000000000000000000000000000000000000000..426280ed25a5e0ccd3d2b83c0daf4981424f69b3 --- /dev/null +++ b/test/fixtures/maxmind_24_24_24_22 @@ -0,0 +1 @@ +US,NY,Jamaica,40.6915,-73.8057, diff --git a/test/fixtures/maxmind_24_24_24_23 b/test/fixtures/maxmind_24_24_24_23 new file mode 100644 index 0000000000000000000000000000000000000000..8ffaf07f09855cf9b27aea3dbb3e0901e664152c --- /dev/null +++ b/test/fixtures/maxmind_24_24_24_23 @@ -0,0 +1 @@ +US,NY,Jamaica,,40.6915,-73.8057,501,718,"Road Runner","Road Runner", diff --git a/test/fixtures/maxmind_24_24_24_24 b/test/fixtures/maxmind_24_24_24_24 new file mode 100644 index 0000000000000000000000000000000000000000..ab20e03758da5e155758065df679c5d682c8106c --- /dev/null +++ b/test/fixtures/maxmind_24_24_24_24 @@ -0,0 +1 @@ +US,"United States",NY,"New York",Jamaica,40.6915,-73.8057,501,718,America/New_York,NA,,"Road Runner","Road Runner",rr.com,"AS11351 Road Runner HoldCo LLC",Cable/DSL,residential,779,99,37,76,, diff --git a/test/fixtures/maxmind_74_200_247_59 b/test/fixtures/maxmind_74_200_247_59 index 22fce3390f59c095a1c844430478794ceac813d8..9eb1bd01172e116e6bc07ec891c969420b03ee4e 100644 --- a/test/fixtures/maxmind_74_200_247_59 +++ b/test/fixtures/maxmind_74_200_247_59 @@ -1 +1 @@ -US,TX,Plano,75093,33.034698,-96.813400,623,972,"Layered Technologies , US","Layered Technologies , US" \ No newline at end of file +US,TX,Plano,75093,33.034698,-96.813400,623,972,"Layered Technologies , US","Layered Technologies , US", diff --git a/test/services_test.rb b/test/services_test.rb index 30968707c102283dddbf4c7d837af518859e3866..551040c92b1eeb2f10fee08e14d2a469a81789fb 100644 --- a/test/services_test.rb +++ b/test/services_test.rb @@ -154,15 +154,45 @@ class ServicesTest < Test::Unit::TestCase # --- MaxMind --- def test_maxmind_result_on_ip_address_search - Geocoder::Configuration.ip_lookup = :maxmind + Geocoder.configure(:ip_lookup => :maxmind, :maxmind => {:service => :city_isp_org}) result = Geocoder.search("74.200.247.59").first assert result.is_a?(Geocoder::Result::Maxmind) end - def test_maxmind_result_components - Geocoder::Configuration.ip_lookup = :maxmind - result = Geocoder.search("74.200.247.59").first - assert_equal "Plano, TX 75093, US", result.address + def test_maxmind_result_knows_country_service_name + Geocoder.configure(:ip_lookup => :maxmind) + assert_equal :country, Geocoder.search("24.24.24.21").first.service_name + end + + def test_maxmind_result_knows_city_service_name + Geocoder.configure(:ip_lookup => :maxmind) + assert_equal :city, Geocoder.search("24.24.24.22").first.service_name + end + + def test_maxmind_result_knows_city_isp_org_service_name + Geocoder.configure(:ip_lookup => :maxmind) + assert_equal :city_isp_org, Geocoder.search("24.24.24.23").first.service_name + end + + def test_maxmind_result_knows_omni_service_name + Geocoder.configure(:ip_lookup => :maxmind) + assert_equal :omni, Geocoder.search("24.24.24.24").first.service_name + end + + def test_maxmind_special_result_components + Geocoder.configure(:ip_lookup => :maxmind) + result = Geocoder.search("24.24.24.24").first + assert_equal "Road Runner", result.isp_name + assert_equal "Cable/DSL", result.netspeed + assert_equal "rr.com", result.domain + end + + def test_maxmind_raises_exception_when_service_not_configured + Geocoder.configure(:ip_lookup => :maxmind) + Geocoder.configure(:maxmind => {:service => nil}) + assert_raises Geocoder::ConfigurationError do + Geocoder::Query.new("24.24.24.24").url + end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 6fca5010f64d82ecd2dfd1cd5d1ee8bbc34d8c70..2fe194c1db8f40e1cd87e95f8317f667296098a1 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -102,7 +102,8 @@ module Geocoder if query.reverse_geocode? filename = "#{fixture_prefix}_reverse" else - filename = "#{fixture_prefix}_#{query.text.gsub(" ", "_")}" + label = query.text.gsub(/[ \.]/, "_") + filename = "#{fixture_prefix}_#{label}" end if fixture_exists?(filename) read_fixture "#{filename}" @@ -238,6 +239,10 @@ end class Test::Unit::TestCase + def setup + Geocoder.configure(:maxmind => {:service => :omni}) + end + def teardown Geocoder.send(:remove_const, :Configuration) load "geocoder/configuration.rb"