Skip to content
Snippets Groups Projects
geocoder.rb 3.12 KiB
Newer Older
require "geocoder/configuration"
require "geocoder/calculations"
require "geocoder/exceptions"
require "geocoder/cache"
require "geocoder/request"
require "geocoder/models/active_record" if defined?(::ActiveRecord)
require "geocoder/models/mongoid" if defined?(::Mongoid)
require "geocoder/models/mongo_mapper" if defined?(::MongoMapper)
module Geocoder
  extend self

  ##
Alex Reisner's avatar
Alex Reisner committed
  # Search for information about an address or a set of coordinates.
  def search(query)
    blank_query?(query) ? [] : lookup(query).search(query)
Alex Reisner's avatar
Alex Reisner committed
  # Look up the coordinates of the given street or IP address.
  #
  def coordinates(address)
    if (results = search(address)).size > 0
      results.first.coordinates
  # Look up the address of the given coordinates ([lat,lon])
  # or IP address (string).
  def address(query)
    if (results = search(query)).size > 0
      results.first.address
  ##
  # The working Cache object, or +nil+ if none configured.
  #
  def cache
    if @cache.nil? and store = Configuration.cache
      @cache = Cache.new(store, Configuration.cache_prefix)
    end
    @cache
  end

Alex Reisner's avatar
Alex Reisner committed
  ##
  # Array of valid Lookup names.
  #
  def valid_lookups
    street_lookups + ip_lookups
  end

  ##
  # All street address lookups, default first.
  #
  def street_lookups
Chris Myers's avatar
Chris Myers committed
    [:google, :google_premier, :yahoo, :bing, :geocoder_ca, :yandex, :nominatim]
  end

  ##
  # All IP address lookups, default first.
  #
  def ip_lookups
    [:freegeoip]

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

Alex Reisner's avatar
Alex Reisner committed
  # Get a Lookup object (which communicates with the remote geocoding API).
  # Takes a search query and returns an IP or street address Lookup
  # depending on the query contents.
  def lookup(query)
    if ip_address?(query)
      get_lookup(ip_lookups.first)
Alex Reisner's avatar
Alex Reisner committed
    else
      get_lookup(Configuration.lookup || street_lookups.first)
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)
    @lookups = {} unless defined?(@lookups)
    @lookups[name] = spawn_lookup(name) unless @lookups.include?(name)
Alex Reisner's avatar
Alex Reisner committed
    @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
      valids = valid_lookups.map(&:inspect).join(", ")
      raise ConfigurationError, "Please specify a valid lookup for Geocoder " +
        "(#{name.inspect} is not one of: #{valids})."
Alex Reisner's avatar
Alex Reisner committed

  ##
  # 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 numbers.
Alex Reisner's avatar
Alex Reisner committed
  #
Alex Reisner's avatar
Alex Reisner committed
  def ip_address?(value)
    !!value.to_s.match(/^(::ffff:)?(\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(/^\s*$/)
# load Railtie if Rails exists
if defined?(Rails)
  require "geocoder/railtie"
  Geocoder::Railtie.insert
end