geocoder-sgonyea 1.1.6.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 (122) hide show
  1. data/.gitignore +5 -0
  2. data/.travis.yml +23 -0
  3. data/CHANGELOG.md +298 -0
  4. data/LICENSE +20 -0
  5. data/README.md +656 -0
  6. data/Rakefile +25 -0
  7. data/bin/geocode +5 -0
  8. data/examples/autoexpire_cache.rb +28 -0
  9. data/gemfiles/Gemfile.mongoid-2.4.x +15 -0
  10. data/lib/generators/geocoder/config/config_generator.rb +14 -0
  11. data/lib/generators/geocoder/config/templates/initializer.rb +21 -0
  12. data/lib/geocoder.rb +55 -0
  13. data/lib/geocoder/cache.rb +85 -0
  14. data/lib/geocoder/calculations.rb +319 -0
  15. data/lib/geocoder/cli.rb +114 -0
  16. data/lib/geocoder/configuration.rb +130 -0
  17. data/lib/geocoder/configuration_hash.rb +11 -0
  18. data/lib/geocoder/exceptions.rb +21 -0
  19. data/lib/geocoder/lookup.rb +82 -0
  20. data/lib/geocoder/lookups/base.rb +250 -0
  21. data/lib/geocoder/lookups/bing.rb +47 -0
  22. data/lib/geocoder/lookups/freegeoip.rb +47 -0
  23. data/lib/geocoder/lookups/geocoder_ca.rb +54 -0
  24. data/lib/geocoder/lookups/google.rb +62 -0
  25. data/lib/geocoder/lookups/google_premier.rb +47 -0
  26. data/lib/geocoder/lookups/mapquest.rb +43 -0
  27. data/lib/geocoder/lookups/maxmind.rb +88 -0
  28. data/lib/geocoder/lookups/nominatim.rb +45 -0
  29. data/lib/geocoder/lookups/ovi.rb +52 -0
  30. data/lib/geocoder/lookups/test.rb +38 -0
  31. data/lib/geocoder/lookups/yahoo.rb +84 -0
  32. data/lib/geocoder/lookups/yandex.rb +54 -0
  33. data/lib/geocoder/models/active_record.rb +46 -0
  34. data/lib/geocoder/models/base.rb +42 -0
  35. data/lib/geocoder/models/mongo_base.rb +60 -0
  36. data/lib/geocoder/models/mongo_mapper.rb +26 -0
  37. data/lib/geocoder/models/mongoid.rb +32 -0
  38. data/lib/geocoder/query.rb +103 -0
  39. data/lib/geocoder/railtie.rb +26 -0
  40. data/lib/geocoder/request.rb +23 -0
  41. data/lib/geocoder/results/base.rb +67 -0
  42. data/lib/geocoder/results/bing.rb +48 -0
  43. data/lib/geocoder/results/freegeoip.rb +45 -0
  44. data/lib/geocoder/results/geocoder_ca.rb +60 -0
  45. data/lib/geocoder/results/google.rb +106 -0
  46. data/lib/geocoder/results/google_premier.rb +6 -0
  47. data/lib/geocoder/results/mapquest.rb +51 -0
  48. data/lib/geocoder/results/maxmind.rb +136 -0
  49. data/lib/geocoder/results/nominatim.rb +94 -0
  50. data/lib/geocoder/results/ovi.rb +62 -0
  51. data/lib/geocoder/results/test.rb +16 -0
  52. data/lib/geocoder/results/yahoo.rb +55 -0
  53. data/lib/geocoder/results/yandex.rb +80 -0
  54. data/lib/geocoder/sql.rb +106 -0
  55. data/lib/geocoder/stores/active_record.rb +259 -0
  56. data/lib/geocoder/stores/base.rb +120 -0
  57. data/lib/geocoder/stores/mongo_base.rb +85 -0
  58. data/lib/geocoder/stores/mongo_mapper.rb +13 -0
  59. data/lib/geocoder/stores/mongoid.rb +13 -0
  60. data/lib/geocoder/version.rb +3 -0
  61. data/lib/hash_recursive_merge.rb +74 -0
  62. data/lib/oauth_util.rb +112 -0
  63. data/lib/tasks/geocoder.rake +25 -0
  64. data/test/active_record_test.rb +15 -0
  65. data/test/cache_test.rb +19 -0
  66. data/test/calculations_test.rb +195 -0
  67. data/test/configuration_test.rb +78 -0
  68. data/test/custom_block_test.rb +32 -0
  69. data/test/error_handling_test.rb +43 -0
  70. data/test/fixtures/bing_invalid_key +1 -0
  71. data/test/fixtures/bing_madison_square_garden +40 -0
  72. data/test/fixtures/bing_no_results +16 -0
  73. data/test/fixtures/bing_reverse +42 -0
  74. data/test/fixtures/freegeoip_74_200_247_59 +12 -0
  75. data/test/fixtures/freegeoip_no_results +1 -0
  76. data/test/fixtures/geocoder_ca_madison_square_garden +1 -0
  77. data/test/fixtures/geocoder_ca_no_results +1 -0
  78. data/test/fixtures/geocoder_ca_reverse +34 -0
  79. data/test/fixtures/google_garbage +456 -0
  80. data/test/fixtures/google_madison_square_garden +57 -0
  81. data/test/fixtures/google_no_city_data +44 -0
  82. data/test/fixtures/google_no_locality +51 -0
  83. data/test/fixtures/google_no_results +4 -0
  84. data/test/fixtures/mapquest_madison_square_garden +52 -0
  85. data/test/fixtures/mapquest_no_results +7 -0
  86. data/test/fixtures/maxmind_24_24_24_21 +1 -0
  87. data/test/fixtures/maxmind_24_24_24_22 +1 -0
  88. data/test/fixtures/maxmind_24_24_24_23 +1 -0
  89. data/test/fixtures/maxmind_24_24_24_24 +1 -0
  90. data/test/fixtures/maxmind_74_200_247_59 +1 -0
  91. data/test/fixtures/maxmind_invalid_key +1 -0
  92. data/test/fixtures/maxmind_no_results +1 -0
  93. data/test/fixtures/nominatim_madison_square_garden +150 -0
  94. data/test/fixtures/nominatim_no_results +1 -0
  95. data/test/fixtures/ovi_madison_square_garden +72 -0
  96. data/test/fixtures/ovi_no_results +8 -0
  97. data/test/fixtures/yahoo_error +1 -0
  98. data/test/fixtures/yahoo_invalid_key +2 -0
  99. data/test/fixtures/yahoo_madison_square_garden +52 -0
  100. data/test/fixtures/yahoo_no_results +10 -0
  101. data/test/fixtures/yahoo_over_limit +2 -0
  102. data/test/fixtures/yandex_invalid_key +1 -0
  103. data/test/fixtures/yandex_kremlin +48 -0
  104. data/test/fixtures/yandex_no_city_and_town +112 -0
  105. data/test/fixtures/yandex_no_results +16 -0
  106. data/test/geocoder_test.rb +59 -0
  107. data/test/https_test.rb +16 -0
  108. data/test/integration/smoke_test.rb +26 -0
  109. data/test/lookup_test.rb +116 -0
  110. data/test/method_aliases_test.rb +25 -0
  111. data/test/mongoid_test.rb +39 -0
  112. data/test/mongoid_test_helper.rb +43 -0
  113. data/test/near_test.rb +43 -0
  114. data/test/oauth_util_test.rb +30 -0
  115. data/test/proxy_test.rb +23 -0
  116. data/test/query_test.rb +51 -0
  117. data/test/request_test.rb +29 -0
  118. data/test/result_test.rb +42 -0
  119. data/test/services_test.rb +277 -0
  120. data/test/test_helper.rb +279 -0
  121. data/test/test_mode_test.rb +50 -0
  122. metadata +170 -0
@@ -0,0 +1,38 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/test'
3
+
4
+ module Geocoder
5
+ module Lookup
6
+ class Test < Base
7
+
8
+ def name
9
+ "Test"
10
+ end
11
+
12
+ def self.add_stub(query_text, results)
13
+ stubs[query_text] = results
14
+ end
15
+
16
+ def self.read_stub(query_text)
17
+ stubs.fetch(query_text) {
18
+ raise ArgumentError, "unknown stub request #{query_text}"
19
+ }
20
+ end
21
+
22
+ def self.stubs
23
+ @stubs ||= {}
24
+ end
25
+
26
+ def self.reset
27
+ @stubs = {}
28
+ end
29
+
30
+ private
31
+
32
+ def results(query)
33
+ Geocoder::Lookup::Test.read_stub(query.text)
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,84 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/yahoo"
3
+ require 'oauth_util'
4
+
5
+ module Geocoder::Lookup
6
+ class Yahoo < Base
7
+
8
+ def name
9
+ "Yahoo BOSS"
10
+ end
11
+
12
+ def map_link_url(coordinates)
13
+ "http://maps.yahoo.com/#lat=#{coordinates[0]}&lon=#{coordinates[1]}"
14
+ end
15
+
16
+ def required_api_key_parts
17
+ ["consumer key", "consumer secret"]
18
+ end
19
+
20
+ def query_url(query)
21
+ parsed_url = URI.parse(raw_url(query))
22
+ o = OauthUtil.new
23
+ o.consumer_key = configuration.api_key[0]
24
+ o.consumer_secret = configuration.api_key[1]
25
+ base_url + o.sign(parsed_url).query_string
26
+ end
27
+
28
+ private # ---------------------------------------------------------------
29
+
30
+ def results(query)
31
+ return [] unless doc = fetch_data(query)
32
+ doc = doc['bossresponse']
33
+ if doc['responsecode'].to_i == 200
34
+ if doc['placefinder']['count'].to_i > 0
35
+ return doc['placefinder']['results']
36
+ else
37
+ return []
38
+ end
39
+ else
40
+ warn "Yahoo Geocoding API error: #{doc['responsecode']} (#{doc['reason']})."
41
+ return []
42
+ end
43
+ end
44
+
45
+ ##
46
+ # Yahoo returns errors as XML even when JSON format is specified.
47
+ # Handle that here, without parsing the XML
48
+ # (which would add unnecessary complexity).
49
+ #
50
+ def parse_raw_data(raw_data)
51
+ if raw_data.match /^<\?xml/
52
+ if raw_data.include?("Rate Limit Exceeded")
53
+ raise_error(Geocoder::OverQueryLimitError) || warn("Over API query limit.")
54
+ elsif raw_data.include?("Please provide valid credentials")
55
+ raise_error(Geocoder::InvalidApiKey) || warn("Invalid API key.")
56
+ end
57
+ else
58
+ super(raw_data)
59
+ end
60
+ end
61
+
62
+ def query_url_params(query)
63
+ {
64
+ :location => query.sanitized_text,
65
+ :flags => "JXTSR",
66
+ :gflags => "AC#{'R' if query.reverse_geocode?}",
67
+ :locale => "#{configuration.language}_US",
68
+ :appid => configuration.api_key
69
+ }.merge(super)
70
+ end
71
+
72
+ def cache_key(query)
73
+ raw_url(query)
74
+ end
75
+
76
+ def base_url
77
+ "#{protocol}://yboss.yahooapis.com/geo/placefinder?"
78
+ end
79
+
80
+ def raw_url(query)
81
+ base_url + url_query_string(query)
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,54 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/yandex"
3
+
4
+ module Geocoder::Lookup
5
+ class Yandex < Base
6
+
7
+ def name
8
+ "Yandex"
9
+ end
10
+
11
+ def map_link_url(coordinates)
12
+ "http://maps.yandex.ru/?ll=#{coordinates.reverse.join(',')}"
13
+ end
14
+
15
+ def query_url(query)
16
+ "#{protocol}://geocode-maps.yandex.ru/1.x/?" + url_query_string(query)
17
+ end
18
+
19
+ private # ---------------------------------------------------------------
20
+
21
+ def results(query)
22
+ return [] unless doc = fetch_data(query)
23
+ if err = doc['error']
24
+ if err["status"] == 401 and err["message"] == "invalid key"
25
+ raise_error(Geocoder::InvalidApiKey) || warn("Invalid API key.")
26
+ else
27
+ warn "Yandex Geocoding API error: #{err['status']} (#{err['message']})."
28
+ end
29
+ return []
30
+ end
31
+ if doc = doc['response']['GeoObjectCollection']
32
+ meta = doc['metaDataProperty']['GeocoderResponseMetaData']
33
+ return meta['found'].to_i > 0 ? doc['featureMember'] : []
34
+ else
35
+ warn "Yandex Geocoding API error: unexpected response format."
36
+ return []
37
+ end
38
+ end
39
+
40
+ def query_url_params(query)
41
+ if query.reverse_geocode?
42
+ q = query.coordinates.reverse.join(",")
43
+ else
44
+ q = query.sanitized_text
45
+ end
46
+ {
47
+ :geocode => q,
48
+ :format => "json",
49
+ :plng => "#{configuration.language}", # supports ru, uk, be
50
+ :key => configuration.api_key
51
+ }.merge(super)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,46 @@
1
+ require 'geocoder/models/base'
2
+
3
+ module Geocoder
4
+ module Model
5
+ module ActiveRecord
6
+ include Base
7
+
8
+ ##
9
+ # Set attribute names and include the Geocoder module.
10
+ #
11
+ def geocoded_by(address_attr, options = {}, &block)
12
+ geocoder_init(
13
+ :geocode => true,
14
+ :user_address => address_attr,
15
+ :latitude => options[:latitude] || :latitude,
16
+ :longitude => options[:longitude] || :longitude,
17
+ :geocode_block => block,
18
+ :units => options[:units],
19
+ :method => options[:method]
20
+ )
21
+ end
22
+
23
+ ##
24
+ # Set attribute names and include the Geocoder module.
25
+ #
26
+ def reverse_geocoded_by(latitude_attr, longitude_attr, options = {}, &block)
27
+ geocoder_init(
28
+ :reverse_geocode => true,
29
+ :fetched_address => options[:address] || :address,
30
+ :latitude => latitude_attr,
31
+ :longitude => longitude_attr,
32
+ :reverse_block => block,
33
+ :units => options[:units],
34
+ :method => options[:method]
35
+ )
36
+ end
37
+
38
+
39
+ private # --------------------------------------------------------------
40
+
41
+ def geocoder_file_name; "active_record"; end
42
+ def geocoder_module_name; "ActiveRecord"; end
43
+ end
44
+ end
45
+ end
46
+
@@ -0,0 +1,42 @@
1
+ require 'geocoder'
2
+
3
+ module Geocoder
4
+
5
+ ##
6
+ # Methods for invoking Geocoder in a model.
7
+ #
8
+ module Model
9
+ module Base
10
+
11
+ def geocoder_options
12
+ if defined?(@geocoder_options)
13
+ @geocoder_options
14
+ elsif superclass.respond_to?(:geocoder_options)
15
+ superclass.geocoder_options || { }
16
+ else
17
+ { }
18
+ end
19
+ end
20
+
21
+ def geocoded_by
22
+ fail
23
+ end
24
+
25
+ def reverse_geocoded_by
26
+ fail
27
+ end
28
+
29
+ private # ----------------------------------------------------------------
30
+
31
+ def geocoder_init(options)
32
+ unless @geocoder_options
33
+ @geocoder_options = {}
34
+ require "geocoder/stores/#{geocoder_file_name}"
35
+ include Geocoder::Store.const_get(geocoder_module_name)
36
+ end
37
+ @geocoder_options.merge! options
38
+ end
39
+ end
40
+ end
41
+ end
42
+
@@ -0,0 +1,60 @@
1
+ require 'geocoder'
2
+
3
+ module Geocoder
4
+
5
+ ##
6
+ # Methods for invoking Geocoder in a model.
7
+ #
8
+ module Model
9
+ module MongoBase
10
+
11
+ ##
12
+ # Set attribute names and include the Geocoder module.
13
+ #
14
+ def geocoded_by(address_attr, options = {}, &block)
15
+ geocoder_init(
16
+ :geocode => true,
17
+ :user_address => address_attr,
18
+ :coordinates => options[:coordinates] || :coordinates,
19
+ :geocode_block => block,
20
+ :units => options[:units],
21
+ :method => options[:method],
22
+ :skip_index => options[:skip_index] || false
23
+ )
24
+ end
25
+
26
+ ##
27
+ # Set attribute names and include the Geocoder module.
28
+ #
29
+ def reverse_geocoded_by(coordinates_attr, options = {}, &block)
30
+ geocoder_init(
31
+ :reverse_geocode => true,
32
+ :fetched_address => options[:address] || :address,
33
+ :coordinates => coordinates_attr,
34
+ :reverse_block => block,
35
+ :units => options[:units],
36
+ :method => options[:method],
37
+ :skip_index => options[:skip_index] || false
38
+ )
39
+ end
40
+
41
+ private # ----------------------------------------------------------------
42
+
43
+ def geocoder_init(options)
44
+ unless geocoder_initialized?
45
+ @geocoder_options = { }
46
+ require "geocoder/stores/#{geocoder_file_name}"
47
+ include Geocoder::Store.const_get(geocoder_module_name)
48
+ end
49
+ @geocoder_options.merge! options
50
+ end
51
+
52
+ def geocoder_initialized?
53
+ included_modules.include? Geocoder::Store.const_get(geocoder_module_name)
54
+ rescue NameError
55
+ false
56
+ end
57
+ end
58
+ end
59
+ end
60
+
@@ -0,0 +1,26 @@
1
+ require 'geocoder/models/base'
2
+ require 'geocoder/models/mongo_base'
3
+
4
+ module Geocoder
5
+ module Model
6
+ module MongoMapper
7
+ include Base
8
+ include MongoBase
9
+
10
+ def self.included(base); base.extend(self); end
11
+
12
+ private # --------------------------------------------------------------
13
+
14
+ def geocoder_file_name; "mongo_mapper"; end
15
+ def geocoder_module_name; "MongoMapper"; end
16
+
17
+ def geocoder_init(options)
18
+ super(options)
19
+ if options[:skip_index] == false
20
+ ensure_index [[ geocoder_options[:coordinates], Mongo::GEO2D ]],
21
+ :min => -180, :max => 180 # create 2d index
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,32 @@
1
+ require 'geocoder/models/base'
2
+ require 'geocoder/models/mongo_base'
3
+
4
+ module Geocoder
5
+ module Model
6
+ module Mongoid
7
+ include Base
8
+ include MongoBase
9
+
10
+ def self.included(base); base.extend(self); end
11
+
12
+ private # --------------------------------------------------------------
13
+
14
+ def geocoder_file_name; "mongoid"; end
15
+ def geocoder_module_name; "Mongoid"; end
16
+
17
+ def geocoder_init(options)
18
+ super(options)
19
+ if options[:skip_index] == false
20
+ # create 2d index
21
+ if defined?(::Mongoid::VERSION) && ::Mongoid::VERSION >= "3"
22
+ index({ geocoder_options[:coordinates].to_sym => '2d' },
23
+ {:min => -180, :max => 180})
24
+ else
25
+ index [[ geocoder_options[:coordinates], '2d' ]],
26
+ :min => -180, :max => 180
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,103 @@
1
+ module Geocoder
2
+ class Query
3
+ attr_accessor :text, :options
4
+
5
+ def initialize(text, options = {})
6
+ self.text = text
7
+ self.options = options
8
+ end
9
+
10
+ def execute
11
+ lookup.search(text, options)
12
+ end
13
+
14
+ def to_s
15
+ text
16
+ end
17
+
18
+ def sanitized_text
19
+ if coordinates?
20
+ text.split(/\s*,\s*/).join(',')
21
+ else
22
+ text
23
+ end
24
+ end
25
+
26
+ ##
27
+ # Get a Lookup object (which communicates with the remote geocoding API)
28
+ # appropriate to the Query text.
29
+ #
30
+ def lookup
31
+ if ip_address?
32
+ name = Configuration.ip_lookup || Geocoder::Lookup.ip_services.first
33
+ else
34
+ name = options[:lookup] || Configuration.lookup || Geocoder::Lookup.street_services.first
35
+ end
36
+ Lookup.get(name)
37
+ end
38
+
39
+ def url
40
+ lookup.query_url(self)
41
+ end
42
+
43
+ ##
44
+ # Is the Query blank? (ie, should we not bother searching?)
45
+ # A query is considered blank if its text is nil or empty string AND
46
+ # no URL parameters are specified.
47
+ #
48
+ def blank?
49
+ !params_given? and (
50
+ (text.is_a?(Array) and text.compact.size < 2) or
51
+ text.to_s.match(/\A\s*\z/)
52
+ )
53
+ end
54
+
55
+ ##
56
+ # Does the Query text look like an IP address?
57
+ #
58
+ # Does not check for actual validity, just the appearance of four
59
+ # dot-delimited numbers.
60
+ #
61
+ def ip_address?
62
+ !!text.to_s.match(/\A(::ffff:)?(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\z/)
63
+ end
64
+
65
+ ##
66
+ # Is the Query text a loopback IP address?
67
+ #
68
+ def loopback_ip_address?
69
+ !!(self.ip_address? and (text == "0.0.0.0" or text.to_s.match(/\A127/)))
70
+ end
71
+
72
+ ##
73
+ # Does the given string look like latitude/longitude coordinates?
74
+ #
75
+ def coordinates?
76
+ text.is_a?(Array) or (
77
+ text.is_a?(String) and
78
+ !!text.to_s.match(/\A-?[0-9\.]+, *-?[0-9\.]+\z/)
79
+ )
80
+ end
81
+
82
+ ##
83
+ # Return the latitude/longitude coordinates specified in the query,
84
+ # or nil if none.
85
+ #
86
+ def coordinates
87
+ sanitized_text.split(',') if coordinates?
88
+ end
89
+
90
+ ##
91
+ # Should reverse geocoding be performed for this query?
92
+ #
93
+ def reverse_geocode?
94
+ coordinates?
95
+ end
96
+
97
+ private # ----------------------------------------------------------------
98
+
99
+ def params_given?
100
+ !!(options[:params].is_a?(Hash) and options[:params].keys.size > 0)
101
+ end
102
+ end
103
+ end