diff --git a/README.md b/README.md index a71dd671ee3c43122361116ce2dace66faf19fe3..92cfbc28b94fc9699424ab8deafb6248edad3dc5 100644 --- a/README.md +++ b/README.md @@ -749,6 +749,15 @@ This uses the PostcodeAnywhere UK Geocode service, this will geocode any string * **Limitations**: ? * **Notes**: You must specify which MaxMind service you are using in your configuration, and also basic authentication. For example: `Geocoder.configure(:maxmind_geoip2 => {:service => :country, :basic_auth => {:user => '', :password => ''}})`. +#### IPInfo.io (`:ipinfo_io`) + +* **API key**: optional - see http://ipinfo.io/pricing +* **Quota**: 1,000/day - more with api key +* **Region**: world +* **SSL support**: no (not without access key - see http://ipinfo.io/pricing) +* **Languages**: English +* **Documentation**: http://ipinfo.io/developers +* **Terms of Service**: http://ipinfo.io/developers ### IP Address Local Database Services diff --git a/lib/geocoder/lookup.rb b/lib/geocoder/lookup.rb index 92d5a50b2069319c9dda6c6576aa0aa413359041..962f5a79c74bfe6f05db49aeeb9207ad9168109f 100644 --- a/lib/geocoder/lookup.rb +++ b/lib/geocoder/lookup.rb @@ -62,7 +62,8 @@ module Geocoder :maxmind_local, :telize, :pointpin, - :maxmind_geoip2 + :maxmind_geoip2, + :ipinfo_io ] end diff --git a/lib/geocoder/lookups/ipinfo_io.rb b/lib/geocoder/lookups/ipinfo_io.rb new file mode 100644 index 0000000000000000000000000000000000000000..22efcbefeb37ebd08f53057019178a72911a4ae8 --- /dev/null +++ b/lib/geocoder/lookups/ipinfo_io.rb @@ -0,0 +1,41 @@ +require 'geocoder/lookups/base' +require 'geocoder/results/ipinfo_io' + +module Geocoder::Lookup + class IpinfoIo < Base + + def name + "Ipinfo.io" + end + + def query_url(query) + "#{protocol}://ipinfo.io/#{query.sanitized_text}/geo" + end + + # currently doesn't support HTTPS + def supported_protocols + [:http] + end + + private # --------------------------------------------------------------- + + def results(query) + # don't look up a loopback address, just return the stored result + return [reserved_result(query.text)] if query.loopback_ip_address? + if (doc = fetch_data(query)).nil? or doc['code'] == 401 or empty_result?(doc) + [] + else + [doc] + end + end + + def empty_result?(doc) + !doc.is_a?(Hash) or doc.keys == ["ip"] + end + + def reserved_result(ip) + {"message" => "Input string is not a valid IP address", "code" => 401} + end + + end +end diff --git a/lib/geocoder/results/ipinfo_io.rb b/lib/geocoder/results/ipinfo_io.rb new file mode 100644 index 0000000000000000000000000000000000000000..4968a498499b1cb071fcb804adcc39251b5cf25b --- /dev/null +++ b/lib/geocoder/results/ipinfo_io.rb @@ -0,0 +1,56 @@ +require 'geocoder/results/base' + +module Geocoder::Result + class IpinfoIo < Base + + def address(format = :full) + "#{city} #{postal_code}, #{country}".sub(/^[ ,]*/, "") + end + + def latitude + @data['loc'].split(',')[0].to_f + end + + def longitude + @data['loc'].split(',')[1].to_f + end + + def coordinates + [@data['loc'].split(',')[0].to_f, @data['loc'].split(',')[1].to_f] + end + + def city + @data['city'] + end + + def state + @data['region'] + end + + def country + @data['country'] + end + + def postal_code + @data['postal'] + end + + def country_code + @data.fetch('country', '') + end + + def state_code + @data.fetch('region_code', '') + end + + def self.response_attributes + %w['ip', 'city', 'region', 'country', 'latitude', 'longitude', 'postal_code'] + end + + response_attributes.each do |a| + define_method a do + @data[a] + end + end + end +end diff --git a/test/fixtures/ipinfo_io_8_8_8_8 b/test/fixtures/ipinfo_io_8_8_8_8 new file mode 100644 index 0000000000000000000000000000000000000000..7d213307f1b1c68df96e52edfa3b2aad658f0710 --- /dev/null +++ b/test/fixtures/ipinfo_io_8_8_8_8 @@ -0,0 +1,8 @@ +{ + "ip": "8.8.8.8", + "city": "Mountain View", + "region": "California", + "country": "US", + "loc": "37.3845,-122.0881", + "postal": "94040" +} diff --git a/test/fixtures/ipinfo_io_no_results b/test/fixtures/ipinfo_io_no_results new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/test/test_helper.rb b/test/test_helper.rb index 973bd083f9a5e3ab15a3326e398266fc9b7bbb10..73c9cac423684bfd364eb4633d6d31920d27e295 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -320,6 +320,15 @@ module Geocoder "latlon_6000_universal_blvd" end end + + require 'geocoder/lookups/ipinfo_io' + class IpinfoIo + private + def default_fixture_filename + "ipinfo_io_8_8_8_8" + end + end + end end diff --git a/test/unit/lookup_test.rb b/test/unit/lookup_test.rb index 1fba832a3e2103eb42cee90f70e4c78736a6f440..002c6a840d05dd446f58449d8151a17a299cdfcc 100644 --- a/test/unit/lookup_test.rb +++ b/test/unit/lookup_test.rb @@ -24,7 +24,7 @@ class LookupTest < GeocoderTestCase def test_query_url_contains_values_in_params_hash Geocoder::Lookup.all_services_except_test.each do |l| - next if [:freegeoip, :maxmind_local, :telize, :pointpin, :geoip2, :maxmind_geoip2, :mapbox].include? l # does not use query string + next if [:freegeoip, :maxmind_local, :telize, :pointpin, :geoip2, :maxmind_geoip2, :mapbox, :ipinfo_io].include? l # does not use query string set_api_key!(l) url = Geocoder::Lookup.get(l).query_url(Geocoder::Query.new( "test", :params => {:one_in_the_hand => "two in the bush"}