From fd5f7e09f4bd50a20876f41f947af4d5e470b5c6 Mon Sep 17 00:00:00 2001 From: Alex Reisner <alex@alexreisner.com> Date: Fri, 4 Mar 2011 01:15:34 -0500 Subject: [PATCH] Add IP address geocoding. --- lib/geocoder.rb | 51 ++++++++++++++++------ lib/geocoder/lookups/freegeoip.rb | 23 ++++++++++ lib/geocoder/results/freegeoip.rb | 25 +++++++++++ test/fixtures/freegeoip_74_200_247_59.json | 12 +++++ test/geocoder_test.rb | 14 ++++-- test/test_helper.rb | 18 +++++++- 6 files changed, 124 insertions(+), 19 deletions(-) create mode 100644 lib/geocoder/lookups/freegeoip.rb create mode 100644 lib/geocoder/results/freegeoip.rb create mode 100644 test/fixtures/freegeoip_74_200_247_59.json diff --git a/lib/geocoder.rb b/lib/geocoder.rb index 372e49c9..b7c8be64 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 00000000..ee6d8a69 --- /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 00000000..00388bad --- /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 00000000..78c7ce3e --- /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 6b6042ca..a9752e94 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 2ec28ee2..b2017a9d 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 -- GitLab