Re: Factor

Factor: the language, the theory, and the practice.


Friday, June 26, 2009


Providing location-based services is all the rage these days. On the newer mobile devices, there are services that use GPS and nearby cellular towers to improve your user experience with the knowledge of where you are at any moment.

In the wild internet, there hasn’t been many convenient ways to do this, but a recent website called IPInfoDB (previously known as “IP Location Tools”), provides an API for mapping an IP address to a physical address. The API consists of a PHP page that returns an XML response that contains your location, or the location of an IP address that you provide. They also seem to support CSV and JSON responses.

For example, to lookup the location of an IP address used by

Curious to see how easily it would be to use this API from Factor, I wrote a vocabulary for accessing IPInfoDB.

First, we define the characteristics of an ip-info tuple.

TUPLE: ip-info ip country-code country-name region-code
region-name city zip-code latitude longitude gmtoffset
dstoffset ;

The API returns XML response, which we need to convert into an ip-info object. For this, we use the excellent XML parsing vocabulary that has seen a lot of improvements recently.

: find-tag ( tag name -- value )
    deep-tag-named children>string ; inline

: xml>ip-info ( xml -- info )
    [ ip-info new ] dip
        [ "Ip" find-tag >>ip ]
        [ "CountryCode" find-tag >>country-code ]
        [ "CountryName" find-tag >>country-name ]
        [ "RegionCode" find-tag >>region-code ]
        [ "RegionName" find-tag >>region-name ]
        [ "City" find-tag >>city ]
        [ "ZipPostalCode" find-tag >>zip-code ]
        [ "Latitude" find-tag >>latitude ]
        [ "Longitude" find-tag >>longitude ]
        [ "Gmtoffset" find-tag >>gmtoffset ]
        [ "Dstoffset" find-tag >>dstoffset ]
    } cleave ;

With that done, it is very easy to make an http request to retrieve one’s own location:

: locate-my-ip ( -- info ) 
    http-get string>xml xml>ip-info nip ;

Or determine the location of any arbitrary IP:

: locate-ip ( ip -- info )
    "" prepend
    http-get string>xml xml>ip-info nip ;

The example I showed earlier would be:

IN: scratchpad "" locate-ip .
T{ ip-info
    { ip "" }
    { country-code "US" }
    { country-name "United States" }
    { region-code "06" }
    { region-name "California" }
    { city "Mountain View" }
    { zip-code "94043" }
    { latitude "37.4192" }
    { longitude "-122.057" }
    { gmtoffset "-8.0" }
    { dstoffset "-7.0" }

Presumably, if this code were to be used more frequently, it would need some error handling for when the http server is not available, or non-responsive.

Anyway, this vocabulary is available on my GitHub