geocoder 1.1.9 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of geocoder might be problematic. Click here for more details.

Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +9 -5
  4. data/CHANGELOG.md +19 -0
  5. data/README.md +175 -10
  6. data/Rakefile +1 -1
  7. data/gemfiles/Gemfile.mongoid-2.4.x +1 -0
  8. data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +28 -0
  9. data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +28 -0
  10. data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +27 -0
  11. data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +17 -0
  12. data/lib/geocoder.rb +4 -12
  13. data/lib/geocoder/cache.rb +3 -2
  14. data/lib/geocoder/calculations.rb +39 -0
  15. data/lib/geocoder/configuration.rb +1 -7
  16. data/lib/geocoder/ip_address.rb +12 -0
  17. data/lib/geocoder/lookup.rb +10 -1
  18. data/lib/geocoder/lookups/baidu.rb +7 -6
  19. data/lib/geocoder/lookups/baidu_ip.rb +54 -0
  20. data/lib/geocoder/lookups/base.rb +37 -9
  21. data/lib/geocoder/lookups/bing.rb +10 -5
  22. data/lib/geocoder/lookups/cloudmade.rb +35 -0
  23. data/lib/geocoder/lookups/freegeoip.rb +5 -1
  24. data/lib/geocoder/lookups/geocodio.rb +42 -0
  25. data/lib/geocoder/lookups/google_premier.rb +1 -1
  26. data/lib/geocoder/lookups/here.rb +62 -0
  27. data/lib/geocoder/lookups/mapquest.rb +2 -1
  28. data/lib/geocoder/lookups/maxmind_local.rb +58 -0
  29. data/lib/geocoder/lookups/nominatim.rb +8 -0
  30. data/lib/geocoder/lookups/smarty_streets.rb +45 -0
  31. data/lib/geocoder/lookups/yahoo.rb +1 -1
  32. data/lib/geocoder/models/active_record.rb +5 -3
  33. data/lib/geocoder/models/base.rb +1 -4
  34. data/lib/geocoder/models/mongo_base.rb +4 -2
  35. data/lib/geocoder/query.rb +4 -4
  36. data/lib/geocoder/railtie.rb +1 -1
  37. data/lib/geocoder/request.rb +10 -8
  38. data/lib/geocoder/results/baidu_ip.rb +62 -0
  39. data/lib/geocoder/results/cloudmade.rb +39 -0
  40. data/lib/geocoder/results/geocodio.rb +66 -0
  41. data/lib/geocoder/results/here.rb +62 -0
  42. data/lib/geocoder/results/maxmind_local.rb +49 -0
  43. data/lib/geocoder/results/smarty_streets.rb +106 -0
  44. data/lib/geocoder/results/test.rb +20 -3
  45. data/lib/geocoder/results/yandex.rb +7 -3
  46. data/lib/geocoder/sql.rb +16 -15
  47. data/lib/geocoder/stores/active_record.rb +6 -2
  48. data/lib/geocoder/stores/base.rb +8 -1
  49. data/lib/geocoder/version.rb +1 -1
  50. data/lib/maxmind_database.rb +109 -0
  51. data/lib/oauth_util.rb +1 -1
  52. data/lib/tasks/geocoder.rake +3 -1
  53. data/lib/tasks/maxmind.rake +73 -0
  54. data/test/fixtures/baidu_ip_202_198_16_3 +19 -0
  55. data/test/fixtures/baidu_ip_invalid_key +1 -0
  56. data/test/fixtures/baidu_ip_no_results +1 -0
  57. data/test/fixtures/cloudmade_invalid_key +1 -0
  58. data/test/fixtures/cloudmade_madison_square_garden +1 -0
  59. data/test/fixtures/cloudmade_no_results +1 -0
  60. data/test/fixtures/geocodio_1101_pennsylvania_ave +1 -0
  61. data/test/fixtures/geocodio_bad_api_key +3 -0
  62. data/test/fixtures/geocodio_invalid +4 -0
  63. data/test/fixtures/geocodio_no_results +1 -0
  64. data/test/fixtures/geocodio_over_query_limit +4 -0
  65. data/test/fixtures/here_madison_square_garden +72 -0
  66. data/test/fixtures/here_no_results +8 -0
  67. data/test/fixtures/nominatim_over_limit +1 -0
  68. data/test/fixtures/smarty_streets_11211 +1 -0
  69. data/test/fixtures/smarty_streets_madison_square_garden +47 -0
  70. data/test/fixtures/smarty_streets_no_results +1 -0
  71. data/test/fixtures/yandex_canada_rue_dupuis_14 +446 -0
  72. data/test/fixtures/yandex_new_york +1 -0
  73. data/test/integration/http_client_test.rb +25 -0
  74. data/test/mongoid_test_helper.rb +2 -2
  75. data/test/test_helper.rb +98 -30
  76. data/test/{active_record_test.rb → unit/active_record_test.rb} +4 -3
  77. data/test/{cache_test.rb → unit/cache_test.rb} +3 -1
  78. data/test/{calculations_test.rb → unit/calculations_test.rb} +22 -13
  79. data/test/{configuration_test.rb → unit/configuration_test.rb} +4 -27
  80. data/test/{error_handling_test.rb → unit/error_handling_test.rb} +10 -9
  81. data/test/{geocoder_test.rb → unit/geocoder_test.rb} +26 -7
  82. data/test/{https_test.rb → unit/https_test.rb} +4 -3
  83. data/test/unit/ip_address_test.rb +24 -0
  84. data/test/{lookup_test.rb → unit/lookup_test.rb} +33 -20
  85. data/test/unit/lookups/bing_test.rb +68 -0
  86. data/test/unit/lookups/dstk_test.rb +26 -0
  87. data/test/unit/lookups/esri_test.rb +48 -0
  88. data/test/unit/lookups/freegeoip_test.rb +27 -0
  89. data/test/unit/lookups/geocoder_ca_test.rb +17 -0
  90. data/test/unit/lookups/geocodio_test.rb +55 -0
  91. data/test/unit/lookups/google_premier_test.rb +22 -0
  92. data/test/unit/lookups/google_test.rb +84 -0
  93. data/test/unit/lookups/mapquest_test.rb +60 -0
  94. data/test/unit/lookups/maxmind_local_test.rb +28 -0
  95. data/test/unit/lookups/maxmind_test.rb +63 -0
  96. data/test/unit/lookups/nominatim_test.rb +31 -0
  97. data/test/unit/lookups/smarty_streets_test.rb +71 -0
  98. data/test/unit/lookups/yahoo_test.rb +35 -0
  99. data/test/{method_aliases_test.rb → unit/method_aliases_test.rb} +5 -4
  100. data/test/unit/model_test.rb +38 -0
  101. data/test/{mongoid_test.rb → unit/mongoid_test.rb} +10 -9
  102. data/test/unit/near_test.rb +87 -0
  103. data/test/{oauth_util_test.rb → unit/oauth_util_test.rb} +3 -2
  104. data/test/{proxy_test.rb → unit/proxy_test.rb} +2 -1
  105. data/test/{query_test.rb → unit/query_test.rb} +7 -8
  106. data/test/unit/rake_task_test.rb +21 -0
  107. data/test/{request_test.rb → unit/request_test.rb} +8 -2
  108. data/test/{result_test.rb → unit/result_test.rb} +29 -1
  109. data/test/{test_mode_test.rb → unit/test_mode_test.rb} +12 -1
  110. metadata +80 -27
  111. data/test/custom_block_test.rb +0 -32
  112. data/test/integration/smoke_test.rb +0 -26
  113. data/test/near_test.rb +0 -61
  114. data/test/services_test.rb +0 -393
@@ -31,7 +31,7 @@ module Geocoder::Lookup
31
31
 
32
32
  def sign(string)
33
33
  raw_private_key = url_safe_base64_decode(configuration.api_key[0])
34
- digest = OpenSSL::Digest::Digest.new('sha1')
34
+ digest = OpenSSL::Digest.new('sha1')
35
35
  raw_signature = OpenSSL::HMAC.digest(digest, raw_private_key, string)
36
36
  url_safe_base64_encode(raw_signature)
37
37
  end
@@ -0,0 +1,62 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/here'
3
+
4
+ module Geocoder::Lookup
5
+ class Here < Base
6
+
7
+ def name
8
+ "Here"
9
+ end
10
+
11
+ def required_api_key_parts
12
+ []
13
+ end
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)
17
+ end
18
+
19
+ private # ---------------------------------------------------------------
20
+
21
+ def results(query)
22
+ return [] unless doc = fetch_data(query)
23
+ return [] unless doc['Response'] && doc['Response']['View']
24
+ if r=doc['Response']['View']
25
+ return [] if r.nil? || !r.is_a?(Array) || r.empty?
26
+ return r.first['Result']
27
+ end
28
+ []
29
+ end
30
+
31
+ def query_url_params(query)
32
+ options = {
33
+ :gen=>4,
34
+ :app_id=>api_key,
35
+ :app_code=>api_code
36
+ }
37
+
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
+ )
47
+ end
48
+ end
49
+
50
+ def api_key
51
+ if a=configuration.api_key
52
+ return a.first if a.is_a?(Array)
53
+ end
54
+ end
55
+
56
+ def api_code
57
+ if a=configuration.api_key
58
+ return a.last if a.is_a?(Array)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -14,7 +14,7 @@ module Geocoder::Lookup
14
14
  end
15
15
 
16
16
  def query_url(query)
17
- domain = configuration[:licensed] ? "www" : "open"
17
+ domain = configuration[:open] ? "open" : "www"
18
18
  version = configuration[:version] || 1
19
19
  url = "#{protocol}://#{domain}.mapquestapi.com/geocoding/v#{version}/#{search_type(query)}?"
20
20
  url + url_query_string(query)
@@ -53,6 +53,7 @@ module Geocoder::Lookup
53
53
  raise_error(Geocoder::Error, messages) ||
54
54
  warn("Mapquest Geocoding API error: #{messages}")
55
55
  end
56
+ []
56
57
  end
57
58
 
58
59
  end
@@ -0,0 +1,58 @@
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? ? [] : [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
38
+ FROM maxmind_geolite_city_location l JOIN maxmind_geolite_city_blocks b USING (loc_id)
39
+ WHERE b.start_ip_num <= #{addr} AND #{addr} <= b.end_ip_num"
40
+ format_result(q, [:country_name, :region_name, :city_name])
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 format_result(query, attr_names)
50
+ if r = ActiveRecord::Base.connection.execute(query).first
51
+ r = r.values if r.is_a?(Hash) # some db adapters return Hash, some Array
52
+ [Hash[*attr_names.zip(r).flatten]]
53
+ else
54
+ []
55
+ end
56
+ end
57
+ end
58
+ end
@@ -25,6 +25,14 @@ module Geocoder::Lookup
25
25
  doc.is_a?(Array) ? doc : [doc]
26
26
  end
27
27
 
28
+ def parse_raw_data(raw_data)
29
+ if raw_data.include?("Bandwidth limit exceeded")
30
+ raise_error(Geocoder::OverQueryLimitError) || warn("Over API query limit.")
31
+ else
32
+ super(raw_data)
33
+ end
34
+ end
35
+
28
36
  def query_url_params(query)
29
37
  params = {
30
38
  :format => "json",
@@ -0,0 +1,45 @@
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(auth-token)
12
+ end
13
+
14
+ def query_url(query)
15
+ path = zipcode_only?(query) ? "zipcode" : "street-address"
16
+ "#{protocol}://api.smartystreets.com/#{path}?#{url_query_string(query)}"
17
+ end
18
+
19
+ private # ---------------------------------------------------------------
20
+
21
+ def zipcode_only?(query)
22
+ !query.text.is_a?(Array) and query.to_s.strip =~ /\A\d{5}(-\d{4})?\Z/
23
+ end
24
+
25
+ def query_url_params(query)
26
+ params = {}
27
+ if zipcode_only?(query)
28
+ params[:zipcode] = query.sanitized_text
29
+ else
30
+ params[:street] = query.sanitized_text
31
+ end
32
+ if configuration.api_key.is_a?(Array)
33
+ params[:"auth-id"] = configuration.api_key[0]
34
+ params[:"auth-token"] = configuration.api_key[1]
35
+ else
36
+ params[:"auth-token"] = configuration.api_key
37
+ end
38
+ params.merge(super)
39
+ end
40
+
41
+ def results(query)
42
+ fetch_data(query) || []
43
+ end
44
+ end
45
+ end
@@ -50,7 +50,7 @@ module Geocoder::Lookup
50
50
  # to warning message.
51
51
  #
52
52
  def parse_raw_data(raw_data)
53
- if raw_data.match /^<\?xml/
53
+ if raw_data.match(/^<\?xml/)
54
54
  if raw_data.include?("Rate Limit Exceeded")
55
55
  raise_error(Geocoder::OverQueryLimitError) || warn("Over API query limit.")
56
56
  elsif raw_data =~ /<yahoo:description>(Please provide valid credentials.*)<\/yahoo:description>/i
@@ -16,7 +16,8 @@ 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]
20
21
  )
21
22
  end
22
23
 
@@ -30,8 +31,9 @@ module Geocoder
30
31
  :latitude => latitude_attr,
31
32
  :longitude => longitude_attr,
32
33
  :reverse_block => block,
33
- :units => options[:units],
34
- :method => options[:method]
34
+ :units => options[:units],
35
+ :method => options[:method],
36
+ :lookup => options[:lookup]
35
37
  )
36
38
  end
37
39
 
@@ -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
-
@@ -19,7 +19,8 @@ module Geocoder
19
19
  :geocode_block => block,
20
20
  :units => options[:units],
21
21
  :method => options[:method],
22
- :skip_index => options[:skip_index] || false
22
+ :skip_index => options[:skip_index] || false,
23
+ :lookup => options[:lookup]
23
24
  )
24
25
  end
25
26
 
@@ -34,7 +35,8 @@ module Geocoder
34
35
  :reverse_block => block,
35
36
  :units => options[:units],
36
37
  :method => options[:method],
37
- :skip_index => options[:skip_index] || false
38
+ :skip_index => options[:skip_index] || false,
39
+ :lookup => options[:lookup]
38
40
  )
39
41
  end
40
42
 
@@ -33,9 +33,9 @@ module Geocoder
33
33
  #
34
34
  def lookup
35
35
  if ip_address?
36
- name = Configuration.ip_lookup || Geocoder::Lookup.ip_services.first
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
  ##
@@ -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,17 +1,19 @@
1
- require 'geocoder'
2
-
3
1
  module Geocoder
4
2
  module Request
5
3
 
6
4
  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
5
+ @location ||= begin
6
+ detected_ip = env['HTTP_X_REAL_IP'] || (
7
+ env['HTTP_X_FORWARDED_FOR'] &&
8
+ env['HTTP_X_FORWARDED_FOR'].split(",").first.strip
9
+ )
10
+ detected_ip = IpAddress.new(detected_ip.to_s)
11
+ if detected_ip.valid? and !detected_ip.loopback?
12
+ real_ip = detected_ip.to_s
12
13
  else
13
- @location = Geocoder.search(ip).first
14
+ real_ip = self.ip
14
15
  end
16
+ Geocoder.search(real_ip).first
15
17
  end
16
18
  @location
17
19
  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
@@ -0,0 +1,39 @@
1
+ require 'geocoder/results/base'
2
+
3
+ module Geocoder::Result
4
+ class Cloudmade < Base
5
+
6
+ def coordinates
7
+ @data["centroid"]["coordinates"]
8
+ end
9
+
10
+ def street
11
+ @data["location"]["road"]
12
+ end
13
+
14
+ def city
15
+ @data["location"]["city"]
16
+ end
17
+
18
+ def state
19
+ @data["location"]["county"]
20
+ end
21
+ alias_method :state_code, :state
22
+
23
+ def country
24
+ @data["location"]["country"]
25
+ end
26
+ alias_method :country_code, :country
27
+
28
+ def postal_code
29
+ @data["location"]["postcode"]
30
+ end
31
+
32
+ def address
33
+ [street, city, state, postal_code, country].compact.reject{|s| s.length == 0 }.join(", ")
34
+ end
35
+
36
+ end
37
+ end
38
+
39
+