geocoder 1.1.4 → 1.1.8

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 (110) hide show
  1. data/.travis.yml +4 -0
  2. data/{CHANGELOG.rdoc → CHANGELOG.md} +120 -39
  3. data/README.md +200 -54
  4. data/examples/autoexpire_cache_dalli.rb +62 -0
  5. data/examples/{autoexpire_cache.rb → autoexpire_cache_redis.rb} +4 -6
  6. data/lib/generators/geocoder/config/templates/initializer.rb +19 -23
  7. data/lib/geocoder/cache.rb +6 -1
  8. data/lib/geocoder/calculations.rb +45 -13
  9. data/lib/geocoder/cli.rb +10 -11
  10. data/lib/geocoder/configuration.rb +67 -43
  11. data/lib/geocoder/configuration_hash.rb +11 -0
  12. data/lib/geocoder/exceptions.rb +3 -0
  13. data/lib/geocoder/lookup.rb +3 -1
  14. data/lib/geocoder/lookups/base.rb +89 -27
  15. data/lib/geocoder/lookups/bing.rb +21 -11
  16. data/lib/geocoder/lookups/esri.rb +48 -0
  17. data/lib/geocoder/lookups/freegeoip.rb +8 -4
  18. data/lib/geocoder/lookups/geocoder_ca.rb +11 -7
  19. data/lib/geocoder/lookups/google.rb +18 -9
  20. data/lib/geocoder/lookups/google_premier.rb +16 -8
  21. data/lib/geocoder/lookups/mapquest.rb +33 -5
  22. data/lib/geocoder/lookups/maxmind.rb +88 -0
  23. data/lib/geocoder/lookups/nominatim.rb +13 -8
  24. data/lib/geocoder/lookups/ovi.rb +52 -0
  25. data/lib/geocoder/lookups/test.rb +10 -0
  26. data/lib/geocoder/lookups/yahoo.rb +51 -38
  27. data/lib/geocoder/lookups/yandex.rb +17 -9
  28. data/lib/geocoder/models/mongoid.rb +1 -1
  29. data/lib/geocoder/query.rb +20 -5
  30. data/lib/geocoder/request.rb +7 -1
  31. data/lib/geocoder/results/base.rb +8 -2
  32. data/lib/geocoder/results/esri.rb +51 -0
  33. data/lib/geocoder/results/google.rb +34 -0
  34. data/lib/geocoder/results/mapquest.rb +46 -2
  35. data/lib/geocoder/results/maxmind.rb +135 -0
  36. data/lib/geocoder/results/nominatim.rb +0 -10
  37. data/lib/geocoder/results/ovi.rb +62 -0
  38. data/lib/geocoder/results/test.rb +2 -1
  39. data/lib/geocoder/results/yahoo.rb +9 -2
  40. data/lib/geocoder/results/yandex.rb +12 -2
  41. data/lib/geocoder/sql.rb +6 -4
  42. data/lib/geocoder/stores/active_record.rb +57 -30
  43. data/lib/geocoder/stores/base.rb +3 -2
  44. data/lib/geocoder/stores/mongo_base.rb +2 -1
  45. data/lib/geocoder/version.rb +1 -1
  46. data/lib/geocoder.rb +2 -4
  47. data/lib/hash_recursive_merge.rb +74 -0
  48. data/lib/oauth_util.rb +112 -0
  49. data/test/cache_test.rb +19 -0
  50. data/test/calculations_test.rb +10 -4
  51. data/test/configuration_test.rb +26 -51
  52. data/test/error_handling_test.rb +8 -4
  53. data/test/fixtures/bing_invalid_key +1 -0
  54. data/test/fixtures/esri_madison_square_garden +59 -0
  55. data/test/fixtures/esri_no_results +8 -0
  56. data/test/fixtures/esri_reverse +21 -0
  57. data/test/fixtures/mapquest_madison_square_garden +52 -0
  58. data/test/fixtures/mapquest_no_results +7 -0
  59. data/test/fixtures/maxmind_24_24_24_21 +1 -0
  60. data/test/fixtures/maxmind_24_24_24_22 +1 -0
  61. data/test/fixtures/maxmind_24_24_24_23 +1 -0
  62. data/test/fixtures/maxmind_24_24_24_24 +1 -0
  63. data/test/fixtures/maxmind_74_200_247_59 +1 -0
  64. data/test/fixtures/maxmind_invalid_key +1 -0
  65. data/test/fixtures/maxmind_no_results +1 -0
  66. data/test/fixtures/ovi_madison_square_garden +72 -0
  67. data/test/fixtures/ovi_no_results +8 -0
  68. data/test/fixtures/yahoo_error +1 -0
  69. data/test/fixtures/yahoo_invalid_key +2 -0
  70. data/test/fixtures/yahoo_madison_square_garden +52 -0
  71. data/test/fixtures/yahoo_no_results +10 -0
  72. data/test/fixtures/yahoo_over_limit +2 -0
  73. data/test/fixtures/yandex_no_city_and_town +112 -0
  74. data/test/https_test.rb +3 -3
  75. data/test/integration/smoke_test.rb +2 -2
  76. data/test/lookup_test.rb +84 -11
  77. data/test/near_test.rb +43 -0
  78. data/test/oauth_util_test.rb +30 -0
  79. data/test/proxy_test.rb +2 -2
  80. data/test/query_test.rb +13 -0
  81. data/test/request_test.rb +29 -0
  82. data/test/result_test.rb +12 -2
  83. data/test/services_test.rb +208 -57
  84. data/test/test_helper.rb +66 -100
  85. data/test/test_mode_test.rb +35 -26
  86. metadata +58 -35
  87. data/test/fixtures/mapquest_madison_square_garden.json +0 -27
  88. data/test/fixtures/mapquest_no_results.json +0 -1
  89. data/test/fixtures/yahoo_madison_square_garden.json +0 -52
  90. data/test/fixtures/yahoo_no_results.json +0 -10
  91. data/test/fixtures/yahoo_v1_madison_square_garden.json +0 -46
  92. data/test/fixtures/yahoo_v1_no_results.json +0 -10
  93. /data/test/fixtures/{bing_madison_square_garden.json → bing_madison_square_garden} +0 -0
  94. /data/test/fixtures/{bing_no_results.json → bing_no_results} +0 -0
  95. /data/test/fixtures/{bing_reverse.json → bing_reverse} +0 -0
  96. /data/test/fixtures/{freegeoip_74_200_247_59.json → freegeoip_74_200_247_59} +0 -0
  97. /data/test/fixtures/{freegeoip_no_results.json → freegeoip_no_results} +0 -0
  98. /data/test/fixtures/{geocoder_ca_madison_square_garden.json → geocoder_ca_madison_square_garden} +0 -0
  99. /data/test/fixtures/{geocoder_ca_no_results.json → geocoder_ca_no_results} +0 -0
  100. /data/test/fixtures/{geocoder_ca_reverse.json → geocoder_ca_reverse} +0 -0
  101. /data/test/fixtures/{google_garbage.json → google_garbage} +0 -0
  102. /data/test/fixtures/{google_madison_square_garden.json → google_madison_square_garden} +0 -0
  103. /data/test/fixtures/{google_no_city_data.json → google_no_city_data} +0 -0
  104. /data/test/fixtures/{google_no_locality.json → google_no_locality} +0 -0
  105. /data/test/fixtures/{google_no_results.json → google_no_results} +0 -0
  106. /data/test/fixtures/{nominatim_madison_square_garden.json → nominatim_madison_square_garden} +0 -0
  107. /data/test/fixtures/{nominatim_no_results.json → nominatim_no_results} +0 -0
  108. /data/test/fixtures/{yandex_invalid_key.json → yandex_invalid_key} +0 -0
  109. /data/test/fixtures/{yandex_kremlin.json → yandex_kremlin} +0 -0
  110. /data/test/fixtures/{yandex_no_results.json → yandex_no_results} +0 -0
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  require 'geocoder/sql'
2
3
  require 'geocoder/stores/base'
3
4
 
@@ -17,13 +18,15 @@ module Geocoder::Store
17
18
 
18
19
  # scope: geocoded objects
19
20
  scope :geocoded, lambda {
20
- {:conditions => "#{geocoder_options[:latitude]} IS NOT NULL " +
21
- "AND #{geocoder_options[:longitude]} IS NOT NULL"}}
21
+ where("#{geocoder_options[:latitude]} IS NOT NULL " +
22
+ "AND #{geocoder_options[:longitude]} IS NOT NULL")
23
+ }
22
24
 
23
25
  # scope: not-geocoded objects
24
26
  scope :not_geocoded, lambda {
25
- {:conditions => "#{geocoder_options[:latitude]} IS NULL " +
26
- "OR #{geocoder_options[:longitude]} IS NULL"}}
27
+ where("#{geocoder_options[:latitude]} IS NULL " +
28
+ "OR #{geocoder_options[:longitude]} IS NULL")
29
+ }
27
30
 
28
31
  ##
29
32
  # Find all objects within a radius of the given location.
@@ -35,7 +38,9 @@ module Geocoder::Store
35
38
  scope :near, lambda{ |location, *args|
36
39
  latitude, longitude = Geocoder::Calculations.extract_coordinates(location)
37
40
  if Geocoder::Calculations.coordinates_present?(latitude, longitude)
38
- near_scope_options(latitude, longitude, *args)
41
+ options = near_scope_options(latitude, longitude, *args)
42
+ select(options[:select]).where(options[:conditions]).
43
+ order(options[:order])
39
44
  else
40
45
  # If no lat/lon given we don't want any results, but we still
41
46
  # need distance and bearing columns so you can add, for example:
@@ -53,11 +58,11 @@ module Geocoder::Store
53
58
  scope :within_bounding_box, lambda{ |bounds|
54
59
  sw_lat, sw_lng, ne_lat, ne_lng = bounds.flatten if bounds
55
60
  if sw_lat && sw_lng && ne_lat && ne_lng
56
- {:conditions => Geocoder::Sql.within_bounding_box(
61
+ where(Geocoder::Sql.within_bounding_box(
57
62
  sw_lat, sw_lng, ne_lat, ne_lng,
58
63
  full_column_name(geocoder_options[:latitude]),
59
64
  full_column_name(geocoder_options[:longitude])
60
- )}
65
+ ))
61
66
  else
62
67
  select(select_clause(nil, "NULL", "NULL")).where(false_condition)
63
68
  end
@@ -87,33 +92,47 @@ module Geocoder::Store
87
92
  # * +:units+ - <tt>:mi</tt> or <tt>:km</tt>; to be used.
88
93
  # for interpreting radius as well as the +distance+ attribute which
89
94
  # is added to each found nearby object.
90
- # See Geocoder::Configuration to know how configure default units.
95
+ # Use Geocoder.configure[:units] to configure default units.
91
96
  # * +:bearing+ - <tt>:linear</tt> or <tt>:spherical</tt>.
92
97
  # the method to be used for calculating the bearing (direction)
93
98
  # between the given point and each found nearby point;
94
- # set to false for no bearing calculation.
95
- # See Geocoder::Configuration to know how configure default method.
96
- # * +:select+ - string with the SELECT SQL fragment (e.g. “id, name”)
97
- # * +:order+ - column(s) for ORDER BY SQL clause; default is distance;
98
- # set to false or nil to omit the ORDER BY clause
99
- # * +:exclude+ - an object to exclude (used by the +nearbys+ method)
99
+ # set to false for no bearing calculation. Use
100
+ # Geocoder.configure[:distances] to configure default calculation method.
101
+ # * +:select+ - string with the SELECT SQL fragment (e.g. “id, name”)
102
+ # * +:select_distance+ - whether to include the distance alias in the
103
+ # SELECT SQL fragment (e.g. <formula> AS distance)
104
+ # * +:select_bearing+ - like +:select_distance+ but for bearing.
105
+ # * +:order+ - column(s) for ORDER BY SQL clause; default is distance;
106
+ # set to false or nil to omit the ORDER BY clause
107
+ # * +:exclude+ - an object to exclude (used by the +nearbys+ method)
100
108
  #
101
109
  def near_scope_options(latitude, longitude, radius = 20, options = {})
102
- options[:units] ||= (geocoder_options[:units] || Geocoder::Configuration.units)
110
+ if options[:units]
111
+ options[:units] = options[:units].to_sym
112
+ end
113
+ options[:units] ||= (geocoder_options[:units] || Geocoder.config.units)
114
+ select_distance = options.fetch(:select_distance, true)
115
+ options[:order] = "" if !select_distance && !options.include?(:order)
116
+ select_bearing = options.fetch(:select_bearing, true)
103
117
  bearing = bearing_sql(latitude, longitude, options)
104
118
  distance = distance_sql(latitude, longitude, options)
119
+
120
+ b = Geocoder::Calculations.bounding_box([latitude, longitude], radius, options)
121
+ args = b + [
122
+ full_column_name(geocoder_options[:latitude]),
123
+ full_column_name(geocoder_options[:longitude])
124
+ ]
125
+ bounding_box_conditions = Geocoder::Sql.within_bounding_box(*args)
126
+
105
127
  if using_sqlite?
106
- b = Geocoder::Calculations.bounding_box([latitude, longitude], radius, options)
107
- args = b + [
108
- full_column_name(geocoder_options[:latitude]),
109
- full_column_name(geocoder_options[:longitude])
110
- ]
111
- conditions = Geocoder::Sql.within_bounding_box(*args)
128
+ conditions = bounding_box_conditions
112
129
  else
113
- conditions = ["#{distance} <= ?", radius]
130
+ conditions = [bounding_box_conditions + " AND #{distance} <= ?", radius]
114
131
  end
115
132
  {
116
- :select => select_clause(options[:select], distance, bearing),
133
+ :select => select_clause(options[:select],
134
+ select_distance ? distance : nil,
135
+ select_bearing ? bearing : nil),
117
136
  :conditions => add_exclude_condition(conditions, options[:exclude]),
118
137
  :order => options.include?(:order) ? options[:order] : "distance ASC"
119
138
  }
@@ -140,7 +159,7 @@ module Geocoder::Store
140
159
  #
141
160
  def bearing_sql(latitude, longitude, options = {})
142
161
  if !options.include?(:bearing)
143
- options[:bearing] = Geocoder::Configuration.distances
162
+ options[:bearing] = Geocoder.config.distances
144
163
  end
145
164
  if options[:bearing]
146
165
  method_prefix = using_sqlite? ? "approx" : "full"
@@ -157,14 +176,23 @@ module Geocoder::Store
157
176
  ##
158
177
  # Generate the SELECT clause.
159
178
  #
160
- def select_clause(columns, distance, bearing = nil)
161
- if columns == :geo_only
179
+ def select_clause(columns, distance = nil, bearing = nil)
180
+ if columns == :id_only
181
+ return full_column_name(primary_key)
182
+ elsif columns == :geo_only
162
183
  clause = ""
163
184
  else
164
- clause = (columns || full_column_name("*")) + ", "
185
+ clause = (columns || full_column_name("*"))
186
+ end
187
+ if distance
188
+ clause += ", " unless clause.empty?
189
+ clause += "#{distance} AS distance"
165
190
  end
166
- clause + "#{distance} AS distance" +
167
- (bearing ? ", #{bearing} AS bearing" : "")
191
+ if bearing
192
+ clause += ", " unless clause.empty?
193
+ clause += "#{bearing} AS bearing"
194
+ end
195
+ clause
168
196
  end
169
197
 
170
198
  ##
@@ -236,4 +264,3 @@ module Geocoder::Store
236
264
  alias_method :fetch_address, :reverse_geocode
237
265
  end
238
266
  end
239
-
@@ -58,10 +58,11 @@ module Geocoder
58
58
  ##
59
59
  # Get nearby geocoded objects.
60
60
  # Takes the same options hash as the near class method (scope).
61
+ # Returns nil if the object is not geocoded.
61
62
  #
62
63
  def nearbys(radius = 20, options = {})
63
- return [] unless geocoded?
64
- options.merge!(:exclude => self)
64
+ return nil unless geocoded?
65
+ options.merge!(:exclude => self) unless send(self.class.primary_key).nil?
65
66
  self.class.near(self, radius, options)
66
67
  end
67
68
 
@@ -24,7 +24,8 @@ module Geocoder::Store
24
24
 
25
25
  # Use BSON::OrderedHash if Ruby's hashes are unordered.
26
26
  # Conditions must be in order required by indexes (see mongo gem).
27
- empty = RUBY_VERSION.split('.')[1].to_i < 9 ? BSON::OrderedHash.new : {}
27
+ version = RUBY_VERSION.split('.').map { |i| i.to_i }
28
+ empty = version[0] < 2 && version[1] < 9 ? BSON::OrderedHash.new : {}
28
29
 
29
30
  conds = empty.clone
30
31
  field = geocoder_options[:coordinates]
@@ -1,3 +1,3 @@
1
1
  module Geocoder
2
- VERSION = "1.1.4"
2
+ VERSION = "1.1.8"
3
3
  end
data/lib/geocoder.rb CHANGED
@@ -43,10 +43,8 @@ module Geocoder
43
43
  # The working Cache object, or +nil+ if none configured.
44
44
  #
45
45
  def cache
46
- if @cache.nil? and store = Configuration.cache
47
- @cache = Cache.new(store, Configuration.cache_prefix)
48
- end
49
- @cache
46
+ warn "WARNING: Calling Geocoder.cache is DEPRECATED. The #cache method now belongs to the Geocoder::Lookup object."
47
+ Geocoder::Lookup.get(Geocoder.config.lookup).cache
50
48
  end
51
49
  end
52
50
 
@@ -0,0 +1,74 @@
1
+ #
2
+ # = Hash Recursive Merge
3
+ #
4
+ # Merges a Ruby Hash recursively, Also known as deep merge.
5
+ # Recursive version of Hash#merge and Hash#merge!.
6
+ #
7
+ # Category:: Ruby
8
+ # Package:: Hash
9
+ # Author:: Simone Carletti <weppos@weppos.net>
10
+ # Copyright:: 2007-2008 The Authors
11
+ # License:: MIT License
12
+ # Link:: http://www.simonecarletti.com/
13
+ # Source:: http://gist.github.com/gists/6391/
14
+ #
15
+ module HashRecursiveMerge
16
+
17
+ #
18
+ # Recursive version of Hash#merge!
19
+ #
20
+ # Adds the contents of +other_hash+ to +hsh+,
21
+ # merging entries in +hsh+ with duplicate keys with those from +other_hash+.
22
+ #
23
+ # Compared with Hash#merge!, this method supports nested hashes.
24
+ # When both +hsh+ and +other_hash+ contains an entry with the same key,
25
+ # it merges and returns the values from both arrays.
26
+ #
27
+ # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
28
+ # h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}}
29
+ # h1.rmerge!(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
30
+ #
31
+ # Simply using Hash#merge! would return
32
+ #
33
+ # h1.merge!(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
34
+ #
35
+ def rmerge!(other_hash)
36
+ merge!(other_hash) do |key, oldval, newval|
37
+ oldval.class == self.class ? oldval.rmerge!(newval) : newval
38
+ end
39
+ end
40
+
41
+ #
42
+ # Recursive version of Hash#merge
43
+ #
44
+ # Compared with Hash#merge!, this method supports nested hashes.
45
+ # When both +hsh+ and +other_hash+ contains an entry with the same key,
46
+ # it merges and returns the values from both arrays.
47
+ #
48
+ # Compared with Hash#merge, this method provides a different approch
49
+ # for merging nasted hashes.
50
+ # If the value of a given key is an Hash and both +other_hash+ abd +hsh
51
+ # includes the same key, the value is merged instead replaced with
52
+ # +other_hash+ value.
53
+ #
54
+ # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
55
+ # h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}}
56
+ # h1.rmerge(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
57
+ #
58
+ # Simply using Hash#merge would return
59
+ #
60
+ # h1.merge(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
61
+ #
62
+ def rmerge(other_hash)
63
+ r = {}
64
+ merge(other_hash) do |key, oldval, newval|
65
+ r[key] = oldval.class == self.class ? oldval.rmerge(newval) : newval
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+
72
+ class Hash
73
+ include HashRecursiveMerge
74
+ end
data/lib/oauth_util.rb ADDED
@@ -0,0 +1,112 @@
1
+ # A utility for signing an url using OAuth in a way that's convenient for debugging
2
+ # Note: the standard Ruby OAuth lib is here http://github.com/mojodna/oauth
3
+ # Source: http://gist.github.com/383159
4
+ # License: http://gist.github.com/375593
5
+ # Usage: see example.rb below
6
+ #
7
+ # NOTE: This file has been modified from the original Gist:
8
+ #
9
+ # 1. Fix to prevent param-array conversion, as mentioned in Gist comment.
10
+ # 2. Query string escaping has been changed. See:
11
+ # https://github.com/alexreisner/geocoder/pull/360
12
+ #
13
+
14
+ require 'uri'
15
+ require 'cgi'
16
+ require 'openssl'
17
+ require 'base64'
18
+
19
+ class OauthUtil
20
+
21
+ attr_accessor :consumer_key, :consumer_secret, :token, :token_secret, :req_method,
22
+ :sig_method, :oauth_version, :callback_url, :params, :req_url, :base_str
23
+
24
+ def initialize
25
+ @consumer_key = ''
26
+ @consumer_secret = ''
27
+ @token = ''
28
+ @token_secret = ''
29
+ @req_method = 'GET'
30
+ @sig_method = 'HMAC-SHA1'
31
+ @oauth_version = '1.0'
32
+ @callback_url = ''
33
+ end
34
+
35
+ # openssl::random_bytes returns non-word chars, which need to be removed. using alt method to get length
36
+ # ref http://snippets.dzone.com/posts/show/491
37
+ def nonce
38
+ Array.new( 5 ) { rand(256) }.pack('C*').unpack('H*').first
39
+ end
40
+
41
+ def percent_encode( string )
42
+
43
+ # ref http://snippets.dzone.com/posts/show/1260
44
+ return URI.escape( string, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]") ).gsub('*', '%2A')
45
+ end
46
+
47
+ # @ref http://oauth.net/core/1.0/#rfc.section.9.2
48
+ def signature
49
+ key = percent_encode( @consumer_secret ) + '&' + percent_encode( @token_secret )
50
+
51
+ # ref: http://blog.nathanielbibler.com/post/63031273/openssl-hmac-vs-ruby-hmac-benchmarks
52
+ digest = OpenSSL::Digest::Digest.new( 'sha1' )
53
+ hmac = OpenSSL::HMAC.digest( digest, key, @base_str )
54
+
55
+ # ref http://groups.google.com/group/oauth-ruby/browse_thread/thread/9110ed8c8f3cae81
56
+ Base64.encode64( hmac ).chomp.gsub( /\n/, '' )
57
+ end
58
+
59
+ # sort (very important as it affects the signature), concat, and percent encode
60
+ # @ref http://oauth.net/core/1.0/#rfc.section.9.1.1
61
+ # @ref http://oauth.net/core/1.0/#9.2.1
62
+ # @ref http://oauth.net/core/1.0/#rfc.section.A.5.1
63
+ def query_string
64
+ pairs = []
65
+ @params.sort.each { | key, val |
66
+ pairs.push( "#{ CGI.escape(key.to_s).gsub(/%(5B|5D)/n) { [$1].pack('H*') } }=#{ CGI.escape(val.to_s) }" )
67
+ }
68
+ pairs.join '&'
69
+ end
70
+
71
+ # organize params & create signature
72
+ def sign( parsed_url )
73
+
74
+ @params = {
75
+ 'oauth_consumer_key' => @consumer_key,
76
+ 'oauth_nonce' => nonce,
77
+ 'oauth_signature_method' => @sig_method,
78
+ 'oauth_timestamp' => Time.now.to_i.to_s,
79
+ 'oauth_version' => @oauth_version
80
+ }
81
+
82
+ # if url has query, merge key/values into params obj overwriting defaults
83
+ if parsed_url.query
84
+ CGI.parse( parsed_url.query ).each do |k,v|
85
+ if v.is_a?(Array) && v.count == 1
86
+ @params[k] = v.first
87
+ else
88
+ @params[k] = v
89
+ end
90
+ end
91
+ end
92
+
93
+ # @ref http://oauth.net/core/1.0/#rfc.section.9.1.2
94
+ @req_url = parsed_url.scheme + '://' + parsed_url.host + parsed_url.path
95
+
96
+ # create base str. make it an object attr for ez debugging
97
+ # ref http://oauth.net/core/1.0/#anchor14
98
+ @base_str = [
99
+ @req_method,
100
+ percent_encode( req_url ),
101
+
102
+ # normalization is just x-www-form-urlencoded
103
+ percent_encode( query_string )
104
+
105
+ ].join( '&' )
106
+
107
+ # add signature
108
+ @params[ 'oauth_signature' ] = signature
109
+
110
+ return self
111
+ end
112
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+ require 'test_helper'
3
+
4
+ class CacheTest < Test::Unit::TestCase
5
+
6
+ def test_second_occurrence_of_request_is_cache_hit
7
+ Geocoder.configure(:cache => {})
8
+ Geocoder::Lookup.all_services_except_test.each do |l|
9
+ Geocoder.configure(:lookup => l)
10
+ set_api_key!(l)
11
+ results = Geocoder.search("Madison Square Garden")
12
+ assert !results.first.cache_hit,
13
+ "Lookup #{l} returned erroneously cached result."
14
+ results = Geocoder.search("Madison Square Garden")
15
+ assert results.first.cache_hit,
16
+ "Lookup #{l} did not return cached result."
17
+ end
18
+ end
19
+ end
@@ -3,10 +3,10 @@ require 'test_helper'
3
3
 
4
4
  class CalculationsTest < Test::Unit::TestCase
5
5
  def setup
6
- Geocoder.configure do |config|
7
- config.units = :mi
8
- config.distances = :linear
9
- end
6
+ Geocoder.configure(
7
+ :units => :mi,
8
+ :distances => :linear
9
+ )
10
10
  end
11
11
 
12
12
  # --- degree distance ---
@@ -38,6 +38,12 @@ class CalculationsTest < Test::Unit::TestCase
38
38
  assert (la_to_ny - 3942).abs < 10
39
39
  end
40
40
 
41
+ def test_distance_between_in_nautical_miles
42
+ assert_equal 60, Geocoder::Calculations.distance_between([0,0], [0,1], :units => :nm).round
43
+ la_to_ny = Geocoder::Calculations.distance_between([34.05,-118.25], [40.72,-74], :units => :nm).round
44
+ assert (la_to_ny - 2124).abs < 10
45
+ end
46
+
41
47
 
42
48
  # --- geographic center ---
43
49
 
@@ -7,69 +7,44 @@ class ConfigurationTest < Test::Unit::TestCase
7
7
  end
8
8
 
9
9
  def test_exception_raised_on_bad_lookup_config
10
- Geocoder::Configuration.lookup = :stoopid
10
+ Geocoder.configure(:lookup => :stoopid)
11
11
  assert_raises Geocoder::ConfigurationError do
12
12
  Geocoder.search "something dumb"
13
13
  end
14
14
  end
15
15
 
16
- # --- class method configuration ---
17
- def test_configurated_by_class_method
18
- Geocoder::Configuration.units = :mi
19
- distance = Geocoder::Calculations.distance_between([0,0], [0,1]).round
20
- assert_not_equal 111, distance
21
- assert_equal 69, distance
22
-
23
- Geocoder::Configuration.units = :km
24
- distance = Geocoder::Calculations.distance_between([0,0], [0,1]).round
25
- assert_equal 111, distance
26
- assert_not_equal 69, distance
27
-
28
- Geocoder::Configuration.distances = :spherical
29
- angle = Geocoder::Calculations.bearing_between([50,-85], [40.750354, -73.993371]).round
30
- assert_equal 136, angle
31
- assert_not_equal 130, angle
16
+ def test_setting_with_class_method
17
+ Geocoder::Configuration.units = :test
18
+ assert_equal :test, Geocoder.config.units
19
+ end
32
20
 
33
- Geocoder::Configuration.distances = :linear
34
- angle = Geocoder::Calculations.bearing_between([50,-85], [40.750354, -73.993371]).round
35
- assert_not_equal 136, angle
36
- assert_equal 130, angle
21
+ def test_setting_with_configure_method
22
+ Geocoder.configure(:units => :test)
23
+ assert_equal :test, Geocoder.config.units
37
24
  end
38
25
 
39
- # --- Geocoder#configure distances configuration ---
40
- def test_geocoder_configuration
41
- # DSL
26
+ def test_setting_with_block_syntax
27
+ orig = $VERBOSE; $VERBOSE = nil
42
28
  Geocoder.configure do |config|
43
- config.units = :mi
44
- config.distances = :linear
29
+ config.units = :test
45
30
  end
31
+ assert_equal :test, Geocoder.config.units
32
+ ensure
33
+ $VERBOSE = orig
34
+ end
46
35
 
47
- assert_equal Geocoder::Configuration.units, :mi
48
- distance = Geocoder::Calculations.distance_between([0,0], [0,1]).round
49
- assert_not_equal 111, distance
50
- assert_equal 69, distance
51
-
52
- assert_equal Geocoder::Configuration.distances, :linear
53
- angle = Geocoder::Calculations.bearing_between([50,-85], [40.750354, -73.993371]).round
54
- assert_not_equal 136, angle
55
- assert_equal 130, angle
56
-
57
- # Direct
58
- Geocoder.configure.units = :km
59
- Geocoder.configure.distances = :spherical
60
-
61
- assert_equal Geocoder::Configuration.units, :km
62
- distance = Geocoder::Calculations.distance_between([0,0], [0,1]).round
63
- assert_equal 111, distance
64
- assert_not_equal 69, distance
65
-
66
- assert_equal Geocoder::Configuration.distances, :spherical
67
- angle = Geocoder::Calculations.bearing_between([50,-85], [40.750354, -73.993371]).round
68
- assert_equal 136, angle
69
- assert_not_equal 130, angle
36
+ def test_config_for_lookup
37
+ Geocoder.configure(
38
+ :timeout => 5,
39
+ :api_key => "aaa",
40
+ :google => {
41
+ :timeout => 2
42
+ }
43
+ )
44
+ assert_equal 2, Geocoder.config_for_lookup(:google).timeout
45
+ assert_equal "aaa", Geocoder.config_for_lookup(:google).api_key
70
46
  end
71
47
 
72
- # Geocoder per-model configuration
73
48
  def test_model_configuration
74
49
  Landmark.reverse_geocoded_by :latitude, :longitude, :method => :spherical, :units => :km
75
50
  assert_equal :km, Landmark.geocoder_options[:units]
@@ -90,7 +65,7 @@ class ConfigurationTest < Test::Unit::TestCase
90
65
  v.longitude = 0
91
66
 
92
67
  # method option > global configuration
93
- Geocoder.configure.units = :km
68
+ Geocoder.configure(:units => :km)
94
69
  assert_equal 69, v.distance_to([0,1], :mi).round
95
70
 
96
71
  # per-model configuration > global configuration
@@ -4,23 +4,26 @@ require 'test_helper'
4
4
  class ErrorHandlingTest < Test::Unit::TestCase
5
5
 
6
6
  def teardown
7
- Geocoder::Configuration.always_raise = []
7
+ Geocoder.configure(:always_raise => [])
8
8
  end
9
9
 
10
10
  def test_does_not_choke_on_timeout
11
11
  # keep test output clean: suppress timeout warning
12
12
  orig = $VERBOSE; $VERBOSE = nil
13
13
  Geocoder::Lookup.all_services_except_test.each do |l|
14
- Geocoder::Configuration.lookup = l
14
+ Geocoder.configure(:lookup => l)
15
+ set_api_key!(l)
15
16
  assert_nothing_raised { Geocoder.search("timeout") }
16
17
  end
18
+ ensure
17
19
  $VERBOSE = orig
18
20
  end
19
21
 
20
22
  def test_always_raise_timeout_error
21
- Geocoder::Configuration.always_raise = [TimeoutError]
23
+ Geocoder.configure(:always_raise => [TimeoutError])
22
24
  Geocoder::Lookup.all_services_except_test.each do |l|
23
25
  lookup = Geocoder::Lookup.get(l)
26
+ set_api_key!(l)
24
27
  assert_raises TimeoutError do
25
28
  lookup.send(:results, Geocoder::Query.new("timeout"))
26
29
  end
@@ -28,9 +31,10 @@ class ErrorHandlingTest < Test::Unit::TestCase
28
31
  end
29
32
 
30
33
  def test_always_raise_socket_error
31
- Geocoder::Configuration.always_raise = [SocketError]
34
+ Geocoder.configure(:always_raise => [SocketError])
32
35
  Geocoder::Lookup.all_services_except_test.each do |l|
33
36
  lookup = Geocoder::Lookup.get(l)
37
+ set_api_key!(l)
34
38
  assert_raises SocketError do
35
39
  lookup.send(:results, Geocoder::Query.new("socket_error"))
36
40
  end
@@ -0,0 +1 @@
1
+ {"authenticationResultCode":"InvalidCredentials","brandLogoUri":"http:\\/\\/dev.virtualearth.net\\/Branding\\/logo_powered_by.png","copyright":"Copyright \xC2\xA9 2012 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation.","errorDetails":["Access was denied. You may have entered your credentials incorrectly, or you might not have access to the requested resource or operation."],"resourceSets":[],"statusCode":401,"statusDescription":"Unauthorized","traceId":"5c539f6e70c44b2e858741b6c932318e|EWRM001670|02.00.83.1900|"}
@@ -0,0 +1,59 @@
1
+ {
2
+ "spatialReference": {
3
+ "wkid": 4326,
4
+ "latestWkid": 4326
5
+ },
6
+ "locations": [
7
+ {
8
+ "name": "Madison Square Garden",
9
+ "extent": {
10
+ "xmin": -74.000241000000003,
11
+ "ymin": 40.744050000000001,
12
+ "xmax": -73.988241000000002,
13
+ "ymax": 40.756050000000002
14
+ },
15
+ "feature": {
16
+ "geometry": {
17
+ "x": -73.994238897999651,
18
+ "y": 40.750049813000487
19
+ },
20
+ "attributes": {
21
+ "Loc_name": "Gaz.WorldGazetteer.POI2",
22
+ "Score": 100,
23
+ "Match_addr": "Madison Square Garden",
24
+ "Addr_type": "POI",
25
+ "Type": "Sports Complex",
26
+ "PlaceName": "Madison Square Garden",
27
+ "Rank": "18",
28
+ "AddBldg": "",
29
+ "AddNum": "",
30
+ "AddNumFrom": "",
31
+ "AddNumTo": "",
32
+ "Side": "",
33
+ "StPreDir": "",
34
+ "StPreType": "",
35
+ "StName": "",
36
+ "StType": "",
37
+ "StDir": "",
38
+ "Nbrhd": "",
39
+ "City": "New York",
40
+ "Subregion": "New York",
41
+ "Region": "New York",
42
+ "Postal": "10001",
43
+ "PostalExt": "",
44
+ "Country": "USA",
45
+ "LangCode": "",
46
+ "Distance": 0,
47
+ "X": -73.994240000000005,
48
+ "Y": 40.750050000000002,
49
+ "DisplayX": -73.994240000000005,
50
+ "DisplayY": 40.750050000000002,
51
+ "Xmin": -74.000241000000003,
52
+ "Xmax": -73.988241000000002,
53
+ "Ymin": 40.744050000000001,
54
+ "Ymax": 40.756050000000002
55
+ }
56
+ }
57
+ }
58
+ ]
59
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "spatialReference": {
3
+ "wkid": 4326,
4
+ "latestWkid": 4326
5
+ },
6
+ "locations": [
7
+ ]
8
+ }