diff --git a/lib/geocoder.rb b/lib/geocoder.rb index 372e49c9fb5d89d2268efb5cb56ad786edf9f7a7..b7c8be64eee5992e7a88ec9cebb46d82965b2c9f 100644 --- a/lib/geocoder.rb +++ b/lib/geocoder.rb @@ -7,14 +7,16 @@ module Geocoder extend self ## - # Alias for Geocoder.lookup.search. + # Search for information about an address or a set of coordinates. # def search(*args) - lookup.search(*args) + return [] if args[0].nil? || args[0] == "" + ip = (args.size == 1 and ip_address?(args.first)) + lookup(ip).search(*args) end ## - # Look up the coordinates of the given street address. + # Look up the coordinates of the given street or IP address. # def coordinates(address) if (results = search(address)).size > 0 @@ -41,23 +43,44 @@ module Geocoder ## # Get the lookup object (which communicates with the remote geocoding API). + # Returns an IP address lookup if +ip+ parameter true. # - def lookup - unless defined?(@lookup) - set_lookup Geocoder::Configuration.lookup + def lookup(ip = false) + if ip + get_lookup :freegeoip + else + get_lookup Geocoder::Configuration.lookup || :google end - @lookup end - def set_lookup(value) - if value == :yahoo - require "geocoder/lookups/yahoo" - @lookup = Geocoder::Lookup::Yahoo.new - else - require "geocoder/lookups/google" - @lookup = Geocoder::Lookup::Google.new + def get_lookup(name) + unless defined?(@lookups) + @lookups = {} + end + unless @lookups.include?(name) + @lookups[name] = spawn_lookup(name) + end + @lookups[name] + end + + def spawn_lookup(name) + if valid_lookups.include?(name) + name = name.to_s + require "geocoder/lookups/#{name}" + eval("Geocoder::Lookup::#{name[0...1].upcase + name[1..-1]}.new") end end + + def valid_lookups + [:google, :yahoo, :freegeoip] + end + + ## + # Does the given value look like an IP address? + # + def ip_address?(value) + value.match /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ + end end Geocoder::Railtie.insert diff --git a/lib/geocoder/lookups/freegeoip.rb b/lib/geocoder/lookups/freegeoip.rb new file mode 100644 index 0000000000000000000000000000000000000000..ee6d8a6905b39d07cf1964b869b24bdb75f56148 --- /dev/null +++ b/lib/geocoder/lookups/freegeoip.rb @@ -0,0 +1,23 @@ +require 'geocoder/lookups/base' +require 'geocoder/results/freegeoip' + +module Geocoder::Lookup + class Freegeoip < Base + + private # --------------------------------------------------------------- + + def results(query, reverse = false) + begin + if doc = fetch_data(query, reverse) + [doc] + end + rescue StandardError # Freegeoip.net returns HTML on bad request + nil + end + end + + def query_url(query, reverse = false) + "http://freegeoip.net/json/#{query}" + end + end +end diff --git a/lib/geocoder/results/freegeoip.rb b/lib/geocoder/results/freegeoip.rb new file mode 100644 index 0000000000000000000000000000000000000000..00388bad11abc7d7c8f7a1dfcb1b33ea81077b93 --- /dev/null +++ b/lib/geocoder/results/freegeoip.rb @@ -0,0 +1,25 @@ +require 'geocoder/results/base' + +module Geocoder::Result + class Freegeoip < Base + + def coordinates + [latitude.to_f, longitude.to_f] + end + + def address(format = :full) + "#{city}#{', ' + region_code unless region_code == ''} #{zipcode}, #{country_name}" + end + + def self.response_attributes + %w[city region_code region_name metrocode zipcode + latitude longitude country_name country_code ip] + end + + response_attributes.each do |a| + define_method a do + @data[a] + end + end + end +end diff --git a/test/fixtures/freegeoip_74_200_247_59.json b/test/fixtures/freegeoip_74_200_247_59.json new file mode 100644 index 0000000000000000000000000000000000000000..78c7ce3e57bcf79534475006a19c0cbeba3422ad --- /dev/null +++ b/test/fixtures/freegeoip_74_200_247_59.json @@ -0,0 +1,12 @@ +{ + "city": "Plano", + "region_code": "TX", + "region_name": "Texas", + "metrocode": "623", + "zipcode": "75093", + "longitude": "-96.8134", + "country_name": "United States", + "country_code": "US", + "ip": "74.200.247.59", + "latitude": "33.0347" +} diff --git a/test/geocoder_test.rb b/test/geocoder_test.rb index 6b6042caa3697df1eae1e3d70389d21505de9438..a9752e94db5104fcbb81da503bb045d01ebe7005 100644 --- a/test/geocoder_test.rb +++ b/test/geocoder_test.rb @@ -4,7 +4,6 @@ class GeocoderTest < Test::Unit::TestCase def setup Geocoder::Configuration.lookup = :google - Geocoder.send :set_lookup, :google end def test_fetch_coordinates @@ -49,16 +48,25 @@ class GeocoderTest < Test::Unit::TestCase # --- Yahoo --- def test_yahoo_result_components Geocoder::Configuration.lookup = :yahoo - Geocoder.send :set_lookup, :yahoo results = Geocoder.search("Madison Square Garden, New York, NY") assert_equal "10001", results.first.postal end def test_yahoo_address_formatting Geocoder::Configuration.lookup = :yahoo - Geocoder.send :set_lookup, :yahoo results = Geocoder.search("Madison Square Garden, New York, NY") assert_equal "Madison Square Garden, New York, NY 10001, United States", results.first.address end + + # --- FreeGeoIp --- + def test_freegeoip_result_on_ip_address_search + results = Geocoder.search("74.200.247.59") + assert results.first.is_a?(Geocoder::Result::Freegeoip) + end + + def test_freegeoip_result_components + results = Geocoder.search("74.200.247.59") + assert_equal "Plano, TX 75093, United States", results.first.address + end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 2ec28ee2be26904534f2ddac3a1eb528b99cea28..b2017a9d29a2bde1452ecf8bd9c9931b85fc55d8 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -40,10 +40,24 @@ require "geocoder/lookups/base" # module Geocoder module Lookup - class Base + class Google < Base private #----------------------------------------------------------------- def fetch_raw_data(query, reverse = false) - File.read(File.join("test", "fixtures", "#{Geocoder::Configuration.lookup}_madison_square_garden.json")) + File.read(File.join("test", "fixtures", "google_madison_square_garden.json")) + end + end + + class Yahoo < Base + private #----------------------------------------------------------------- + def fetch_raw_data(query, reverse = false) + File.read(File.join("test", "fixtures", "yahoo_madison_square_garden.json")) + end + end + + class Freegeoip < Base + private #----------------------------------------------------------------- + def fetch_raw_data(query, reverse = false) + File.read(File.join("test", "fixtures", "freegeoip_74_200_247_59.json")) end end end