Skip to content
Snippets Groups Projects
geocoder.rb 4.44 KiB
Newer Older
  • Learn to ignore specific revisions
  • require "geocoder/configuration"
    
    require "geocoder/calculations"
    
    require "geocoder/cache"
    
    require "geocoder/request"
    
    require "geocoder/models/active_record"
    require "geocoder/models/mongoid"
    
    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, *args)
        # convert coordinates as separate arguments to an array
        if query.is_a?(Numeric) and args.first.is_a?(Numeric)
          warn "DEPRECATION WARNING: Instead of passing latitude/longitude as separate arguments to the search method, please pass an array: [#{query},#{args.first}]. The old argument format will not be supported in Geocoder v.1.0."
          query = [query, args.first]
        end
        if blank_query?(query)
    
    Alex Reisner's avatar
    Alex Reisner committed
          results = []
        else
    
          results = lookup(ip_address?(query)).search(query)
    
    Alex Reisner's avatar
    Alex Reisner committed
        end
        results.instance_eval do
          def warn_search_deprecation(attr)
            warn "DEPRECATION WARNING: Geocoder.search now returns an array of Geocoder::Result objects. " +
              "Calling '%s' directly on the returned array will cause an exception in Geocoder v1.0." % attr
          end
    
          def coordinates; warn_search_deprecation('coordinates'); first.coordinates if first; end
          def latitude; warn_search_deprecation('latitude'); first.latitude if first; end
          def longitude; warn_search_deprecation('longitude'); first.longitude if first; end
          def address; warn_search_deprecation('address'); first.address if first; end
          def city; warn_search_deprecation('city'); first.city if first; end
          def country; warn_search_deprecation('country'); first.country if first; end
          def country_code; warn_search_deprecation('country_code'); first.country_code if first; end
        end
        return results
    
    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, *args)
        if lon = args.first
          warn "DEPRECATION WARNING: Instead of passing latitude/longitude as separate arguments to the address method, please pass an array: [#{query},#{args.first}]. The old argument format will not be supported in Geocoder v.1.0."
          query = [query, lon]
        end
        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
        [:google, :yahoo, :geocoder_ca, :yandex, :freegeoip]
      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 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
    
      ##
      # 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.to_s.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(/^\s*$/)
    
    # load Railtie if Rails exists
    if defined?(Rails)
      require "geocoder/railtie"
      Geocoder::Railtie.insert
    end