geocoder 1.1.9 → 1.3.7

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 (174) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +157 -0
  3. data/README.md +467 -70
  4. data/examples/reverse_geocode_job.rb +40 -0
  5. data/lib/generators/geocoder/config/templates/initializer.rb +16 -16
  6. data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +28 -0
  7. data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +28 -0
  8. data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +30 -0
  9. data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +17 -0
  10. data/lib/geocoder/cache.rb +3 -2
  11. data/lib/geocoder/calculations.rb +44 -2
  12. data/lib/geocoder/configuration.rb +17 -10
  13. data/lib/geocoder/esri_token.rb +38 -0
  14. data/lib/geocoder/exceptions.rb +19 -0
  15. data/lib/geocoder/ip_address.rb +13 -0
  16. data/lib/geocoder/kernel_logger.rb +25 -0
  17. data/lib/geocoder/logger.rb +47 -0
  18. data/lib/geocoder/lookup.rb +32 -8
  19. data/lib/geocoder/lookups/baidu.rb +18 -13
  20. data/lib/geocoder/lookups/baidu_ip.rb +59 -0
  21. data/lib/geocoder/lookups/base.rb +81 -19
  22. data/lib/geocoder/lookups/bing.rb +40 -7
  23. data/lib/geocoder/lookups/esri.rb +42 -5
  24. data/lib/geocoder/lookups/freegeoip.rb +9 -1
  25. data/lib/geocoder/lookups/geocoder_ca.rb +1 -2
  26. data/lib/geocoder/lookups/geocoder_us.rb +6 -2
  27. data/lib/geocoder/lookups/geocodio.rb +42 -0
  28. data/lib/geocoder/lookups/geoip2.rb +45 -0
  29. data/lib/geocoder/lookups/geoportail_lu.rb +65 -0
  30. data/lib/geocoder/lookups/google.rb +29 -5
  31. data/lib/geocoder/lookups/google_places_details.rb +50 -0
  32. data/lib/geocoder/lookups/google_premier.rb +1 -1
  33. data/lib/geocoder/lookups/here.rb +62 -0
  34. data/lib/geocoder/lookups/ipapi_com.rb +86 -0
  35. data/lib/geocoder/lookups/ipinfo_io.rb +55 -0
  36. data/lib/geocoder/lookups/latlon.rb +59 -0
  37. data/lib/geocoder/lookups/mapbox.rb +53 -0
  38. data/lib/geocoder/lookups/mapquest.rb +6 -6
  39. data/lib/geocoder/lookups/mapzen.rb +15 -0
  40. data/lib/geocoder/lookups/maxmind.rb +4 -2
  41. data/lib/geocoder/lookups/maxmind_geoip2.rb +69 -0
  42. data/lib/geocoder/lookups/maxmind_local.rb +65 -0
  43. data/lib/geocoder/lookups/nominatim.rb +9 -1
  44. data/lib/geocoder/lookups/okf.rb +44 -0
  45. data/lib/geocoder/lookups/opencagedata.rb +58 -0
  46. data/lib/geocoder/lookups/pelias.rb +64 -0
  47. data/lib/geocoder/lookups/pointpin.rb +68 -0
  48. data/lib/geocoder/lookups/postcode_anywhere_uk.rb +51 -0
  49. data/lib/geocoder/lookups/smarty_streets.rb +53 -0
  50. data/lib/geocoder/lookups/telize.rb +55 -0
  51. data/lib/geocoder/lookups/yandex.rb +8 -4
  52. data/lib/geocoder/models/active_record.rb +7 -3
  53. data/lib/geocoder/models/base.rb +1 -4
  54. data/lib/geocoder/models/mongo_base.rb +6 -4
  55. data/lib/geocoder/query.rb +9 -5
  56. data/lib/geocoder/railtie.rb +1 -1
  57. data/lib/geocoder/request.rb +74 -12
  58. data/lib/geocoder/results/baidu_ip.rb +62 -0
  59. data/lib/geocoder/results/bing.rb +4 -0
  60. data/lib/geocoder/results/esri.rb +30 -6
  61. data/lib/geocoder/results/freegeoip.rb +2 -2
  62. data/lib/geocoder/results/geocodio.rb +70 -0
  63. data/lib/geocoder/results/geoip2.rb +62 -0
  64. data/lib/geocoder/results/geoportail_lu.rb +69 -0
  65. data/lib/geocoder/results/google.rb +15 -0
  66. data/lib/geocoder/results/google_places_details.rb +35 -0
  67. data/lib/geocoder/results/here.rb +71 -0
  68. data/lib/geocoder/results/ipapi_com.rb +45 -0
  69. data/lib/geocoder/results/ipinfo_io.rb +48 -0
  70. data/lib/geocoder/results/latlon.rb +71 -0
  71. data/lib/geocoder/results/mapbox.rb +47 -0
  72. data/lib/geocoder/results/mapquest.rb +5 -8
  73. data/lib/geocoder/results/mapzen.rb +5 -0
  74. data/lib/geocoder/results/maxmind_geoip2.rb +9 -0
  75. data/lib/geocoder/results/maxmind_local.rb +49 -0
  76. data/lib/geocoder/results/nominatim.rb +6 -1
  77. data/lib/geocoder/results/okf.rb +106 -0
  78. data/lib/geocoder/results/opencagedata.rb +90 -0
  79. data/lib/geocoder/results/ovi.rb +9 -0
  80. data/lib/geocoder/results/pelias.rb +58 -0
  81. data/lib/geocoder/results/pointpin.rb +40 -0
  82. data/lib/geocoder/results/postcode_anywhere_uk.rb +42 -0
  83. data/lib/geocoder/results/smarty_streets.rb +106 -0
  84. data/lib/geocoder/results/telize.rb +45 -0
  85. data/lib/geocoder/results/test.rb +20 -3
  86. data/lib/geocoder/results/yandex.rb +18 -6
  87. data/lib/geocoder/sql.rb +16 -15
  88. data/lib/geocoder/stores/active_record.rb +51 -18
  89. data/lib/geocoder/stores/base.rb +8 -12
  90. data/lib/geocoder/stores/mongo_base.rb +0 -31
  91. data/lib/geocoder/version.rb +1 -1
  92. data/lib/geocoder.rb +6 -13
  93. data/lib/maxmind_database.rb +109 -0
  94. data/lib/tasks/geocoder.rake +14 -3
  95. data/lib/tasks/maxmind.rake +73 -0
  96. metadata +59 -85
  97. data/.gitignore +0 -5
  98. data/.travis.yml +0 -27
  99. data/Rakefile +0 -25
  100. data/gemfiles/Gemfile.mongoid-2.4.x +0 -15
  101. data/lib/geocoder/lookups/yahoo.rb +0 -86
  102. data/lib/geocoder/results/yahoo.rb +0 -55
  103. data/lib/oauth_util.rb +0 -112
  104. data/test/active_record_test.rb +0 -15
  105. data/test/cache_test.rb +0 -35
  106. data/test/calculations_test.rb +0 -211
  107. data/test/configuration_test.rb +0 -78
  108. data/test/custom_block_test.rb +0 -32
  109. data/test/error_handling_test.rb +0 -43
  110. data/test/fixtures/baidu_invalid_key +0 -1
  111. data/test/fixtures/baidu_no_results +0 -1
  112. data/test/fixtures/baidu_reverse +0 -1
  113. data/test/fixtures/baidu_shanghai_pearl_tower +0 -12
  114. data/test/fixtures/bing_invalid_key +0 -1
  115. data/test/fixtures/bing_madison_square_garden +0 -40
  116. data/test/fixtures/bing_no_results +0 -16
  117. data/test/fixtures/bing_reverse +0 -42
  118. data/test/fixtures/esri_madison_square_garden +0 -59
  119. data/test/fixtures/esri_no_results +0 -8
  120. data/test/fixtures/esri_reverse +0 -21
  121. data/test/fixtures/freegeoip_74_200_247_59 +0 -12
  122. data/test/fixtures/freegeoip_no_results +0 -1
  123. data/test/fixtures/geocoder_ca_madison_square_garden +0 -1
  124. data/test/fixtures/geocoder_ca_no_results +0 -1
  125. data/test/fixtures/geocoder_ca_reverse +0 -34
  126. data/test/fixtures/geocoder_us_madison_square_garden +0 -1
  127. data/test/fixtures/geocoder_us_no_results +0 -1
  128. data/test/fixtures/google_garbage +0 -456
  129. data/test/fixtures/google_madison_square_garden +0 -57
  130. data/test/fixtures/google_no_city_data +0 -44
  131. data/test/fixtures/google_no_locality +0 -51
  132. data/test/fixtures/google_no_results +0 -4
  133. data/test/fixtures/google_over_limit +0 -4
  134. data/test/fixtures/mapquest_error +0 -16
  135. data/test/fixtures/mapquest_invalid_api_key +0 -16
  136. data/test/fixtures/mapquest_invalid_request +0 -16
  137. data/test/fixtures/mapquest_madison_square_garden +0 -52
  138. data/test/fixtures/mapquest_no_results +0 -16
  139. data/test/fixtures/maxmind_24_24_24_21 +0 -1
  140. data/test/fixtures/maxmind_24_24_24_22 +0 -1
  141. data/test/fixtures/maxmind_24_24_24_23 +0 -1
  142. data/test/fixtures/maxmind_24_24_24_24 +0 -1
  143. data/test/fixtures/maxmind_74_200_247_59 +0 -1
  144. data/test/fixtures/maxmind_invalid_key +0 -1
  145. data/test/fixtures/maxmind_no_results +0 -1
  146. data/test/fixtures/nominatim_madison_square_garden +0 -150
  147. data/test/fixtures/nominatim_no_results +0 -1
  148. data/test/fixtures/ovi_madison_square_garden +0 -72
  149. data/test/fixtures/ovi_no_results +0 -8
  150. data/test/fixtures/yahoo_error +0 -1
  151. data/test/fixtures/yahoo_invalid_key +0 -2
  152. data/test/fixtures/yahoo_madison_square_garden +0 -52
  153. data/test/fixtures/yahoo_no_results +0 -10
  154. data/test/fixtures/yahoo_over_limit +0 -2
  155. data/test/fixtures/yandex_invalid_key +0 -1
  156. data/test/fixtures/yandex_kremlin +0 -48
  157. data/test/fixtures/yandex_no_city_and_town +0 -112
  158. data/test/fixtures/yandex_no_results +0 -16
  159. data/test/geocoder_test.rb +0 -59
  160. data/test/https_test.rb +0 -16
  161. data/test/integration/smoke_test.rb +0 -26
  162. data/test/lookup_test.rb +0 -117
  163. data/test/method_aliases_test.rb +0 -25
  164. data/test/mongoid_test.rb +0 -46
  165. data/test/mongoid_test_helper.rb +0 -43
  166. data/test/near_test.rb +0 -61
  167. data/test/oauth_util_test.rb +0 -30
  168. data/test/proxy_test.rb +0 -36
  169. data/test/query_test.rb +0 -52
  170. data/test/request_test.rb +0 -29
  171. data/test/result_test.rb +0 -42
  172. data/test/services_test.rb +0 -393
  173. data/test/test_helper.rb +0 -289
  174. data/test/test_mode_test.rb +0 -59
@@ -0,0 +1,58 @@
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 query_url(query)
12
+ "#{protocol}://api.opencagedata.com/geocode/v1/json?key=#{configuration.api_key}&#{url_query_string(query)}"
13
+ end
14
+
15
+ def required_api_key_parts
16
+ ["key"]
17
+ end
18
+
19
+ private
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
+ :language => (query.language || configuration.language)
48
+ }.merge(super)
49
+
50
+ unless (bounds = query.options[:bounds]).nil?
51
+ params[:bounds] = bounds.map{ |point| "%f,%f" % point }.join(',')
52
+ end
53
+
54
+ params
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,64 @@
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 query_url(query)
15
+ query_type = query.reverse_geocode? ? 'reverse' : 'search'
16
+ "#{protocol}://#{endpoint}/v1/#{query_type}?" + url_query_string(query)
17
+ end
18
+
19
+ def required_api_key_parts
20
+ ['search-XXXX']
21
+ end
22
+
23
+ private
24
+
25
+ def query_url_params(query)
26
+ params = {
27
+ api_key: configuration.api_key,
28
+ size: 1
29
+ }.merge(super)
30
+
31
+ if query.reverse_geocode?
32
+ lat,lon = query.coordinates
33
+ params[:'point.lat'] = lat
34
+ params[:'point.lon'] = lon
35
+ else
36
+ params[:text] = query.text
37
+ end
38
+ params
39
+ end
40
+
41
+ def results(query)
42
+ return [] unless doc = fetch_data(query)
43
+
44
+ # not all responses include a meta
45
+ if doc['meta']
46
+ error = doc.fetch('results', {}).fetch('error', {})
47
+ message = error.fetch('type', 'Unknown Error') + ': ' + error.fetch('message', 'No message')
48
+ log_message = 'Pelias Geocoding API error - ' + message
49
+ case doc['meta']['status_code']
50
+ when '200'
51
+ # nothing to see here
52
+ when '403'
53
+ raise_error(Geocoder::RequestDenied, message) || Geocoder.log(:warn, log_message)
54
+ when '429'
55
+ raise_error(Geocoder::OverQueryLimitError, message) || Geocoder.log(:warn, log_message)
56
+ else
57
+ raise_error(Geocoder::Error, message) || Geocoder.log(:warn, log_message)
58
+ end
59
+ end
60
+
61
+ doc['features'] || []
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,68 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/pointpin'
3
+
4
+ module Geocoder::Lookup
5
+ class Pointpin < Base
6
+
7
+ def name
8
+ "Pointpin"
9
+ end
10
+
11
+ def required_api_key_parts
12
+ ["key"]
13
+ end
14
+
15
+ def query_url(query)
16
+ "#{ protocol }://geo.pointp.in/#{ api_key }/json/#{ query.sanitized_text }"
17
+ end
18
+
19
+ private
20
+
21
+ def results(query)
22
+ # don't look up a loopback address, just return the stored result
23
+ return [] if query.loopback_ip_address?
24
+ doc = fetch_data(query)
25
+ if doc and doc.is_a?(Hash)
26
+ if !data_contains_error?(doc)
27
+ return [doc]
28
+ elsif doc['error']
29
+ case doc['error']
30
+ when "Invalid IP address"
31
+ raise_error(Geocoder::InvalidRequest) || Geocoder.log(:warn, "Invalid Pointpin request.")
32
+ when "Invalid API key"
33
+ raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid Pointpin API key.")
34
+ when "Address not found"
35
+ Geocoder.log(:warn, "Address not found.")
36
+ end
37
+ else
38
+ raise_error(Geocoder::Error) || Geocoder.log(:warn, "Pointpin server error")
39
+ end
40
+ end
41
+
42
+ return []
43
+ end
44
+
45
+ def data_contains_error?(parsed_data)
46
+ parsed_data.keys.include?('error')
47
+ end
48
+
49
+ def reserved_result(ip)
50
+ {
51
+ "ip" => ip,
52
+ "city" => "",
53
+ "region_code" => "",
54
+ "region_name" => "",
55
+ "metrocode" => "",
56
+ "zipcode" => "",
57
+ "latitude" => "0",
58
+ "longitude" => "0",
59
+ "country_name" => "Reserved",
60
+ "country_code" => "RD"
61
+ }
62
+ end
63
+
64
+ def api_key
65
+ configuration.api_key
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,51 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/postcode_anywhere_uk'
3
+
4
+ module Geocoder::Lookup
5
+ class PostcodeAnywhereUk < Base
6
+ # API documentation: http://www.postcodeanywhere.co.uk/Support/WebService/Geocoding/UK/Geocode/2/
7
+ BASE_URL_GEOCODE_V2_00 = 'services.postcodeanywhere.co.uk/Geocoding/UK/Geocode/v2.00/json.ws'
8
+ DAILY_LIMIT_EXEEDED_ERROR_CODES = ['8', '17'] # api docs say these two codes are the same error
9
+ INVALID_API_KEY_ERROR_CODE = '2'
10
+
11
+ def name
12
+ 'PostcodeAnywhereUk'
13
+ end
14
+
15
+ def required_api_key_parts
16
+ %w(key)
17
+ end
18
+
19
+ def query_url(query)
20
+ format('%s://%s?%s', protocol, BASE_URL_GEOCODE_V2_00, url_query_string(query))
21
+ end
22
+
23
+ private
24
+
25
+ def results(query)
26
+ response = fetch_data(query)
27
+ return [] if response.nil? || !response.is_a?(Array) || response.empty?
28
+
29
+ raise_exception_for_response(response[0]) if response[0]['Error']
30
+ response
31
+ end
32
+
33
+ def raise_exception_for_response(response)
34
+ case response['Error']
35
+ when *DAILY_LIMIT_EXEEDED_ERROR_CODES
36
+ raise_error(Geocoder::OverQueryLimitError, response['Cause']) || Geocoder.log(:warn, response['Cause'])
37
+ when INVALID_API_KEY_ERROR_CODE
38
+ raise_error(Geocoder::InvalidApiKey, response['Cause']) || Geocoder.log(:warn, response['Cause'])
39
+ else # anything else just raise general error with the api cause
40
+ raise_error(Geocoder::Error, response['Cause']) || Geocoder.log(:warn, response['Cause'])
41
+ end
42
+ end
43
+
44
+ def query_url_params(query)
45
+ {
46
+ :location => query.sanitized_text,
47
+ :key => configuration.api_key
48
+ }.merge(super)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,53 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/smarty_streets'
3
+
4
+ module Geocoder::Lookup
5
+ class SmartyStreets < Base
6
+ def name
7
+ "SmartyStreets"
8
+ end
9
+
10
+ def required_api_key_parts
11
+ %w(auti-id auth-token)
12
+ end
13
+
14
+ def query_url(query)
15
+ if zipcode_only?(query)
16
+ "#{protocol}://us-zipcode.api.smartystreets.com/lookup?#{url_query_string(query)}"
17
+ else
18
+ "#{protocol}://api.smartystreets.com/street-address?#{url_query_string(query)}"
19
+ end
20
+ end
21
+
22
+ # required by API as of 26 March 2015
23
+ def supported_protocols
24
+ [:https]
25
+ end
26
+
27
+ private # ---------------------------------------------------------------
28
+
29
+ def zipcode_only?(query)
30
+ !query.text.is_a?(Array) and query.to_s.strip =~ /\A\d{5}(-\d{4})?\Z/
31
+ end
32
+
33
+ def query_url_params(query)
34
+ params = {}
35
+ if zipcode_only?(query)
36
+ params[:zipcode] = query.sanitized_text
37
+ else
38
+ params[:street] = query.sanitized_text
39
+ end
40
+ if configuration.api_key.is_a?(Array)
41
+ params[:"auth-id"] = configuration.api_key[0]
42
+ params[:"auth-token"] = configuration.api_key[1]
43
+ else
44
+ params[:"auth-token"] = configuration.api_key
45
+ end
46
+ params.merge(super)
47
+ end
48
+
49
+ def results(query)
50
+ fetch_data(query) || []
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,55 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/telize'
3
+
4
+ module Geocoder::Lookup
5
+ class Telize < Base
6
+
7
+ def name
8
+ "Telize"
9
+ end
10
+
11
+ def required_api_key_parts
12
+ configuration[:host] ? [] : ["key"]
13
+ end
14
+
15
+ def query_url(query)
16
+ if configuration[:host]
17
+ "#{protocol}://#{configuration[:host]}/geoip/#{query.sanitized_text}"
18
+ else
19
+ "#{protocol}://telize-v1.p.mashape.com/geoip/#{query.sanitized_text}?mashape-key=#{api_key}"
20
+ end
21
+ end
22
+
23
+ def supported_protocols
24
+ [].tap do |array|
25
+ array << :https
26
+ array << :http if configuration[:host]
27
+ end
28
+ end
29
+
30
+ private # ---------------------------------------------------------------
31
+
32
+ def results(query)
33
+ # don't look up a loopback address, just return the stored result
34
+ return [reserved_result(query.text)] if query.loopback_ip_address?
35
+ if (doc = fetch_data(query)).nil? or doc['code'] == 401 or empty_result?(doc)
36
+ []
37
+ else
38
+ [doc]
39
+ end
40
+ end
41
+
42
+ def empty_result?(doc)
43
+ !doc.is_a?(Hash) or doc.keys == ["ip"]
44
+ end
45
+
46
+ def reserved_result(ip)
47
+ {"message" => "Input string is not a valid IP address", "code" => 401}
48
+ end
49
+
50
+ def api_key
51
+ configuration.api_key
52
+ end
53
+
54
+ end
55
+ end
@@ -15,6 +15,10 @@ module Geocoder::Lookup
15
15
  def query_url(query)
16
16
  "#{protocol}://geocode-maps.yandex.ru/1.x/?" + url_query_string(query)
17
17
  end
18
+
19
+ def supported_protocols
20
+ [:https]
21
+ end
18
22
 
19
23
  private # ---------------------------------------------------------------
20
24
 
@@ -22,9 +26,9 @@ module Geocoder::Lookup
22
26
  return [] unless doc = fetch_data(query)
23
27
  if err = doc['error']
24
28
  if err["status"] == 401 and err["message"] == "invalid key"
25
- raise_error(Geocoder::InvalidApiKey) || warn("Invalid API key.")
29
+ raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid API key.")
26
30
  else
27
- warn "Yandex Geocoding API error: #{err['status']} (#{err['message']})."
31
+ Geocoder.log(:warn, "Yandex Geocoding API error: #{err['status']} (#{err['message']}).")
28
32
  end
29
33
  return []
30
34
  end
@@ -32,7 +36,7 @@ module Geocoder::Lookup
32
36
  meta = doc['metaDataProperty']['GeocoderResponseMetaData']
33
37
  return meta['found'].to_i > 0 ? doc['featureMember'] : []
34
38
  else
35
- warn "Yandex Geocoding API error: unexpected response format."
39
+ Geocoder.log(:warn, "Yandex Geocoding API error: unexpected response format.")
36
40
  return []
37
41
  end
38
42
  end
@@ -46,7 +50,7 @@ module Geocoder::Lookup
46
50
  {
47
51
  :geocode => q,
48
52
  :format => "json",
49
- :plng => "#{configuration.language}", # supports ru, uk, be
53
+ :plng => "#{query.language || configuration.language}", # supports ru, uk, be
50
54
  :key => configuration.api_key
51
55
  }.merge(super)
52
56
  end
@@ -16,7 +16,9 @@ module Geocoder
16
16
  :longitude => options[:longitude] || :longitude,
17
17
  :geocode_block => block,
18
18
  :units => options[:units],
19
- :method => options[:method]
19
+ :method => options[:method],
20
+ :lookup => options[:lookup],
21
+ :language => options[:language]
20
22
  )
21
23
  end
22
24
 
@@ -30,8 +32,10 @@ module Geocoder
30
32
  :latitude => latitude_attr,
31
33
  :longitude => longitude_attr,
32
34
  :reverse_block => block,
33
- :units => options[:units],
34
- :method => options[:method]
35
+ :units => options[:units],
36
+ :method => options[:method],
37
+ :lookup => options[:lookup],
38
+ :language => options[:language]
35
39
  )
36
40
  end
37
41
 
@@ -1,5 +1,3 @@
1
- require 'geocoder'
2
-
3
1
  module Geocoder
4
2
 
5
3
  ##
@@ -29,7 +27,7 @@ module Geocoder
29
27
  private # ----------------------------------------------------------------
30
28
 
31
29
  def geocoder_init(options)
32
- unless @geocoder_options
30
+ unless defined?(@geocoder_options)
33
31
  @geocoder_options = {}
34
32
  require "geocoder/stores/#{geocoder_file_name}"
35
33
  include Geocoder::Store.const_get(geocoder_module_name)
@@ -39,4 +37,3 @@ module Geocoder
39
37
  end
40
38
  end
41
39
  end
42
-
@@ -1,5 +1,3 @@
1
- require 'geocoder'
2
-
3
1
  module Geocoder
4
2
 
5
3
  ##
@@ -19,7 +17,9 @@ module Geocoder
19
17
  :geocode_block => block,
20
18
  :units => options[:units],
21
19
  :method => options[:method],
22
- :skip_index => options[:skip_index] || false
20
+ :skip_index => options[:skip_index] || false,
21
+ :lookup => options[:lookup],
22
+ :language => options[:language]
23
23
  )
24
24
  end
25
25
 
@@ -34,7 +34,9 @@ module Geocoder
34
34
  :reverse_block => block,
35
35
  :units => options[:units],
36
36
  :method => options[:method],
37
- :skip_index => options[:skip_index] || false
37
+ :skip_index => options[:skip_index] || false,
38
+ :lookup => options[:lookup],
39
+ :language => options[:language]
38
40
  )
39
41
  end
40
42
 
@@ -32,10 +32,10 @@ module Geocoder
32
32
  # appropriate to the Query text.
33
33
  #
34
34
  def lookup
35
- if ip_address?
36
- name = Configuration.ip_lookup || Geocoder::Lookup.ip_services.first
35
+ if !options[:street_address] and (options[:ip_address] or ip_address?)
36
+ name = options[:ip_lookup] || Configuration.ip_lookup || Geocoder::Lookup.ip_services.first
37
37
  else
38
- name = Configuration.lookup || Geocoder::Lookup.street_services.first
38
+ name = options[:lookup] || Configuration.lookup || Geocoder::Lookup.street_services.first
39
39
  end
40
40
  Lookup.get(name)
41
41
  end
@@ -63,14 +63,14 @@ module Geocoder
63
63
  # dot-delimited numbers.
64
64
  #
65
65
  def ip_address?
66
- !!text.to_s.match(/\A(::ffff:)?(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\z/)
66
+ IpAddress.new(text).valid? rescue false
67
67
  end
68
68
 
69
69
  ##
70
70
  # Is the Query text a loopback IP address?
71
71
  #
72
72
  def loopback_ip_address?
73
- !!(self.ip_address? and (text == "0.0.0.0" or text.to_s.match(/\A127/)))
73
+ ip_address? && IpAddress.new(text).loopback?
74
74
  end
75
75
 
76
76
  ##
@@ -98,6 +98,10 @@ module Geocoder
98
98
  coordinates?
99
99
  end
100
100
 
101
+ def language
102
+ options[:language]
103
+ end
104
+
101
105
  private # ----------------------------------------------------------------
102
106
 
103
107
  def params_given?
@@ -1,4 +1,3 @@
1
- require 'geocoder'
2
1
  require 'geocoder/models/active_record'
3
2
 
4
3
  module Geocoder
@@ -12,6 +11,7 @@ module Geocoder
12
11
  end
13
12
  rake_tasks do
14
13
  load "tasks/geocoder.rake"
14
+ load "tasks/maxmind.rake"
15
15
  end
16
16
  end
17
17
  end
@@ -1,23 +1,85 @@
1
- require 'geocoder'
2
-
3
1
  module Geocoder
4
2
  module Request
5
3
 
4
+ # The location() method is vulnerable to trivial IP spoofing.
5
+ # Don't use it in authorization/authentication code, or any
6
+ # other security-sensitive application. Use safe_location
7
+ # instead.
6
8
  def location
7
- unless defined?(@location)
8
- if env.has_key?('HTTP_X_REAL_IP')
9
- @location = Geocoder.search(env['HTTP_X_REAL_IP']).first
10
- elsif env.has_key?('HTTP_X_FORWARDED_FOR')
11
- @location = Geocoder.search(env['HTTP_X_FORWARDED_FOR'].split(/\s*,\s*/)[0]).first
12
- else
13
- @location = Geocoder.search(ip).first
9
+ @location ||= Geocoder.search(geocoder_spoofable_ip, ip_address: true).first
10
+ end
11
+
12
+ # This safe_location() protects you from trivial IP spoofing.
13
+ # For requests that go through a proxy that you haven't
14
+ # whitelisted as trusted in your Rack config, you will get the
15
+ # location for the IP of the last untrusted proxy in the chain,
16
+ # not the original client IP. You WILL NOT get the location
17
+ # corresponding to the original client IP for any request sent
18
+ # through a non-whitelisted proxy.
19
+ def safe_location
20
+ @safe_location ||= Geocoder.search(ip, ip_address: true).first
21
+ end
22
+
23
+ # There's a whole zoo of nonstandard headers added by various
24
+ # proxy softwares to indicate original client IP.
25
+ # ANY of these can be trivially spoofed!
26
+ # (except REMOTE_ADDR, which should by set by your server,
27
+ # and is included at the end as a fallback.
28
+ # Order does matter: we're following the convention established in
29
+ # ActionDispatch::RemoteIp::GetIp::calculate_ip()
30
+ # https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/remote_ip.rb
31
+ # where the forwarded_for headers, possibly containing lists,
32
+ # are arbitrarily preferred over headers expected to contain a
33
+ # single address.
34
+ GEOCODER_CANDIDATE_HEADERS = ['HTTP_X_FORWARDED_FOR',
35
+ 'HTTP_X_FORWARDED',
36
+ 'HTTP_FORWARDED_FOR',
37
+ 'HTTP_FORWARDED',
38
+ 'HTTP_X_CLIENT_IP',
39
+ 'HTTP_CLIENT_IP',
40
+ 'HTTP_X_REAL_IP',
41
+ 'HTTP_X_CLUSTER_CLIENT_IP',
42
+ 'REMOTE_ADDR']
43
+
44
+ def geocoder_spoofable_ip
45
+
46
+ # We could use a more sophisticated IP-guessing algorithm here,
47
+ # in which we'd try to resolve the use of different headers by
48
+ # different proxies. The idea is that by comparing IPs repeated
49
+ # in different headers, you can sometimes decide which header
50
+ # was used by a proxy further along in the chain, and thus
51
+ # prefer the headers used earlier. However, the gains might not
52
+ # be worth the performance tradeoff, since this method is likely
53
+ # to be called on every request in a lot of applications.
54
+ GEOCODER_CANDIDATE_HEADERS.each do |header|
55
+ if @env.has_key? header
56
+ addrs = geocoder_split_ip_addresses(@env[header])
57
+ addrs = geocoder_reject_trusted_ip_addresses(addrs)
58
+ return addrs.first if addrs.any?
14
59
  end
15
60
  end
16
- @location
61
+
62
+ @env['REMOTE_ADDR']
63
+ end
64
+
65
+ private
66
+
67
+ def geocoder_split_ip_addresses(ip_addresses)
68
+ ip_addresses ? ip_addresses.strip.split(/[,\s]+/) : []
69
+ end
70
+
71
+ # use Rack's trusted_proxy?() method to filter out IPs that have
72
+ # been configured as trusted; includes private ranges by
73
+ # default. (we don't want every lookup to return the location
74
+ # of our own proxy/load balancer)
75
+ def geocoder_reject_trusted_ip_addresses(ip_addresses)
76
+ ip_addresses.reject { |ip| trusted_proxy?(ip) }
17
77
  end
18
78
  end
19
79
  end
20
80
 
21
- if defined?(Rack) and defined?(Rack::Request)
22
- Rack::Request.send :include, Geocoder::Request
81
+ if defined?(ActionDispatch::Request)
82
+ ActionDispatch::Request.__send__(:include, Geocoder::Request)
83
+ elsif defined?(Rack::Request)
84
+ Rack::Request.__send__(:include, Geocoder::Request)
23
85
  end
@@ -0,0 +1,62 @@
1
+ require 'geocoder/results/base'
2
+
3
+ module Geocoder::Result
4
+ class BaiduIp < Base
5
+ def coordinates
6
+ [point['y'].to_f, point['x'].to_f]
7
+ end
8
+
9
+ def address
10
+ @data['address']
11
+ end
12
+
13
+ def state
14
+ province
15
+ end
16
+
17
+ def province
18
+ address_detail['province']
19
+ end
20
+
21
+ def city
22
+ address_detail['city']
23
+ end
24
+
25
+ def district
26
+ address_detail['district']
27
+ end
28
+
29
+ def street
30
+ address_detail['street']
31
+ end
32
+
33
+ def street_number
34
+ address_detail['street_number']
35
+ end
36
+
37
+ def state_code
38
+ ""
39
+ end
40
+
41
+ def postal_code
42
+ ""
43
+ end
44
+
45
+ def country
46
+ "China"
47
+ end
48
+
49
+ def country_code
50
+ "CN"
51
+ end
52
+
53
+ private
54
+ def address_detail
55
+ @data['address_detail']
56
+ end
57
+
58
+ def point
59
+ @data['point']
60
+ end
61
+ end
62
+ end