geocoder-sgonyea 1.1.6.1

Sign up to get free protection for your applications and to get access to all the features.
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