really-broken-geocoder 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +557 -0
  3. data/LICENSE +20 -0
  4. data/README.md +3 -0
  5. data/bin/geocode +5 -0
  6. data/examples/autoexpire_cache_dalli.rb +62 -0
  7. data/examples/autoexpire_cache_redis.rb +28 -0
  8. data/examples/cache_bypass.rb +48 -0
  9. data/examples/reverse_geocode_job.rb +40 -0
  10. data/lib/generators/geocoder/config/config_generator.rb +14 -0
  11. data/lib/generators/geocoder/config/templates/initializer.rb +22 -0
  12. data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +30 -0
  13. data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +30 -0
  14. data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +30 -0
  15. data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +17 -0
  16. data/lib/generators/geocoder/migration_version.rb +15 -0
  17. data/lib/geocoder.rb +48 -0
  18. data/lib/geocoder/cache.rb +94 -0
  19. data/lib/geocoder/calculations.rb +420 -0
  20. data/lib/geocoder/cli.rb +121 -0
  21. data/lib/geocoder/configuration.rb +137 -0
  22. data/lib/geocoder/configuration_hash.rb +11 -0
  23. data/lib/geocoder/esri_token.rb +38 -0
  24. data/lib/geocoder/exceptions.rb +40 -0
  25. data/lib/geocoder/ip_address.rb +26 -0
  26. data/lib/geocoder/kernel_logger.rb +25 -0
  27. data/lib/geocoder/logger.rb +47 -0
  28. data/lib/geocoder/lookup.rb +118 -0
  29. data/lib/geocoder/lookups/amap.rb +63 -0
  30. data/lib/geocoder/lookups/baidu.rb +63 -0
  31. data/lib/geocoder/lookups/baidu_ip.rb +30 -0
  32. data/lib/geocoder/lookups/ban_data_gouv_fr.rb +130 -0
  33. data/lib/geocoder/lookups/base.rb +348 -0
  34. data/lib/geocoder/lookups/bing.rb +82 -0
  35. data/lib/geocoder/lookups/db_ip_com.rb +52 -0
  36. data/lib/geocoder/lookups/dstk.rb +22 -0
  37. data/lib/geocoder/lookups/esri.rb +95 -0
  38. data/lib/geocoder/lookups/freegeoip.rb +60 -0
  39. data/lib/geocoder/lookups/geocoder_ca.rb +53 -0
  40. data/lib/geocoder/lookups/geocoder_us.rb +51 -0
  41. data/lib/geocoder/lookups/geocodio.rb +42 -0
  42. data/lib/geocoder/lookups/geoip2.rb +45 -0
  43. data/lib/geocoder/lookups/geoportail_lu.rb +65 -0
  44. data/lib/geocoder/lookups/google.rb +95 -0
  45. data/lib/geocoder/lookups/google_places_details.rb +50 -0
  46. data/lib/geocoder/lookups/google_places_search.rb +33 -0
  47. data/lib/geocoder/lookups/google_premier.rb +57 -0
  48. data/lib/geocoder/lookups/here.rb +77 -0
  49. data/lib/geocoder/lookups/ip2location.rb +75 -0
  50. data/lib/geocoder/lookups/ipapi_com.rb +82 -0
  51. data/lib/geocoder/lookups/ipdata_co.rb +62 -0
  52. data/lib/geocoder/lookups/ipinfo_io.rb +44 -0
  53. data/lib/geocoder/lookups/ipstack.rb +63 -0
  54. data/lib/geocoder/lookups/latlon.rb +59 -0
  55. data/lib/geocoder/lookups/location_iq.rb +50 -0
  56. data/lib/geocoder/lookups/mapbox.rb +59 -0
  57. data/lib/geocoder/lookups/mapquest.rb +58 -0
  58. data/lib/geocoder/lookups/maxmind.rb +90 -0
  59. data/lib/geocoder/lookups/maxmind_geoip2.rb +70 -0
  60. data/lib/geocoder/lookups/maxmind_local.rb +65 -0
  61. data/lib/geocoder/lookups/nominatim.rb +64 -0
  62. data/lib/geocoder/lookups/opencagedata.rb +65 -0
  63. data/lib/geocoder/lookups/pelias.rb +63 -0
  64. data/lib/geocoder/lookups/pickpoint.rb +41 -0
  65. data/lib/geocoder/lookups/pointpin.rb +69 -0
  66. data/lib/geocoder/lookups/postcode_anywhere_uk.rb +50 -0
  67. data/lib/geocoder/lookups/postcodes_io.rb +31 -0
  68. data/lib/geocoder/lookups/smarty_streets.rb +63 -0
  69. data/lib/geocoder/lookups/telize.rb +75 -0
  70. data/lib/geocoder/lookups/tencent.rb +59 -0
  71. data/lib/geocoder/lookups/test.rb +44 -0
  72. data/lib/geocoder/lookups/yandex.rb +62 -0
  73. data/lib/geocoder/models/active_record.rb +51 -0
  74. data/lib/geocoder/models/base.rb +39 -0
  75. data/lib/geocoder/models/mongo_base.rb +62 -0
  76. data/lib/geocoder/models/mongo_mapper.rb +26 -0
  77. data/lib/geocoder/models/mongoid.rb +32 -0
  78. data/lib/geocoder/query.rb +125 -0
  79. data/lib/geocoder/railtie.rb +26 -0
  80. data/lib/geocoder/request.rb +114 -0
  81. data/lib/geocoder/results/amap.rb +87 -0
  82. data/lib/geocoder/results/baidu.rb +79 -0
  83. data/lib/geocoder/results/baidu_ip.rb +62 -0
  84. data/lib/geocoder/results/ban_data_gouv_fr.rb +257 -0
  85. data/lib/geocoder/results/base.rb +79 -0
  86. data/lib/geocoder/results/bing.rb +52 -0
  87. data/lib/geocoder/results/db_ip_com.rb +58 -0
  88. data/lib/geocoder/results/dstk.rb +6 -0
  89. data/lib/geocoder/results/esri.rb +75 -0
  90. data/lib/geocoder/results/freegeoip.rb +40 -0
  91. data/lib/geocoder/results/geocoder_ca.rb +60 -0
  92. data/lib/geocoder/results/geocoder_us.rb +39 -0
  93. data/lib/geocoder/results/geocodio.rb +78 -0
  94. data/lib/geocoder/results/geoip2.rb +76 -0
  95. data/lib/geocoder/results/geoportail_lu.rb +71 -0
  96. data/lib/geocoder/results/google.rb +150 -0
  97. data/lib/geocoder/results/google_places_details.rb +39 -0
  98. data/lib/geocoder/results/google_places_search.rb +52 -0
  99. data/lib/geocoder/results/google_premier.rb +6 -0
  100. data/lib/geocoder/results/here.rb +79 -0
  101. data/lib/geocoder/results/ip2location.rb +22 -0
  102. data/lib/geocoder/results/ipapi_com.rb +45 -0
  103. data/lib/geocoder/results/ipdata_co.rb +40 -0
  104. data/lib/geocoder/results/ipinfo_io.rb +48 -0
  105. data/lib/geocoder/results/ipstack.rb +60 -0
  106. data/lib/geocoder/results/latlon.rb +71 -0
  107. data/lib/geocoder/results/location_iq.rb +6 -0
  108. data/lib/geocoder/results/mapbox.rb +57 -0
  109. data/lib/geocoder/results/mapquest.rb +48 -0
  110. data/lib/geocoder/results/maxmind.rb +130 -0
  111. data/lib/geocoder/results/maxmind_geoip2.rb +9 -0
  112. data/lib/geocoder/results/maxmind_local.rb +44 -0
  113. data/lib/geocoder/results/nominatim.rb +109 -0
  114. data/lib/geocoder/results/opencagedata.rb +100 -0
  115. data/lib/geocoder/results/pelias.rb +58 -0
  116. data/lib/geocoder/results/pickpoint.rb +6 -0
  117. data/lib/geocoder/results/pointpin.rb +40 -0
  118. data/lib/geocoder/results/postcode_anywhere_uk.rb +42 -0
  119. data/lib/geocoder/results/postcodes_io.rb +40 -0
  120. data/lib/geocoder/results/smarty_streets.rb +142 -0
  121. data/lib/geocoder/results/telize.rb +40 -0
  122. data/lib/geocoder/results/tencent.rb +72 -0
  123. data/lib/geocoder/results/test.rb +33 -0
  124. data/lib/geocoder/results/yandex.rb +134 -0
  125. data/lib/geocoder/sql.rb +110 -0
  126. data/lib/geocoder/stores/active_record.rb +328 -0
  127. data/lib/geocoder/stores/base.rb +115 -0
  128. data/lib/geocoder/stores/mongo_base.rb +58 -0
  129. data/lib/geocoder/stores/mongo_mapper.rb +13 -0
  130. data/lib/geocoder/stores/mongoid.rb +13 -0
  131. data/lib/geocoder/version.rb +3 -0
  132. data/lib/hash_recursive_merge.rb +74 -0
  133. data/lib/maxmind_database.rb +109 -0
  134. data/lib/tasks/geocoder.rake +54 -0
  135. data/lib/tasks/maxmind.rake +73 -0
  136. metadata +186 -0
@@ -0,0 +1,90 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/maxmind'
3
+ require 'csv'
4
+
5
+ module Geocoder::Lookup
6
+ class Maxmind < Base
7
+
8
+ def name
9
+ "MaxMind"
10
+ end
11
+
12
+ private # ---------------------------------------------------------------
13
+
14
+ def base_query_url(query)
15
+ "#{protocol}://geoip.maxmind.com/#{service_code}?"
16
+ end
17
+
18
+ ##
19
+ # Return the name of the configured service, or raise an exception.
20
+ #
21
+ def configured_service!
22
+ if s = configuration[:service] and services.keys.include?(s)
23
+ return s
24
+ else
25
+ raise(
26
+ Geocoder::ConfigurationError,
27
+ "When using MaxMind you MUST specify a service name: " +
28
+ "Geocoder.configure(:maxmind => {:service => ...}), " +
29
+ "where '...' is one of: #{services.keys.inspect}"
30
+ )
31
+ end
32
+ end
33
+
34
+ def service_code
35
+ services[configured_service!]
36
+ end
37
+
38
+ def service_response_fields_count
39
+ Geocoder::Result::Maxmind.field_names[configured_service!].size
40
+ end
41
+
42
+ def data_contains_error?(parsed_data)
43
+ # if all fields given then there is an error
44
+ parsed_data.size == service_response_fields_count and !parsed_data.last.nil?
45
+ end
46
+
47
+ ##
48
+ # Service names mapped to code used in URL.
49
+ #
50
+ def services
51
+ {
52
+ :country => "a",
53
+ :city => "b",
54
+ :city_isp_org => "f",
55
+ :omni => "e"
56
+ }
57
+ end
58
+
59
+ def results(query)
60
+ # don't look up a loopback or private address, just return the stored result
61
+ return [reserved_result] if query.internal_ip_address?
62
+ doc = fetch_data(query)
63
+ if doc and doc.is_a?(Array)
64
+ if !data_contains_error?(doc)
65
+ return [doc]
66
+ elsif doc.last == "INVALID_LICENSE_KEY"
67
+ raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid MaxMind API key.")
68
+ end
69
+ end
70
+ return []
71
+ end
72
+
73
+ def parse_raw_data(raw_data)
74
+ # Maxmind just returns text/plain as csv format but according to documentation,
75
+ # we get ISO-8859-1 encoded string. We need to convert it.
76
+ CSV.parse_line raw_data.force_encoding("ISO-8859-1").encode("UTF-8")
77
+ end
78
+
79
+ def reserved_result
80
+ ",,,,0,0,0,0,,,".split(",")
81
+ end
82
+
83
+ def query_url_params(query)
84
+ {
85
+ :l => configuration.api_key,
86
+ :i => query.sanitized_text
87
+ }.merge(super)
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,70 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/maxmind_geoip2'
3
+
4
+ module Geocoder::Lookup
5
+ class MaxmindGeoip2 < Base
6
+
7
+ def name
8
+ "MaxMind GeoIP2"
9
+ end
10
+
11
+ # Maxmind's GeoIP2 Precision Services only supports HTTPS,
12
+ # otherwise a `404 Not Found` HTTP response will be returned
13
+ def supported_protocols
14
+ [:https]
15
+ end
16
+
17
+ def query_url(query)
18
+ "#{protocol}://geoip.maxmind.com/geoip/v2.1/#{configured_service!}/#{query.sanitized_text.strip}"
19
+ end
20
+
21
+ private # ---------------------------------------------------------------
22
+
23
+ def cache_key(query)
24
+ query_url(query)
25
+ end
26
+
27
+ ##
28
+ # Return the name of the configured service, or raise an exception.
29
+ #
30
+ def configured_service!
31
+ if s = configuration[:service] and services.include?(s) and configuration[:basic_auth][:user] and configuration[:basic_auth][:password]
32
+ return s
33
+ else
34
+ raise(
35
+ Geocoder::ConfigurationError, "When using MaxMind GeoIP2 you must specify a service and credentials: Geocoder.configure(maxmind_geoip2: {service: ..., basic_auth: {user: ..., password: ...}}), where service is one of: #{services.inspect}"
36
+ )
37
+ end
38
+ end
39
+
40
+ def data_contains_error?(doc)
41
+ (["code", "error"] - doc.keys).empty?
42
+ end
43
+
44
+ ##
45
+ # Service names used in URL.
46
+ #
47
+ def services
48
+ [
49
+ :country,
50
+ :city,
51
+ :insights,
52
+ ]
53
+ end
54
+
55
+ def results(query)
56
+ # don't look up a loopback or private address, just return the stored result
57
+ return [] if query.internal_ip_address?
58
+
59
+ doc = fetch_data(query)
60
+ if doc
61
+ if !data_contains_error?(doc)
62
+ return [doc]
63
+ else
64
+ Geocoder.log(:warn, "MaxMind GeoIP2 Geocoding API error: #{doc['code']} (#{doc['error']}).")
65
+ end
66
+ end
67
+ return []
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,65 @@
1
+ require 'ipaddr'
2
+ require 'geocoder/lookups/base'
3
+ require 'geocoder/results/maxmind_local'
4
+
5
+ module Geocoder::Lookup
6
+ class MaxmindLocal < Base
7
+
8
+ def initialize
9
+ if !configuration[:file].nil?
10
+ begin
11
+ gem = RUBY_PLATFORM == 'java' ? 'jgeoip' : 'geoip'
12
+ require gem
13
+ rescue LoadError
14
+ raise "Could not load geoip dependency. To use MaxMind Local lookup you must add the #{gem} gem to your Gemfile or have it installed in your system."
15
+ end
16
+ end
17
+ super
18
+ end
19
+
20
+ def name
21
+ "MaxMind Local"
22
+ end
23
+
24
+ def required_api_key_parts
25
+ []
26
+ end
27
+
28
+ private
29
+
30
+ def results(query)
31
+ if configuration[:file]
32
+ geoip_class = RUBY_PLATFORM == "java" ? JGeoIP : GeoIP
33
+ result = geoip_class.new(configuration[:file]).city(query.to_s)
34
+ result.nil? ? [] : [encode_hash(result.to_hash)]
35
+ elsif configuration[:package] == :city
36
+ addr = IPAddr.new(query.text).to_i
37
+ q = "SELECT l.country, l.region, l.city, l.latitude, l.longitude
38
+ FROM maxmind_geolite_city_location l WHERE l.loc_id = (SELECT b.loc_id FROM maxmind_geolite_city_blocks b
39
+ WHERE b.start_ip_num <= #{addr} AND #{addr} <= b.end_ip_num)"
40
+ format_result(q, [:country_name, :region_name, :city_name, :latitude, :longitude])
41
+ elsif configuration[:package] == :country
42
+ addr = IPAddr.new(query.text).to_i
43
+ q = "SELECT country, country_code FROM maxmind_geolite_country
44
+ WHERE start_ip_num <= #{addr} AND #{addr} <= end_ip_num"
45
+ format_result(q, [:country_name, :country_code2])
46
+ end
47
+ end
48
+
49
+ def encode_hash(hash, encoding = "UTF-8")
50
+ hash.inject({}) do |h,i|
51
+ h[i[0]] = i[1].is_a?(String) ? i[1].encode(encoding) : i[1]
52
+ h
53
+ end
54
+ end
55
+
56
+ def format_result(query, attr_names)
57
+ if r = ActiveRecord::Base.connection.execute(query).first
58
+ r = r.values if r.is_a?(Hash) # some db adapters return Hash, some Array
59
+ [Hash[*attr_names.zip(r).flatten]]
60
+ else
61
+ []
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,64 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/nominatim"
3
+
4
+ module Geocoder::Lookup
5
+ class Nominatim < Base
6
+
7
+ def name
8
+ "Nominatim"
9
+ end
10
+
11
+ def map_link_url(coordinates)
12
+ "https://www.openstreetmap.org/?lat=#{coordinates[0]}&lon=#{coordinates[1]}&zoom=15&layers=M"
13
+ end
14
+
15
+ private # ---------------------------------------------------------------
16
+
17
+ def base_query_url(query)
18
+ method = query.reverse_geocode? ? "reverse" : "search"
19
+ "#{protocol}://#{configured_host}/#{method}?"
20
+ end
21
+
22
+ def configured_host
23
+ configuration[:host] || "nominatim.openstreetmap.org"
24
+ end
25
+
26
+ def use_ssl?
27
+ # nominatim.openstreetmap.org redirects HTTP requests to HTTPS
28
+ if configured_host == "nominatim.openstreetmap.org"
29
+ true
30
+ else
31
+ super
32
+ end
33
+ end
34
+
35
+ def results(query)
36
+ return [] unless doc = fetch_data(query)
37
+ doc.is_a?(Array) ? doc : [doc]
38
+ end
39
+
40
+ def parse_raw_data(raw_data)
41
+ if raw_data.include?("Bandwidth limit exceeded")
42
+ raise_error(Geocoder::OverQueryLimitError) || Geocoder.log(:warn, "Over API query limit.")
43
+ else
44
+ super(raw_data)
45
+ end
46
+ end
47
+
48
+ def query_url_params(query)
49
+ params = {
50
+ :format => "json",
51
+ :addressdetails => "1",
52
+ :"accept-language" => (query.language || configuration.language)
53
+ }.merge(super)
54
+ if query.reverse_geocode?
55
+ lat,lon = query.coordinates
56
+ params[:lat] = lat
57
+ params[:lon] = lon
58
+ else
59
+ params[:q] = query.sanitized_text
60
+ end
61
+ params
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,65 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/opencagedata'
3
+
4
+ module Geocoder::Lookup
5
+ class Opencagedata < Base
6
+
7
+ def name
8
+ "OpenCageData"
9
+ end
10
+
11
+ def required_api_key_parts
12
+ ["key"]
13
+ end
14
+
15
+ private # ----------------------------------------------------------------
16
+
17
+ def base_query_url(query)
18
+ "#{protocol}://api.opencagedata.com/geocode/v1/json?"
19
+ end
20
+
21
+ def results(query)
22
+ return [] unless doc = fetch_data(query)
23
+ # return doc["results"]
24
+
25
+ messages = doc['status']['message']
26
+ case doc['status']['code']
27
+ when 400 # Error with input
28
+ raise_error(Geocoder::InvalidRequest, messages) ||
29
+ Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}")
30
+ when 403 # Key related error
31
+ raise_error(Geocoder::InvalidApiKey, messages) ||
32
+ Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}")
33
+ when 402 # Quata Exceeded
34
+ raise_error(Geocoder::OverQueryLimitError, messages) ||
35
+ Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}")
36
+ when 500 # Unknown error
37
+ raise_error(Geocoder::Error, messages) ||
38
+ Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}")
39
+ end
40
+
41
+ return doc["results"]
42
+ end
43
+
44
+ def query_url_params(query)
45
+ params = {
46
+ :q => query.sanitized_text,
47
+ :key => configuration.api_key,
48
+ :language => (query.language || configuration.language)
49
+ }.merge(super)
50
+
51
+ [:abbrv, :countrycode, :min_confidence, :no_dedupe, :no_annotations, :no_record, :limit].each do |option|
52
+ unless (option_value = query.options[option]).nil?
53
+ params[option] = option_value
54
+ end
55
+ end
56
+
57
+ unless (bounds = query.options[:bounds]).nil?
58
+ params[:bounds] = bounds.map{ |point| "%f,%f" % point }.join(',')
59
+ end
60
+
61
+ params
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,63 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/pelias'
3
+
4
+ module Geocoder::Lookup
5
+ class Pelias < Base
6
+ def name
7
+ 'Pelias'
8
+ end
9
+
10
+ def endpoint
11
+ configuration[:endpoint] || 'localhost'
12
+ end
13
+
14
+ def required_api_key_parts
15
+ ['search-XXXX']
16
+ end
17
+
18
+ private # ----------------------------------------------------------------
19
+
20
+ def base_query_url(query)
21
+ query_type = query.reverse_geocode? ? 'reverse' : 'search'
22
+ "#{protocol}://#{endpoint}/v1/#{query_type}?"
23
+ end
24
+
25
+ def query_url_params(query)
26
+ params = {
27
+ api_key: configuration.api_key
28
+ }.merge(super)
29
+
30
+ if query.reverse_geocode?
31
+ lat, lon = query.coordinates
32
+ params[:'point.lat'] = lat
33
+ params[:'point.lon'] = lon
34
+ else
35
+ params[:text] = query.text
36
+ end
37
+ params
38
+ end
39
+
40
+ def results(query)
41
+ return [] unless doc = fetch_data(query)
42
+
43
+ # not all responses include a meta
44
+ if doc['meta']
45
+ error = doc.fetch('results', {}).fetch('error', {})
46
+ message = error.fetch('type', 'Unknown Error') + ': ' + error.fetch('message', 'No message')
47
+ log_message = 'Pelias Geocoding API error - ' + message
48
+ case doc['meta']['status_code']
49
+ when '200'
50
+ # nothing to see here
51
+ when '403'
52
+ raise_error(Geocoder::RequestDenied, message) || Geocoder.log(:warn, log_message)
53
+ when '429'
54
+ raise_error(Geocoder::OverQueryLimitError, message) || Geocoder.log(:warn, log_message)
55
+ else
56
+ raise_error(Geocoder::Error, message) || Geocoder.log(:warn, log_message)
57
+ end
58
+ end
59
+
60
+ doc['features'] || []
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,41 @@
1
+ require "geocoder/lookups/nominatim"
2
+ require "geocoder/results/pickpoint"
3
+
4
+ module Geocoder::Lookup
5
+ class Pickpoint < Nominatim
6
+ def name
7
+ "Pickpoint"
8
+ end
9
+
10
+ def supported_protocols
11
+ [:https]
12
+ end
13
+
14
+ def required_api_key_parts
15
+ ["api_key"]
16
+ end
17
+
18
+ private # ----------------------------------------------------------------
19
+
20
+ def base_query_url(query)
21
+ method = query.reverse_geocode? ? "reverse" : "forward"
22
+ "#{protocol}://api.pickpoint.io/v1/#{method}?"
23
+ end
24
+
25
+ def query_url_params(query)
26
+ params = {
27
+ key: configuration.api_key
28
+ }.merge(super)
29
+ end
30
+
31
+ def results(query)
32
+ return [] unless doc = fetch_data(query)
33
+
34
+ if !doc.is_a?(Array) && doc['message'] == 'Unauthorized'
35
+ raise_error(Geocoder::InvalidApiKey, 'Unauthorized')
36
+ end
37
+
38
+ doc.is_a?(Array) ? doc : [doc]
39
+ end
40
+ end
41
+ end