Skip to content
Snippets Groups Projects
geocoder.rb 2.44 KiB
Newer Older
require "geocoder/configuration"
require "geocoder/calculations"
require "geocoder/railtie"
require "geocoder/request"
module Geocoder
  extend self

  ##
Alex Reisner's avatar
Alex Reisner committed
  # Search for information about an address or a set of coordinates.
  #
  def search(*args)
    return nil if blank_query?(args[0])
Alex Reisner's avatar
Alex Reisner committed
    ip = (args.size == 1 and ip_address?(args.first))
    lookup(ip).search(*args)
Alex Reisner's avatar
Alex Reisner committed
  # Look up the coordinates of the given street or IP address.
  #
  def coordinates(address)
    if result = search(address)
      result.coordinates
    end
  end

  ##
  # Look up the address of the given coordinates.
  #
  def address(latitude, longitude)
    if result = search(latitude, longitude)
      result.address
    end
  end


  # exception classes
  class Error < StandardError; end
  class ConfigurationError < Error; end


  private # -----------------------------------------------------------------

Alex Reisner's avatar
Alex Reisner committed
  # Get a Lookup object (which communicates with the remote geocoding API).
Alex Reisner's avatar
Alex Reisner committed
  # Returns an IP address lookup if +ip+ parameter true.
Alex Reisner's avatar
Alex Reisner committed
  def lookup(ip = false)
    if ip
      get_lookup :freegeoip
    else
      get_lookup Geocoder::Configuration.lookup || :google
Alex Reisner's avatar
Alex Reisner committed
  ##
  # Retrieve a Lookup object from the store.
  #
Alex Reisner's avatar
Alex Reisner committed
  def get_lookup(name)
    unless defined?(@lookups)
      @lookups = {}
    end
    unless @lookups.include?(name)
      @lookups[name] = spawn_lookup(name)
    end
    @lookups[name]
  end

Alex Reisner's avatar
Alex Reisner committed
  ##
  # Spawn a Lookup of the given name.
  #
Alex Reisner's avatar
Alex Reisner committed
  def spawn_lookup(name)
    if valid_lookups.include?(name)
      name = name.to_s
      require "geocoder/lookups/#{name}"
      klass = name.split("_").map{ |i| i[0...1].upcase + i[1..-1] }.join
      eval("Geocoder::Lookup::#{klass}.new")
    else
      valids = valid_lookups.map{ |l| ":#{l}" }.join(", ")
      raise ConfigurationError, "Please specify a valid lookup for Geocoder " +
        "(#{name.inspect} is not one of: #{valids})."
Alex Reisner's avatar
Alex Reisner committed
  ##
  # Array of valid Lookup names.
  #
Alex Reisner's avatar
Alex Reisner committed
  def valid_lookups
Alex Reisner's avatar
Alex Reisner committed
    [:google, :yahoo, :geocoder_ca, :freegeoip]
Alex Reisner's avatar
Alex Reisner committed
  end

  ##
  # Does the given value look like an IP address?
  #
Alex Reisner's avatar
Alex Reisner committed
  # Does not check for actual validity, just the appearance of four
  # dot-delimited 8-bit numbers.
  #
Alex Reisner's avatar
Alex Reisner committed
  def ip_address?(value)
    !!value.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/)
Alex Reisner's avatar
Alex Reisner committed
  end

  ##
  # Is the given search query blank? (ie, should we not bother searching?)
  #
  def blank_query?(value)
    !value.to_s.match(/[A-z0-9]/)
  end
Geocoder::Railtie.insert