From 8d51c2e6a590fafaceab7c1277251c6e10cd3349 Mon Sep 17 00:00:00 2001 From: Olivier Gonzalez <gonzoyumo@gmail.com> Date: Wed, 12 Oct 2011 15:58:45 +0200 Subject: [PATCH] add MaxMind support for ip geocoding --- README.rdoc | 16 +++++++ lib/geocoder.rb | 4 +- lib/geocoder/configuration.rb | 8 ++++ lib/geocoder/lookups/maxmind.rb | 38 +++++++++++++++++ lib/geocoder/results/maxmind.rb | 55 +++++++++++++++++++++++++ test/fixtures/maxmind_74_200_247_59.txt | 1 + test/fixtures/maxmind_no_results.txt | 1 + test/lookup_test.rb | 6 +++ test/services_test.rb | 14 +++++++ test/test_helper.rb | 13 ++++++ 10 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 lib/geocoder/lookups/maxmind.rb create mode 100644 lib/geocoder/results/maxmind.rb create mode 100644 test/fixtures/maxmind_74_200_247_59.txt create mode 100644 test/fixtures/maxmind_no_results.txt diff --git a/README.rdoc b/README.rdoc index ce88545d..d4210ba3 100644 --- a/README.rdoc +++ b/README.rdoc @@ -235,6 +235,12 @@ By default Geocoder uses Google's geocoding API to fetch coordinates and street # to use an API key: Geocoder::Configuration.api_key = "..." + # IP geocoding service : + Geocoder::Configuration.ip_lookup = :maxmind + + # to use an API key for IP geocoding service: + Geocoder::Configuration.ip_lookup_api_key = "..." + # geocoding service request timeout, in seconds (default 3): Geocoder::Configuration.timeout = 5 @@ -328,6 +334,16 @@ Documentation:: http://github.com/fiorix/freegeoip/blob/master/README.rst Terms of Service:: ? Limitations:: ? +==== MaxMind City + +API key:: required +Quota:: Requests Packs can be purchased (starting at 50,000 for 20$) +Region:: world +SSL support:: no +Languages:: English +Documentation:: http://www.maxmind.com/app/web_services +Terms of Service:: ? +Limitations:: ? == Caching diff --git a/lib/geocoder.rb b/lib/geocoder.rb index 5bce2283..123cb619 100644 --- a/lib/geocoder.rb +++ b/lib/geocoder.rb @@ -63,7 +63,7 @@ module Geocoder # All IP address lookups, default first. # def ip_lookups - [:freegeoip] + [:freegeoip, :maxmind] end @@ -81,7 +81,7 @@ module Geocoder # def lookup(query) if ip_address?(query) - get_lookup(ip_lookups.first) + get_lookup(Configuration.ip_lookup || ip_lookups.first) else get_lookup(Configuration.lookup || street_lookups.first) end diff --git a/lib/geocoder/configuration.rb b/lib/geocoder/configuration.rb index 44baabee..13d39e36 100644 --- a/lib/geocoder/configuration.rb +++ b/lib/geocoder/configuration.rb @@ -25,6 +25,14 @@ module Geocoder # for Google Premier use a 3-element array: [key, client, channel] [:api_key, nil], + # name of IP geocoding service (symbol) + # FIXME: temporary added for Maxmind support, need clean rewrite + [:ip_lookup, nil], + + # API key for IP geocoding service + # FIXME: temporary added for Maxmind support, need clean rewrite + [:ip_lookup_api_key, nil], + # cache object (must respond to #[], #[]=, and #keys) [:cache, nil], diff --git a/lib/geocoder/lookups/maxmind.rb b/lib/geocoder/lookups/maxmind.rb new file mode 100644 index 00000000..334463d5 --- /dev/null +++ b/lib/geocoder/lookups/maxmind.rb @@ -0,0 +1,38 @@ +require 'geocoder/lookups/base' +require 'geocoder/results/maxmind' + +module Geocoder::Lookup + class Maxmind < Base + + private # --------------------------------------------------------------- + + def results(query, reverse = false) + # don't look up a loopback address, just return the stored result + return [reserved_result] if loopback_address?(query) + begin + doc = fetch_data(query, reverse) + if doc && doc.size == 10 + return [doc] + else + warn "Maxmind error : #{doc[10]}" if doc + return [] + end + rescue StandardError => err + raise_error(err) + return [] + end + end + + def parse_raw_data(raw_data) + raw_data.split(',') # Maxmind just returns text/plain + end + + def reserved_result + ",,,,0,0,0,0,," + end + + def query_url(query, reverse = false) + "http://geoip3.maxmind.com/f?l=#{Geocoder::Configuration.ip_lookup_api_key}&i=#{query}" + end + end +end diff --git a/lib/geocoder/results/maxmind.rb b/lib/geocoder/results/maxmind.rb new file mode 100644 index 00000000..8963ab5c --- /dev/null +++ b/lib/geocoder/results/maxmind.rb @@ -0,0 +1,55 @@ +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 + + def country_code + @data[0] + end + + def state_code + @data[1] + end + + def city + @data[2] + end + + def postal_code + @data[3] + end + + def coordinates + [@data[4].to_f, @data[5].to_f] + end + + def metrocode + @data[6] + end + + def area_code + @data[7] + end + + def isp + @data[8][1,@data[8].length-2] + end + + def organization + @data[9][1,@data[9].length-2] + end + + def country #not given by MaxMind + country_code + end + + def state #not given by MaxMind + state_code + end + end +end diff --git a/test/fixtures/maxmind_74_200_247_59.txt b/test/fixtures/maxmind_74_200_247_59.txt new file mode 100644 index 00000000..226e7219 --- /dev/null +++ b/test/fixtures/maxmind_74_200_247_59.txt @@ -0,0 +1 @@ +US,TX,Plano,75093,33.034698,-96.813400,623,972,"Layered Technologies","Layered Technologies" \ No newline at end of file diff --git a/test/fixtures/maxmind_no_results.txt b/test/fixtures/maxmind_no_results.txt new file mode 100644 index 00000000..c1d1bc2d --- /dev/null +++ b/test/fixtures/maxmind_no_results.txt @@ -0,0 +1 @@ +,,,,,,,,,,IP_NOT_FOUND \ No newline at end of file diff --git a/test/lookup_test.rb b/test/lookup_test.rb index 3c72d05b..75745666 100644 --- a/test/lookup_test.rb +++ b/test/lookup_test.rb @@ -27,4 +27,10 @@ class LookupTest < Test::Unit::TestCase g = Geocoder::Lookup::Yahoo.new assert_match "appid=MY_KEY", g.send(:query_url, "Madison Square Garden, New York, NY 10001, United States") end + + def test_maxmind_api_key + Geocoder::Configuration.ip_lookup_api_key = "MY_KEY" + g = Geocoder::Lookup::Maxmind.new + assert_match "l=MY_KEY", g.send(:query_url, "74.200.247.59") + end end diff --git a/test/services_test.rb b/test/services_test.rb index b01b84ab..7c421e81 100644 --- a/test/services_test.rb +++ b/test/services_test.rb @@ -94,6 +94,20 @@ class ServicesTest < Test::Unit::TestCase assert_equal "Plano, TX 75093, United States", result.address end + # --- MaxMind --- + + def test_maxmind_result_on_ip_address_search + Geocoder::Configuration.ip_lookup = :maxmind + 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 + end + # --- Bing --- diff --git a/test/test_helper.rb b/test/test_helper.rb index 137f756f..e0567689 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -133,6 +133,19 @@ module Geocoder end end + class Maxmind < Base + private #----------------------------------------------------------------- + def fetch_raw_data(query, reverse = false) + raise TimeoutError if query == "timeout" + raise SocketError if query == "socket_error" + file = case query + when "no results"; :no_results + else "74_200_247_59" + end + read_fixture "maxmind_#{file}.txt" + end + end + class Bing < Base private #----------------------------------------------------------------- def fetch_raw_data(query, reverse = false) -- GitLab