geocoder 1.4.9 → 1.6.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +60 -0
  3. data/LICENSE +1 -1
  4. data/README.md +350 -979
  5. data/bin/console +13 -0
  6. data/examples/autoexpire_cache_redis.rb +2 -0
  7. data/lib/easting_northing.rb +171 -0
  8. data/lib/generators/geocoder/config/templates/initializer.rb +1 -1
  9. data/lib/geocoder/cache.rb +4 -0
  10. data/lib/geocoder/calculations.rb +1 -1
  11. data/lib/geocoder/cli.rb +2 -2
  12. data/lib/geocoder/configuration.rb +3 -2
  13. data/lib/geocoder/configuration_hash.rb +4 -4
  14. data/lib/geocoder/exceptions.rb +1 -1
  15. data/lib/geocoder/ip_address.rb +16 -2
  16. data/lib/geocoder/lookup.rb +9 -5
  17. data/lib/geocoder/lookups/abstract_api.rb +46 -0
  18. data/lib/geocoder/lookups/amap.rb +7 -3
  19. data/lib/geocoder/lookups/baidu.rb +14 -10
  20. data/lib/geocoder/lookups/baidu_ip.rb +7 -36
  21. data/lib/geocoder/lookups/ban_data_gouv_fr.rb +18 -5
  22. data/lib/geocoder/lookups/base.rb +27 -4
  23. data/lib/geocoder/lookups/bing.rb +10 -13
  24. data/lib/geocoder/lookups/db_ip_com.rb +9 -6
  25. data/lib/geocoder/lookups/dstk.rb +4 -2
  26. data/lib/geocoder/lookups/esri.rb +44 -28
  27. data/lib/geocoder/lookups/freegeoip.rb +11 -7
  28. data/lib/geocoder/lookups/geocoder_ca.rb +4 -4
  29. data/lib/geocoder/lookups/geocodio.rb +5 -5
  30. data/lib/geocoder/lookups/geoportail_lu.rb +7 -7
  31. data/lib/geocoder/lookups/google.rb +15 -10
  32. data/lib/geocoder/lookups/google_places_details.rb +11 -17
  33. data/lib/geocoder/lookups/google_places_search.rb +30 -4
  34. data/lib/geocoder/lookups/google_premier.rb +14 -0
  35. data/lib/geocoder/lookups/here.rb +29 -23
  36. data/lib/geocoder/lookups/ip2location.rb +67 -0
  37. data/lib/geocoder/lookups/ipapi_com.rb +9 -13
  38. data/lib/geocoder/lookups/ipdata_co.rb +9 -4
  39. data/lib/geocoder/lookups/ipgeolocation.rb +51 -0
  40. data/lib/geocoder/lookups/ipinfo_io.rb +11 -29
  41. data/lib/geocoder/lookups/ipregistry.rb +68 -0
  42. data/lib/geocoder/lookups/ipstack.rb +11 -12
  43. data/lib/geocoder/lookups/latlon.rb +5 -6
  44. data/lib/geocoder/lookups/location_iq.rb +10 -4
  45. data/lib/geocoder/lookups/mapbox.rb +7 -6
  46. data/lib/geocoder/lookups/mapquest.rb +4 -5
  47. data/lib/geocoder/lookups/maxmind.rb +6 -6
  48. data/lib/geocoder/lookups/maxmind_geoip2.rb +8 -7
  49. data/lib/geocoder/lookups/maxmind_local.rb +7 -1
  50. data/lib/geocoder/lookups/nationaal_georegister_nl.rb +38 -0
  51. data/lib/geocoder/lookups/nominatim.rb +4 -4
  52. data/lib/geocoder/lookups/opencagedata.rb +6 -5
  53. data/lib/geocoder/lookups/osmnames.rb +57 -0
  54. data/lib/geocoder/lookups/pelias.rb +8 -9
  55. data/lib/geocoder/lookups/pickpoint.rb +9 -3
  56. data/lib/geocoder/lookups/pointpin.rb +10 -9
  57. data/lib/geocoder/lookups/postcode_anywhere_uk.rb +4 -5
  58. data/lib/geocoder/lookups/postcodes_io.rb +6 -3
  59. data/lib/geocoder/lookups/smarty_streets.rb +26 -11
  60. data/lib/geocoder/lookups/telize.rb +24 -4
  61. data/lib/geocoder/lookups/tencent.rb +59 -0
  62. data/lib/geocoder/lookups/test.rb +4 -0
  63. data/lib/geocoder/lookups/uk_ordnance_survey_names.rb +59 -0
  64. data/lib/geocoder/lookups/yandex.rb +7 -8
  65. data/lib/geocoder/query.rb +14 -0
  66. data/lib/geocoder/railtie.rb +1 -1
  67. data/lib/geocoder/results/abstract_api.rb +146 -0
  68. data/lib/geocoder/results/baidu.rb +10 -14
  69. data/lib/geocoder/results/ban_data_gouv_fr.rb +27 -2
  70. data/lib/geocoder/results/base.rb +13 -1
  71. data/lib/geocoder/results/bing.rb +1 -1
  72. data/lib/geocoder/results/db_ip_com.rb +1 -6
  73. data/lib/geocoder/results/freegeoip.rb +0 -5
  74. data/lib/geocoder/results/geocoder_ca.rb +3 -3
  75. data/lib/geocoder/results/geoip2.rb +0 -4
  76. data/lib/geocoder/results/geoportail_lu.rb +5 -3
  77. data/lib/geocoder/results/here.rb +4 -1
  78. data/lib/geocoder/results/ip2location.rb +22 -0
  79. data/lib/geocoder/results/ipdata_co.rb +0 -5
  80. data/lib/geocoder/results/ipgeolocation.rb +59 -0
  81. data/lib/geocoder/results/ipregistry.rb +304 -0
  82. data/lib/geocoder/results/maxmind.rb +0 -5
  83. data/lib/geocoder/results/maxmind_local.rb +0 -5
  84. data/lib/geocoder/results/nationaal_georegister_nl.rb +62 -0
  85. data/lib/geocoder/results/nominatim.rb +4 -0
  86. data/lib/geocoder/results/osmnames.rb +56 -0
  87. data/lib/geocoder/results/smarty_streets.rb +48 -18
  88. data/lib/geocoder/results/telize.rb +0 -5
  89. data/lib/geocoder/results/tencent.rb +72 -0
  90. data/lib/geocoder/results/test.rb +1 -1
  91. data/lib/geocoder/results/uk_ordnance_survey_names.rb +59 -0
  92. data/lib/geocoder/results/yandex.rb +217 -59
  93. data/lib/geocoder/sql.rb +4 -4
  94. data/lib/geocoder/stores/active_record.rb +1 -3
  95. data/lib/geocoder/util.rb +29 -0
  96. data/lib/geocoder/version.rb +1 -1
  97. data/lib/maxmind_database.rb +3 -3
  98. metadata +27 -23
  99. data/lib/geocoder/lookups/geocoder_us.rb +0 -43
  100. data/lib/geocoder/lookups/mapzen.rb +0 -15
  101. data/lib/geocoder/lookups/okf.rb +0 -44
  102. data/lib/geocoder/lookups/ovi.rb +0 -62
  103. data/lib/geocoder/results/geocoder_us.rb +0 -39
  104. data/lib/geocoder/results/mapzen.rb +0 -5
  105. data/lib/geocoder/results/okf.rb +0 -106
  106. data/lib/geocoder/results/ovi.rb +0 -71
  107. data/lib/hash_recursive_merge.rb +0 -74
@@ -8,12 +8,12 @@ module Geocoder::Lookup
8
8
  "Geocoder.ca"
9
9
  end
10
10
 
11
- def query_url(query)
12
- "#{protocol}://geocoder.ca/?" + url_query_string(query)
13
- end
14
-
15
11
  private # ---------------------------------------------------------------
16
12
 
13
+ def base_query_url(query)
14
+ "#{protocol}://geocoder.ca/?"
15
+ end
16
+
17
17
  def results(query)
18
18
  return [] unless doc = fetch_data(query)
19
19
  if doc['error'].nil?
@@ -8,11 +8,6 @@ module Geocoder::Lookup
8
8
  "Geocodio"
9
9
  end
10
10
 
11
- def query_url(query)
12
- path = query.reverse_geocode? ? "reverse" : "geocode"
13
- "#{protocol}://api.geocod.io/v1.3/#{path}?#{url_query_string(query)}"
14
- end
15
-
16
11
  def results(query)
17
12
  return [] unless doc = fetch_data(query)
18
13
  return doc["results"] if doc['error'].nil?
@@ -32,6 +27,11 @@ module Geocoder::Lookup
32
27
 
33
28
  private # ---------------------------------------------------------------
34
29
 
30
+ def base_query_url(query)
31
+ path = query.reverse_geocode? ? "reverse" : "geocode"
32
+ "#{protocol}://api.geocod.io/v1.6/#{path}?"
33
+ end
34
+
35
35
  def query_url_params(query)
36
36
  {
37
37
  :api_key => configuration.api_key,
@@ -9,14 +9,14 @@ module Geocoder
9
9
  "Geoportail.lu"
10
10
  end
11
11
 
12
- def query_url(query)
13
- url_base_path(query) + url_query_string(query)
14
- end
15
-
16
- private
12
+ private # ---------------------------------------------------------------
17
13
 
18
- def url_base_path(query)
19
- query.reverse_geocode? ? reverse_geocode_url_base_path : search_url_base_path
14
+ def base_query_url(query)
15
+ if query.reverse_geocode?
16
+ reverse_geocode_url_base_path
17
+ else
18
+ search_url_base_path
19
+ end
20
20
  end
21
21
 
22
22
  def search_url_base_path
@@ -21,12 +21,12 @@ module Geocoder::Lookup
21
21
  end
22
22
  end
23
23
 
24
- def query_url(query)
25
- "#{protocol}://maps.googleapis.com/maps/api/geocode/json?" + url_query_string(query)
26
- end
27
-
28
24
  private # ---------------------------------------------------------------
29
25
 
26
+ def base_query_url(query)
27
+ "#{protocol}://maps.googleapis.com/maps/api/geocode/json?"
28
+ end
29
+
30
30
  def configure_ssl!(client)
31
31
  client.instance_eval {
32
32
  @ssl_context = OpenSSL::SSL::SSLContext.new
@@ -44,19 +44,24 @@ module Geocoder::Lookup
44
44
  super(response) and ['OK', 'ZERO_RESULTS'].include?(status)
45
45
  end
46
46
 
47
+ def result_root_attr
48
+ 'results'
49
+ end
50
+
47
51
  def results(query)
48
52
  return [] unless doc = fetch_data(query)
49
- case doc['status']; when "OK" # OK status implies >0 results
50
- return doc['results']
53
+ case doc['status']
54
+ when "OK" # OK status implies >0 results
55
+ return doc[result_root_attr]
51
56
  when "OVER_QUERY_LIMIT"
52
57
  raise_error(Geocoder::OverQueryLimitError) ||
53
58
  Geocoder.log(:warn, "#{name} API error: over query limit.")
54
59
  when "REQUEST_DENIED"
55
- raise_error(Geocoder::RequestDenied) ||
56
- Geocoder.log(:warn, "#{name} API error: request denied.")
60
+ raise_error(Geocoder::RequestDenied, doc['error_message']) ||
61
+ Geocoder.log(:warn, "#{name} API error: request denied (#{doc['error_message']}).")
57
62
  when "INVALID_REQUEST"
58
- raise_error(Geocoder::InvalidRequest) ||
59
- Geocoder.log(:warn, "#{name} API error: invalid request.")
63
+ raise_error(Geocoder::InvalidRequest, doc['error_message']) ||
64
+ Geocoder.log(:warn, "#{name} API error: invalid request (#{doc['error_message']}).")
60
65
  end
61
66
  return []
62
67
  end
@@ -16,27 +16,21 @@ module Geocoder
16
16
  [:https]
17
17
  end
18
18
 
19
- def query_url(query)
20
- "#{protocol}://maps.googleapis.com/maps/api/place/details/json?#{url_query_string(query)}"
19
+ private
20
+
21
+ def base_query_url(query)
22
+ "#{protocol}://maps.googleapis.com/maps/api/place/details/json?"
21
23
  end
22
24
 
23
- private
25
+ def result_root_attr
26
+ 'result'
27
+ end
24
28
 
25
29
  def results(query)
26
- return [] unless doc = fetch_data(query)
27
-
28
- case doc["status"]
29
- when "OK"
30
- return [doc["result"]]
31
- when "OVER_QUERY_LIMIT"
32
- raise_error(Geocoder::OverQueryLimitError) || Geocoder.log(:warn, "Google Places Details API error: over query limit.")
33
- when "REQUEST_DENIED"
34
- raise_error(Geocoder::RequestDenied) || Geocoder.log(:warn, "Google Places Details API error: request denied.")
35
- when "INVALID_REQUEST"
36
- raise_error(Geocoder::InvalidRequest) || Geocoder.log(:warn, "Google Places Details API error: invalid request.")
37
- end
38
-
39
- []
30
+ result = super(query)
31
+ return [result] unless result.is_a? Array
32
+
33
+ result
40
34
  end
41
35
 
42
36
  def query_url_google_params(query)
@@ -16,18 +16,44 @@ module Geocoder
16
16
  [:https]
17
17
  end
18
18
 
19
- def query_url(query)
20
- "#{protocol}://maps.googleapis.com/maps/api/place/textsearch/json?#{url_query_string(query)}"
19
+ private
20
+
21
+ def result_root_attr
22
+ 'candidates'
21
23
  end
22
24
 
23
- private
25
+ def base_query_url(query)
26
+ "#{protocol}://maps.googleapis.com/maps/api/place/findplacefromtext/json?"
27
+ end
24
28
 
25
29
  def query_url_google_params(query)
26
30
  {
27
- query: query.text,
31
+ input: query.text,
32
+ inputtype: 'textquery',
33
+ fields: fields(query),
28
34
  language: query.language || configuration.language
29
35
  }
30
36
  end
37
+
38
+ def fields(query)
39
+ query_fields = query.options[:fields]
40
+ return format_fields(query_fields) if query_fields
41
+
42
+ default_fields
43
+ end
44
+
45
+ def default_fields
46
+ legacy = %w[id reference]
47
+ basic = %w[business_status formatted_address geometry icon name
48
+ photos place_id plus_code types]
49
+ contact = %w[opening_hours]
50
+ atmosphere = %W[price_level rating user_ratings_total]
51
+ format_fields(legacy, basic, contact, atmosphere)
52
+ end
53
+
54
+ def format_fields(*fields)
55
+ fields.flatten.join(',')
56
+ end
31
57
  end
32
58
  end
33
59
  end
@@ -21,6 +21,20 @@ module Geocoder::Lookup
21
21
 
22
22
  private # ---------------------------------------------------------------
23
23
 
24
+ def result_root_attr
25
+ 'results'
26
+ end
27
+
28
+ def cache_key(query)
29
+ "#{protocol}://maps.googleapis.com/maps/api/geocode/json?" + hash_to_query(cache_key_params(query))
30
+ end
31
+
32
+ def cache_key_params(query)
33
+ query_url_google_params(query).merge(super).reject do |k,v|
34
+ [:key, :client, :channel].include?(k)
35
+ end
36
+ end
37
+
24
38
  def query_url_params(query)
25
39
  query_url_google_params(query).merge(super).merge(
26
40
  :key => nil, # don't use param inherited from Google lookup
@@ -9,15 +9,19 @@ module Geocoder::Lookup
9
9
  end
10
10
 
11
11
  def required_api_key_parts
12
- ["app_id", "app_code"]
12
+ ['api_key']
13
13
  end
14
14
 
15
- def query_url(query)
16
- "#{protocol}://#{if query.reverse_geocode? then 'reverse.' end}geocoder.api.here.com/6.2/#{if query.reverse_geocode? then 'reverse' end}geocode.json?" + url_query_string(query)
15
+ def supported_protocols
16
+ [:https]
17
17
  end
18
18
 
19
19
  private # ---------------------------------------------------------------
20
20
 
21
+ def base_query_url(query)
22
+ "#{protocol}://#{if query.reverse_geocode? then 'reverse.' end}geocoder.ls.hereapi.com/6.2/#{if query.reverse_geocode? then 'reverse' end}geocode.json?"
23
+ end
24
+
21
25
  def results(query)
22
26
  return [] unless doc = fetch_data(query)
23
27
  return [] unless doc['Response'] && doc['Response']['View']
@@ -28,34 +32,36 @@ module Geocoder::Lookup
28
32
  []
29
33
  end
30
34
 
31
- def query_url_params(query)
35
+ def query_url_here_options(query, reverse_geocode)
32
36
  options = {
33
- :gen=>4,
34
- :app_id=>api_key,
35
- :app_code=>api_code
37
+ gen: 9,
38
+ apikey: configuration.api_key,
39
+ language: (query.language || configuration.language)
36
40
  }
41
+ if reverse_geocode
42
+ options[:mode] = :retrieveAddresses
43
+ return options
44
+ end
37
45
 
38
- if query.reverse_geocode?
39
- super.merge(options).merge(
40
- :prox=>query.sanitized_text,
41
- :mode=>:retrieveAddresses
42
- )
43
- else
44
- super.merge(options).merge(
45
- :searchtext=>query.sanitized_text
46
- )
46
+ unless (country = query.options[:country]).nil?
47
+ options[:country] = country
47
48
  end
48
- end
49
49
 
50
- def api_key
51
- if a=configuration.api_key
52
- return a.first if a.is_a?(Array)
50
+ unless (mapview = query.options[:bounds]).nil?
51
+ options[:mapview] = mapview.map{ |point| "%f,%f" % point }.join(';')
53
52
  end
53
+ options
54
54
  end
55
55
 
56
- def api_code
57
- if a=configuration.api_key
58
- return a.last if a.is_a?(Array)
56
+ def query_url_params(query)
57
+ if query.reverse_geocode?
58
+ super.merge(query_url_here_options(query, true)).merge(
59
+ prox: query.sanitized_text
60
+ )
61
+ else
62
+ super.merge(query_url_here_options(query, false)).merge(
63
+ searchtext: query.sanitized_text
64
+ )
59
65
  end
60
66
  end
61
67
  end
@@ -0,0 +1,67 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/ip2location'
3
+
4
+ module Geocoder::Lookup
5
+ class Ip2location < Base
6
+
7
+ def name
8
+ "IP2LocationApi"
9
+ end
10
+
11
+ def supported_protocols
12
+ [:http, :https]
13
+ end
14
+
15
+ private # ----------------------------------------------------------------
16
+
17
+ def base_query_url(query)
18
+ "#{protocol}://api.ip2location.com/?"
19
+ end
20
+
21
+ def query_url_params(query)
22
+ params = super
23
+ if configuration.has_key?(:package)
24
+ params.merge!(package: configuration[:package])
25
+ end
26
+ params
27
+ end
28
+
29
+ def results(query)
30
+ # don't look up a loopback or private address, just return the stored result
31
+ return [reserved_result(query.text)] if query.internal_ip_address?
32
+ return [] unless doc = fetch_data(query)
33
+ if doc["response"] == "INVALID ACCOUNT"
34
+ raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "INVALID ACCOUNT")
35
+ return []
36
+ else
37
+ return [doc]
38
+ end
39
+ end
40
+
41
+ def reserved_result(query)
42
+ {
43
+ "country_code" => "INVALID IP ADDRESS",
44
+ "country_name" => "INVALID IP ADDRESS",
45
+ "region_name" => "INVALID IP ADDRESS",
46
+ "city_name" => "INVALID IP ADDRESS",
47
+ "latitude" => "INVALID IP ADDRESS",
48
+ "longitude" => "INVALID IP ADDRESS",
49
+ "zip_code" => "INVALID IP ADDRESS",
50
+ "time_zone" => "INVALID IP ADDRESS",
51
+ "isp" => "INVALID IP ADDRESS",
52
+ "domain" => "INVALID IP ADDRESS",
53
+ "net_speed" => "INVALID IP ADDRESS",
54
+ "idd_code" => "INVALID IP ADDRESS",
55
+ "area_code" => "INVALID IP ADDRESS",
56
+ "weather_station_code" => "INVALID IP ADDRESS",
57
+ "weather_station_name" => "INVALID IP ADDRESS",
58
+ "mcc" => "INVALID IP ADDRESS",
59
+ "mnc" => "INVALID IP ADDRESS",
60
+ "mobile_brand" => "INVALID IP ADDRESS",
61
+ "elevation" => "INVALID IP ADDRESS",
62
+ "usage_type" => "INVALID IP ADDRESS"
63
+ }
64
+ end
65
+
66
+ end
67
+ end
@@ -8,17 +8,6 @@ module Geocoder::Lookup
8
8
  "ip-api.com"
9
9
  end
10
10
 
11
- def query_url(query)
12
- domain = configuration.api_key ? "pro.ip-api.com" : "ip-api.com"
13
- url_ = "#{protocol}://#{domain}/json/#{query.sanitized_text}"
14
-
15
- if (params = url_query_string(query)) && !params.empty?
16
- url_ + "?" + params
17
- else
18
- url_
19
- end
20
- end
21
-
22
11
  def supported_protocols
23
12
  if configuration.api_key
24
13
  [:http, :https]
@@ -27,8 +16,14 @@ module Geocoder::Lookup
27
16
  end
28
17
  end
29
18
 
19
+ private # ----------------------------------------------------------------
30
20
 
31
- private
21
+ def base_query_url(query)
22
+ domain = configuration.api_key ? "pro.ip-api.com" : "ip-api.com"
23
+ url = "#{protocol}://#{domain}/json/#{query.sanitized_text}"
24
+ url << "?" if not url_query_string(query).empty?
25
+ url
26
+ end
32
27
 
33
28
  def parse_raw_data(raw_data)
34
29
  if raw_data.chomp == "invalid key"
@@ -39,7 +34,8 @@ module Geocoder::Lookup
39
34
  end
40
35
 
41
36
  def results(query)
42
- return [reserved_result(query.text)] if query.loopback_ip_address?
37
+ # don't look up a loopback or private address, just return the stored result
38
+ return [reserved_result(query.text)] if query.internal_ip_address?
43
39
 
44
40
  return [] unless doc = fetch_data(query)
45
41
 
@@ -13,15 +13,20 @@ module Geocoder::Lookup
13
13
  end
14
14
 
15
15
  def query_url(query)
16
- "#{protocol}://#{host}/#{query.sanitized_text}"
16
+ # Ipdata.co requires that the API key be sent as a query parameter.
17
+ # It no longer supports API keys sent as headers.
18
+ "#{protocol}://#{host}/#{query.sanitized_text}?api-key=#{configuration.api_key}"
17
19
  end
18
20
 
19
21
  private # ---------------------------------------------------------------
20
22
 
23
+ def cache_key(query)
24
+ query_url(query)
25
+ end
26
+
21
27
  def results(query)
22
- Geocoder.configure(:ipdata_co => {:http_headers => { "api-key" => configuration.api_key }}) if configuration.api_key
23
- # don't look up a loopback address, just return the stored result
24
- return [reserved_result(query.text)] if query.loopback_ip_address?
28
+ # don't look up a loopback or private address, just return the stored result
29
+ return [reserved_result(query.text)] if query.internal_ip_address?
25
30
  # note: Ipdata.co returns plain text on bad request
26
31
  (doc = fetch_data(query)) ? [doc] : []
27
32
  end
@@ -0,0 +1,51 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/ipgeolocation'
3
+
4
+
5
+ module Geocoder::Lookup
6
+ class Ipgeolocation < Base
7
+
8
+ ERROR_CODES = {
9
+ 400 => Geocoder::RequestDenied, # subscription is paused
10
+ 401 => Geocoder::InvalidApiKey, # missing/invalid API key
11
+ 403 => Geocoder::InvalidRequest, # invalid IP address
12
+ 404 => Geocoder::InvalidRequest, # not found
13
+ 423 => Geocoder::InvalidRequest # bogon/reserved IP address
14
+ }
15
+ ERROR_CODES.default = Geocoder::Error
16
+
17
+ def name
18
+ "Ipgeolocation"
19
+ end
20
+
21
+ def supported_protocols
22
+ [:https]
23
+ end
24
+
25
+ private # ----------------------------------------------------------------
26
+
27
+ def base_query_url(query)
28
+ "#{protocol}://api.ipgeolocation.io/ipgeo?"
29
+ end
30
+ def query_url_params(query)
31
+ {
32
+ ip: query.sanitized_text,
33
+ apiKey: configuration.api_key
34
+ }.merge(super)
35
+ end
36
+
37
+ def results(query)
38
+ # don't look up a loopback or private address, just return the stored result
39
+ return [reserved_result(query.text)] if query.internal_ip_address?
40
+ [fetch_data(query)]
41
+ end
42
+
43
+ def reserved_result(ip)
44
+ {
45
+ "ip" => ip,
46
+ "country_name" => "Reserved",
47
+ "country_code2" => "RD"
48
+ }
49
+ end
50
+ end
51
+ end