From 3b4039762bf75e76d69d2eb53421125f59208dc6 Mon Sep 17 00:00:00 2001 From: IP2Location Date: Thu, 14 Sep 2023 09:25:40 +0800 Subject: [PATCH 1/4] Add IP2Location LITE support --- README_API_GUIDE.md | 22 ++++++++++ lib/geocoder/lookup.rb | 3 +- lib/geocoder/lookups/ip2location_lite.rb | 41 +++++++++++++++++++ lib/geocoder/results/ip2location_lite.rb | 47 ++++++++++++++++++++++ test/test_helper.rb | 17 ++++++++ test/unit/cache_test.rb | 1 + test/unit/error_handling_test.rb | 8 ++-- test/unit/lookup_test.rb | 2 +- test/unit/lookups/ip2location_lite_test.rb | 13 ++++++ test/unit/result_test.rb | 2 + 10 files changed, 150 insertions(+), 6 deletions(-) create mode 100644 lib/geocoder/lookups/ip2location_lite.rb create mode 100644 lib/geocoder/results/ip2location_lite.rb create mode 100644 test/unit/lookups/ip2location_lite_test.rb diff --git a/README_API_GUIDE.md b/README_API_GUIDE.md index 9080a62ea..5763a0fd4 100644 --- a/README_API_GUIDE.md +++ b/README_API_GUIDE.md @@ -815,4 +815,26 @@ You can generate ActiveRecord migrations and download and import data via provid You can replace `city` with `country` in any of the above tasks, generators, and configurations. +### IP2Location LITE (`:ip2location_lite`) + +This lookup provides methods for geocoding IP addresses without making a call to a remote API (improves speed and availability). + +* **API key**: none (requires a IP2Location or FREE IP2Location LITE binary database which can be downloaded from [IP2Location LITE](https://lite.ip2location.com/)) +* **Quota**: none +* **Region**: world +* **SSL support**: N/A +* **Languages**: English +* **Documentation**: https://lite.ip2location.com/ +* **Terms of Service**: https://lite.ip2location.com/ +* **Notes**: **You must download a binary database (BIN) file from IP2Location LITE and set the `:file` configuration option.** Set the path to the database file in your configuration: + + Geocoder.configure( + ip_lookup: :ip2location_lite, + ip2location_lite: { + file: File.join('folder', 'IP2LOCATION-LITE-DB11.BIN') + } + ) + +You must add the *[ip2location_ruby](https://rubygems.org/gems/ip2location_ruby)* gem (pure Ruby implementation) to your Gemfile or have it installed in your system. + Copyright (c) 2009-2021 Alex Reisner, released under the MIT license. diff --git a/lib/geocoder/lookup.rb b/lib/geocoder/lookup.rb index 56b36319f..4dca778fb 100644 --- a/lib/geocoder/lookup.rb +++ b/lib/geocoder/lookup.rb @@ -95,7 +95,8 @@ def ip_services :ipgeolocation, :ipqualityscore, :ipbase, - :ip2location_io + :ip2location_io, + :ip2location_lite ] end diff --git a/lib/geocoder/lookups/ip2location_lite.rb b/lib/geocoder/lookups/ip2location_lite.rb new file mode 100644 index 000000000..a05266af1 --- /dev/null +++ b/lib/geocoder/lookups/ip2location_lite.rb @@ -0,0 +1,41 @@ +require 'geocoder/lookups/base' +require 'geocoder/results/ip2location_lite' + +module Geocoder + module Lookup + class Ip2locationLite < Base + attr_reader :gem_name + + def initialize + unless configuration[:file].nil? + begin + @gem_name = 'ip2location_ruby' + require @gem_name + rescue LoadError + raise "Could not load IP2Location DB dependency. To use the IP2LocationLite lookup you must add the #{@gem_name} gem to your Gemfile or have it installed in your system." + end + + @i2l = Ip2location.new.open(configuration[:file].to_s) + end + super + end + + def name + 'IP2LocationLite' + end + + def required_api_key_parts + [] + end + + private + + def results(query) + return [] unless configuration[:file] + + result = @i2l.get_all(query.to_s) + result.nil? ? [] : [result] + end + end + end +end \ No newline at end of file diff --git a/lib/geocoder/results/ip2location_lite.rb b/lib/geocoder/results/ip2location_lite.rb new file mode 100644 index 000000000..82d8b0376 --- /dev/null +++ b/lib/geocoder/results/ip2location_lite.rb @@ -0,0 +1,47 @@ +require 'geocoder/results/base' + +module Geocoder::Result + class Ip2locationLite < Base + + def coordinates + [@data[:latitude], @data[:longitude]] + end + + def city + @data[:city] + end + + def state + @data[:region] + end + + def state_code + "" # Not available in Maxmind's database + end + + def country + @data[:country_long] + end + + def country_code + @data[:country_short] + end + + def postal_code + @data[:zipcode] + end + + def self.response_attributes + %w[country_short country_long region latitude longitude isp + domain netspeed areacode iddcode timezone zipcode weatherstationname + weatherstationcode mcc mnc mobilebrand elevation usagetype addresstype + category district asn as] + end + + response_attributes.each do |a| + define_method a do + @data[a] || "" + end + end + end +end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index c0fb78382..e5a020f67 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -260,6 +260,23 @@ def default_fixture_filename end end + require 'geocoder/lookups/ip2location_lite' + class Ip2locationLite + private + + remove_method(:results) + + def results query + return [] if query.to_s == "no results" + + if query.to_s == '127.0.0.1' + [{:country_short=>"-", :country_long=>"-", :region=>"-", :city=>"-", :latitude=>0.0, :longitude=>0.0, :zipcode=>"-", :timezone=>"-", :isp=>"Loopback", :domain=>"-", :netspeed=>"-", :iddcode=>"-", :areacode=>"-", :weatherstationcode=>"-", :weatherstationname=>"-", :mcc=>"-", :mnc=>"-", :mobilebrand=>"-", :elevation=>0, :usagetype=>"RSV", :addresstype=>"U", :category=>"IAB24", :district=>"-", :asn=>"-", :as=>"-"}] + elsif query.to_s == '8.8.8.8' + [{:country_short=>"US", :country_long=>"United States of America", :region=>"California", :city=>"Mountain View", :latitude=>37.40599060058594, :longitude=>-122.0785140991211, :zipcode=>"94043", :timezone=>"-07:00", :isp=>"Google LLC", :domain=>"google.com", :netspeed=>"T1", :iddcode=>"1", :areacode=>"650", :weatherstationcode=>"USCA0746", :weatherstationname=>"Mountain View", :mcc=>"-", :mnc=>"-", :mobilebrand=>"-", :elevation=>32, :usagetype=>"DCH", :addresstype=>"A", :category=>"IAB19-11", :district=>"San Diego County", :asn=>"15169", :as=>"Google LLC"}] + end + end + end + require 'geocoder/lookups/ipgeolocation' class Ipgeolocation private diff --git a/test/unit/cache_test.rb b/test/unit/cache_test.rb index f58bc4f66..6b41895df 100644 --- a/test/unit/cache_test.rb +++ b/test/unit/cache_test.rb @@ -21,6 +21,7 @@ def test_second_occurrence_of_request_is_cache_hit # local, does not use cache l == :maxmind_local || l == :geoip2 || + l == :ip2location_lite || # uses the AWS gem, not HTTP requests with caching l == :amazon_location_service Geocoder.configure(:lookup => l) diff --git a/test/unit/error_handling_test.rb b/test/unit/error_handling_test.rb index c63ef2fcd..dbdfbe67b 100644 --- a/test/unit/error_handling_test.rb +++ b/test/unit/error_handling_test.rb @@ -43,7 +43,7 @@ def test_never_raise_response_parse_error def test_always_raise_timeout_error Geocoder.configure(:always_raise => [Timeout::Error]) Geocoder::Lookup.all_services_with_http_requests.each do |l| - next if l == :maxmind_local || l == :geoip2 # local, does not use cache + next if l == :maxmind_local || l == :geoip2 || l == :ip2location_lite # local, does not use cache lookup = Geocoder::Lookup.get(l) set_api_key!(l) assert_raises Timeout::Error do @@ -55,7 +55,7 @@ def test_always_raise_timeout_error def test_always_raise_socket_error Geocoder.configure(:always_raise => [SocketError]) Geocoder::Lookup.all_services_with_http_requests.each do |l| - next if l == :maxmind_local || l == :geoip2 # local, does not use cache + next if l == :maxmind_local || l == :geoip2 || l == :ip2location_lite # local, does not use cache lookup = Geocoder::Lookup.get(l) set_api_key!(l) assert_raises SocketError do @@ -67,7 +67,7 @@ def test_always_raise_socket_error def test_always_raise_connection_refused_error Geocoder.configure(:always_raise => [Errno::ECONNREFUSED]) Geocoder::Lookup.all_services_with_http_requests.each do |l| - next if l == :maxmind_local || l == :geoip2 # local, does not use cache + next if l == :maxmind_local || l == :geoip2 || l == :ip2location_lite # local, does not use cache lookup = Geocoder::Lookup.get(l) set_api_key!(l) assert_raises Errno::ECONNREFUSED do @@ -79,7 +79,7 @@ def test_always_raise_connection_refused_error def test_always_raise_host_unreachable_error Geocoder.configure(:always_raise => [Errno::EHOSTUNREACH]) Geocoder::Lookup.all_services_with_http_requests.each do |l| - next if l == :maxmind_local || l == :geoip2 # local, does not use cache + next if l == :maxmind_local || l == :geoip2 || l == :ip2location_lite # local, does not use cache lookup = Geocoder::Lookup.get(l) set_api_key!(l) assert_raises Errno::EHOSTUNREACH do diff --git a/test/unit/lookup_test.rb b/test/unit/lookup_test.rb index cb60ddf83..ab4dc0b5c 100644 --- a/test/unit/lookup_test.rb +++ b/test/unit/lookup_test.rb @@ -32,7 +32,7 @@ def test_search_returns_empty_array_when_no_results 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, :ipdata_co, :ipinfo_io, :ipapi_com, :ipregistry, :ipstack, :postcodes_io, :uk_ordnance_survey_names, :amazon_location_service, :ipbase].include? l # does not use query string + next if [:freegeoip, :maxmind_local, :telize, :pointpin, :geoip2, :maxmind_geoip2, :mapbox, :ipdata_co, :ipinfo_io, :ipapi_com, :ipregistry, :ipstack, :postcodes_io, :uk_ordnance_survey_names, :amazon_location_service, :ipbase, :ip2location_lite].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"} diff --git a/test/unit/lookups/ip2location_lite_test.rb b/test/unit/lookups/ip2location_lite_test.rb new file mode 100644 index 000000000..08df262f8 --- /dev/null +++ b/test/unit/lookups/ip2location_lite_test.rb @@ -0,0 +1,13 @@ +# encoding: utf-8 +require 'test_helper' + +class Ip2locationLiteTest < GeocoderTestCase + def setup + Geocoder.configure(ip_lookup: :ip2location_lite, ip2location_lite: { file: File.join('folder', 'test_file') }) + end + + def test_loopback + result = Geocoder.search('127.0.0.1').first + assert_equal '', result.country_short + end +end \ No newline at end of file diff --git a/test/unit/result_test.rb b/test/unit/result_test.rb index c0b0c4cdc..a37411c55 100644 --- a/test/unit/result_test.rb +++ b/test/unit/result_test.rb @@ -8,6 +8,7 @@ def test_forward_geocoding_result_has_required_attributes next if [ :ip2location, # has pay-per-attribute pricing model :ip2location_io, # has pay-per-attribute pricing model + :ip2location_lite, # no forward geocoding :twogis, # cant find 'Madison Square Garden' ].include?(l) @@ -23,6 +24,7 @@ def test_reverse_geocoding_result_has_required_attributes next if [ :ip2location, # has pay-per-attribute pricing model :ip2location_io, # has pay-per-attribute pricing model + :ip2location_lite, # no reverse geocoding :nationaal_georegister_nl, # no reverse geocoding :melissa_street, # reverse geocoding not implemented :twogis, # cant find 'Madison Square Garden' From 84db64c6a954aa428d73265967124a7986c9de35 Mon Sep 17 00:00:00 2001 From: IP2Location Date: Mon, 25 Sep 2023 10:32:07 +0800 Subject: [PATCH 2/4] Update lookup.rb --- lib/geocoder/lookup.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/geocoder/lookup.rb b/lib/geocoder/lookup.rb index 4dca778fb..c2054b847 100644 --- a/lib/geocoder/lookup.rb +++ b/lib/geocoder/lookup.rb @@ -23,7 +23,7 @@ def all_services_except_test # For example, Amazon Location Service uses the AWS gem, not HTTP REST requests, to fetch data. # def all_services_with_http_requests - all_services_except_test - [:amazon_location_service] + all_services_except_test - [:amazon_location_service, :ip2location_lite] end ## From 13b5a0c63926ba7a4f1ef018af5c7f3cfde0f25b Mon Sep 17 00:00:00 2001 From: IP2Location Date: Tue, 26 Sep 2023 08:13:44 +0800 Subject: [PATCH 3/4] Fix missing ip2location_ruby gem --- Gemfile | 1 + test/unit/lookups/ip2location_lite_test.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/Gemfile b/Gemfile index e64517f55..2ff6837f8 100644 --- a/Gemfile +++ b/Gemfile @@ -7,6 +7,7 @@ group :development, :test do gem 'rubyzip' gem 'rails', '~>5.1.0' gem 'test-unit' # needed for Ruby >=2.2.0 + gem 'ip2location_ruby' platforms :jruby do gem 'jruby-openssl' diff --git a/test/unit/lookups/ip2location_lite_test.rb b/test/unit/lookups/ip2location_lite_test.rb index 08df262f8..8ff9f695a 100644 --- a/test/unit/lookups/ip2location_lite_test.rb +++ b/test/unit/lookups/ip2location_lite_test.rb @@ -3,6 +3,7 @@ class Ip2locationLiteTest < GeocoderTestCase def setup + super Geocoder.configure(ip_lookup: :ip2location_lite, ip2location_lite: { file: File.join('folder', 'test_file') }) end From 0e42a7473736c3f04c888c79cab3c760277be8c9 Mon Sep 17 00:00:00 2001 From: IP2Location Date: Tue, 26 Sep 2023 09:20:59 +0800 Subject: [PATCH 4/4] Update ip2location_lite.rb --- lib/geocoder/lookups/ip2location_lite.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/geocoder/lookups/ip2location_lite.rb b/lib/geocoder/lookups/ip2location_lite.rb index a05266af1..396ff0af0 100644 --- a/lib/geocoder/lookups/ip2location_lite.rb +++ b/lib/geocoder/lookups/ip2location_lite.rb @@ -14,8 +14,6 @@ def initialize rescue LoadError raise "Could not load IP2Location DB dependency. To use the IP2LocationLite lookup you must add the #{@gem_name} gem to your Gemfile or have it installed in your system." end - - @i2l = Ip2location.new.open(configuration[:file].to_s) end super end @@ -33,7 +31,8 @@ def required_api_key_parts def results(query) return [] unless configuration[:file] - result = @i2l.get_all(query.to_s) + i2l = Ip2location.new.open(configuration[:file].to_s) + result = i2l.get_all(query.to_s) result.nil? ? [] : [result] end end