Skip to content
Snippets Groups Projects
Commit 3259fdfb authored by Alex Reisner's avatar Alex Reisner
Browse files

Make Geocoder.search return an array of results.

After further consideration, multiple results should be the default
behavior for this method. Some services only return one result (eg: any
IP-based service) but a great feature of advanced services like Yahoo
and Google is that they return an array of choices when doing a vague
search like "100 Main St" with no city, region, or country specified.
parent 63dd486a
No related branches found
No related tags found
No related merge requests found
......@@ -10,7 +10,7 @@ module Geocoder
# Search for information about an address or a set of coordinates.
#
def search(*args)
return nil if blank_query?(args[0])
return [] if blank_query?(args[0])
ip = (args.size == 1 and ip_address?(args.first))
lookup(ip).search(*args)
end
......@@ -19,8 +19,8 @@ module Geocoder
# Look up the coordinates of the given street or IP address.
#
def coordinates(address)
if result = search(address)
result.coordinates
if (results = search(address)).size > 0
results.first.coordinates
end
end
......@@ -28,8 +28,8 @@ module Geocoder
# Look up the address of the given coordinates.
#
def address(latitude, longitude)
if result = search(latitude, longitude)
result.address
if (results = search(latitude, longitude)).size > 0
results.first.address
end
end
......
......@@ -17,13 +17,11 @@ module Geocoder
#
# Takes a search string (eg: "Mississippi Coast Coliseumf, Biloxi, MS",
# "205.128.54.202") for geocoding, or coordinates (latitude, longitude)
# for reverse geocoding.
# for reverse geocoding. Returns an array of <tt>Geocoder::Result</tt>s.
#
def search(*args)
reverse = (args.size == 2) || coordinates?(args.first)
if res = result(args.join(","), reverse)
result_class.new(res)
end
results(args.join(","), reverse).map{ |r| result_class.new(r) }
end
......@@ -32,7 +30,7 @@ module Geocoder
##
# Geocoder::Result object or nil on timeout or other error.
#
def result(query, reverse = false)
def results(query, reverse = false)
fail
end
......
......@@ -6,13 +6,13 @@ module Geocoder::Lookup
private # ---------------------------------------------------------------
def result(query, reverse = false)
def results(query, reverse = false)
# don't look up a loopback address, just return the stored result
return reserved_result(query) if loopback_address?(query)
return [reserved_result(query)] if loopback_address?(query)
begin
fetch_data(query, reverse)
return [fetch_data(query, reverse)]
rescue StandardError # Freegeoip.net returns HTML on bad request
nil
return []
end
end
......
......@@ -6,15 +6,16 @@ module Geocoder::Lookup
private # ---------------------------------------------------------------
def result(query, reverse = false)
return nil unless doc = fetch_data(query, reverse)
def results(query, reverse = false)
return [] unless doc = fetch_data(query, reverse)
if doc['error'].nil?
doc
return [doc]
elsif doc['error']['code'] == "005"
nil # "Postal Code is not in the proper Format" => no results, just shut up
# "Postal Code is not in the proper Format" => no results, just shut up
else
warn "Geocoder.ca service error: #{doc['error']['code']} (#{doc['error']['description']})."
end
return []
end
def query_url(query, reverse = false)
......
......@@ -6,10 +6,10 @@ module Geocoder::Lookup
private # ---------------------------------------------------------------
def result(query, reverse = false)
return nil unless doc = fetch_data(query, reverse)
def results(query, reverse = false)
return [] unless doc = fetch_data(query, reverse)
case doc['status']; when "OK" # OK status implies >0 results
doc['results'].first
return doc['results']
when "OVER_QUERY_LIMIT"
warn "Google Geocoding API error: over query limit."
when "REQUEST_DENIED"
......@@ -17,6 +17,7 @@ module Geocoder::Lookup
when "INVALID_REQUEST"
warn "Google Geocoding API error: invalid request."
end
return []
end
def query_url(query, reverse = false)
......
......@@ -6,12 +6,13 @@ module Geocoder::Lookup
private # ---------------------------------------------------------------
def result(query, reverse = false)
return nil unless doc = fetch_data(query, reverse)
def results(query, reverse = false)
return [] unless doc = fetch_data(query, reverse)
if doc = doc['ResultSet'] and doc['Error'] == 0
doc['Results'].first if doc['Found'] > 0
return doc['Found'] > 0 ? doc['Results'] : []
else
warn "Yahoo Geocoding API error: #{doc['Error']} (#{doc['ErrorMessage']})."
return []
end
end
......
......@@ -151,7 +151,8 @@ module Geocoder::Orm
# (or other as specified in +geocoded_by+). Returns coordinates (array).
#
def geocode
do_lookup(false) do |o,r|
do_lookup(false) do |o,rs|
r = rs.first
unless r.latitude.nil? or r.longitude.nil?
o.send :write_attribute, self.class.geocoder_options[:latitude], r.latitude
o.send :write_attribute, self.class.geocoder_options[:longitude], r.longitude
......@@ -167,7 +168,8 @@ module Geocoder::Orm
# in +reverse_geocoded_by+). Returns address (string).
#
def reverse_geocode
do_lookup(true) do |o,r|
do_lookup(true) do |o,rs|
r = rs.first
unless r.address.nil?
o.send :write_attribute, self.class.geocoder_options[:fetched_address], r.address
end
......
......@@ -7,7 +7,8 @@ module Geocoder::Orm::ActiveRecord
def fetch_coordinates!
warn "DEPRECATION WARNING: The 'fetch_coordinates!' method is deprecated and will be removed in geocoder v1.0. " +
"Please use 'geocode' instead and then save your objects manually."
do_lookup(false) do |o,r|
do_lookup(false) do |o,rs|
r = rs.first
unless r.latitude.nil? or r.longitude.nil?
o.send :update_attribute, self.class.geocoder_options[:latitude], r.latitude
o.send :update_attribute, self.class.geocoder_options[:longitude], r.longitude
......@@ -19,7 +20,8 @@ module Geocoder::Orm::ActiveRecord
def fetch_coordinates(*args)
warn "DEPRECATION WARNING: The 'fetch_coordinates' method will cease taking " +
"an argument in geocoder v1.0. Please save your objects manually." if args.size > 0
do_lookup(false) do |o,r|
do_lookup(false) do |o,rs|
r = rs.first
unless r.latitude.nil? or r.longitude.nil?
method = ((args.size > 0 && args.first) ? "update" : "write" ) + "_attribute"
o.send method, self.class.geocoder_options[:latitude], r.latitude
......@@ -35,7 +37,8 @@ module Geocoder::Orm::ActiveRecord
def fetch_address!
warn "DEPRECATION WARNING: The 'fetch_address!' method is deprecated and will be removed in geocoder v1.0. " +
"Please use 'reverse_geocode' instead and then save your objects manually."
do_lookup(true) do |o,r|
do_lookup(true) do |o,rs|
r = rs.first
unless r.address.nil?
o.send :update_attribute, self.class.geocoder_options[:fetched_address], r.address
end
......@@ -46,7 +49,8 @@ module Geocoder::Orm::ActiveRecord
def fetch_address(*args)
warn "DEPRECATION WARNING: The 'fetch_address' method will cease taking " +
"an argument in geocoder v1.0. Please save your objects manually." if args.size > 0
do_lookup(true) do |o,r|
do_lookup(true) do |o,rs|
r = rs.first
unless r.latitude.nil? or r.longitude.nil?
method = ((args.size > 0 && args.first) ? "update" : "write" ) + "_attribute"
o.send method, self.class.geocoder_options[:fetched_address], r.address
......
......@@ -60,10 +60,10 @@ module Geocoder
##
# Look up geographic data based on object attributes (configured in
# geocoded_by or reverse_geocoded_by) and handle the result with the
# geocoded_by or reverse_geocoded_by) and handle the results with the
# block (given to geocoded_by or reverse_geocoded_by). The block is
# given two-arguments: the object being geocoded and a
# Geocoder::Result object with the geocoding results).
# given two-arguments: the object being geocoded and an array of
# Geocoder::Result objects).
#
def do_lookup(reverse = false)
options = self.class.geocoder_options
......@@ -76,17 +76,17 @@ module Geocoder
end
args.map!{ |a| send(options[a]) }
if result = Geocoder.search(*args)
if (results = Geocoder.search(*args)).size > 0
# execute custom block, if specified in configuration
block_key = reverse ? :reverse_block : :geocode_block
if custom_block = options[block_key]
custom_block.call(self, result)
custom_block.call(self, results)
# else execute block passed directly to this method,
# which generally performs the "auto-assigns"
elsif block_given?
yield(self, result)
yield(self, results)
end
end
end
......
......@@ -5,7 +5,7 @@ module Geocoder
def location
unless defined?(@location)
@location = Geocoder.search(ip)
@location = Geocoder.search(ip).first
end
@location
end
......
......@@ -77,11 +77,11 @@ class GeocoderTest < Test::Unit::TestCase
end
def test_coordinates_method
assert_not_nil Geocoder.coordinates("Madison Square Garden, New York, NY")
assert Geocoder.coordinates("Madison Square Garden, New York, NY").is_a?(Array)
end
def test_address_method
assert_not_nil Geocoder.address(40.750354, -73.993371)
assert Geocoder.address(40.750354, -73.993371).is_a?(String)
end
......@@ -153,18 +153,18 @@ class GeocoderTest < Test::Unit::TestCase
assert_equal address, v.address
end
def test_returns_nil_when_no_results
def test_search_returns_empty_array_when_no_results
street_lookups.each do |l|
Geocoder::Configuration.lookup = l
assert_nil Geocoder.search("no results"),
"Lookup #{l} does not return nil when no results."
assert_equal [], Geocoder.search("no results"),
"Lookup #{l} does not return empty array when no results."
end
end
def test_result_has_required_attributes
all_lookups.each do |l|
Geocoder::Configuration.lookup = l
result = Geocoder.search(45.423733, -75.676333)
result = Geocoder.search(45.423733, -75.676333).first
assert_result_has_required_attributes(result)
end
end
......@@ -173,13 +173,13 @@ class GeocoderTest < Test::Unit::TestCase
# --- Google ---
def test_google_result_components
result = Geocoder.search("Madison Square Garden, New York, NY")
result = Geocoder.search("Madison Square Garden, New York, NY").first
assert_equal "Manhattan",
result.address_components_of_type(:sublocality).first['long_name']
end
def test_google_returns_city_when_no_locality_in_result
result = Geocoder.search("no locality")
result = Geocoder.search("no locality").first
assert_equal "Haram", result.city
end
......@@ -188,13 +188,13 @@ class GeocoderTest < Test::Unit::TestCase
def test_yahoo_result_components
Geocoder::Configuration.lookup = :yahoo
result = Geocoder.search("Madison Square Garden, New York, NY")
result = Geocoder.search("Madison Square Garden, New York, NY").first
assert_equal "10001", result.postal_code
end
def test_yahoo_address_formatting
Geocoder::Configuration.lookup = :yahoo
result = Geocoder.search("Madison Square Garden, New York, NY")
result = Geocoder.search("Madison Square Garden, New York, NY").first
assert_equal "Madison Square Garden, New York, NY 10001, United States",
result.address
end
......@@ -204,7 +204,7 @@ class GeocoderTest < Test::Unit::TestCase
def test_geocoder_ca_result_components
Geocoder::Configuration.lookup = :geocoder_ca
result = Geocoder.search(45.423733, -75.676333)
result = Geocoder.search(45.423733, -75.676333).first
assert_equal "CA", result.country_code
assert_equal "289 Somerset ST E, Ottawa, ON K1N6W1, Canada", result.address
end
......@@ -213,12 +213,12 @@ class GeocoderTest < Test::Unit::TestCase
# --- FreeGeoIp ---
def test_freegeoip_result_on_ip_address_search
result = Geocoder.search("74.200.247.59")
result = Geocoder.search("74.200.247.59").first
assert result.is_a?(Geocoder::Result::Freegeoip)
end
def test_freegeoip_result_components
result = Geocoder.search("74.200.247.59")
result = Geocoder.search("74.200.247.59").first
assert_equal "Plano, TX 75093, United States", result.address
end
......@@ -258,7 +258,7 @@ class GeocoderTest < Test::Unit::TestCase
def test_detection_of_coordinates_in_search_string
Geocoder::Configuration.lookup = :geocoder_ca
result = Geocoder.search("51.178844, -1.826189")
result = Geocoder.search("51.178844, -1.826189").first
assert_not_nil result.city
# city only present if reverse geocoding search performed
end
......
......@@ -134,8 +134,8 @@ end
# Geocoded model with block.
#
class Event < ActiveRecord::Base
geocoded_by :address do |obj,result|
if result
geocoded_by :address do |obj,results|
if result = results.first
obj.coords_string = "#{result.latitude},#{result.longitude}"
end
end
......@@ -151,8 +151,8 @@ end
# Reverse geocoded model with block.
#
class Party < ActiveRecord::Base
reverse_geocoded_by :latitude, :longitude do |obj,result|
if result
reverse_geocoded_by :latitude, :longitude do |obj,results|
if result = results.first
obj.country = result.country_code
end
end
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment