Geolocation with MaxMind's GeoIP and the geoip-city RubyGem
Ruby posted over 3 years ago by christian
http://snippets.aktagon.com/snippets/179-Geolocation-with-MaxMind-s-GeoIP-and-the-geoip-city-RubyGemInstall GeoIP library
wget http<span class="UserDefinedConstant"><span class="UserDefinedConstant">:</span>/</span><span class="Operator">/</span>www.<span class="FunctionName">maxmind</span>.<span class="FunctionName">com</span><span class="Operator">/</span>download<span class="Operator">/</span>geoip<span class="Operator">/</span>api<span class="Operator">/</span>c<span class="Operator">/</span><span class="LibraryClassType">GeoIP</span>.<span class="FunctionName">tar</span>.<span class="FunctionName">gz</span> tar <span class="Operator">-</span>zxvf <span class="LibraryClassType">GeoIP</span>.<span class="FunctionName">tar</span>.<span class="FunctionName">gz</span> cd <span class="Variable">GeoIP</span> .<span class="Operator">/</span>configure <span class="Operator">-</span><span class="Operator">-</span>prefix<span class="Operator">=</span><span class="String"><span class="String">/</span></span><span class="String">opt</span><span class="String"><span class="String">/</span></span><span class="Variable">GeoIP</span> make <span class="Operator">&&</span> sudo make install
Install the geoip-city gem
gem install geoip_city <span class="Operator">-</span><span class="Operator">-</span> <span class="Operator">-</span><span class="Operator">-</span>with<span class="Operator">-</span>geoip<span class="Operator">-</span>dir<span class="Operator">=</span><span class="String"><span class="String">/</span></span><span class="String">opt</span><span class="String"><span class="String">/</span></span><span class="Variable">GeoIP</span>
Test the bindings
curl <span class="Operator">-</span><span class="Variable">O</span> http<span class="UserDefinedConstant"><span class="UserDefinedConstant">:</span>/</span><span class="Operator">/</span>geolite.<span class="FunctionName">maxmind</span>.<span class="FunctionName">com</span><span class="Operator">/</span>download<span class="Operator">/</span>geoip<span class="Operator">/</span>database<span class="Operator">/</span><span class="LibraryClassType">GeoLiteCity</span>.<span class="FunctionName">dat</span>.<span class="FunctionName">gz</span> gunzip <span class="LibraryClassType">GeoLiteCity</span>.<span class="FunctionName">dat</span>.<span class="FunctionName">gz</span>
Fire up IRB and try the following code:
<span class="Keyword">require</span> <span class="String"><span class="String">'</span>rubygems<span class="String">'</span></span> <span class="Keyword">require</span> <span class="String"><span class="String">'</span>geoip_city<span class="String">'</span></span> db <span class="Operator">=</span> <span class="LibraryClassType">GeoIPCity</span>::<span class="FunctionName">Database</span>.<span class="FunctionName">new</span>(<span class="String"><span class="String">'</span>GeoLiteCity.dat<span class="String">'</span></span>) result <span class="Operator">=</span> db.<span class="FunctionName">look_up</span>(<span class="String"><span class="String">'</span>192.143.34.23<span class="String">'</span></span>) p result
Another option is to use hostip.info’s database, as described in this article.
Create a wrapper
<span class="Keyword">require</span> <span class="String"><span class="String">'</span>rubygems<span class="String">'</span></span> <span class="Keyword">require</span> <span class="String"><span class="String">'</span>geoip_city<span class="String">'</span></span> <span class="Keyword">require</span> <span class="String"><span class="String">'</span>ostruct<span class="String">'</span></span> <span class="Keyword">class</span> <span class="TypeName">Location<span class="InheritedClass"> <span class="InheritedClass"><</span> OpenStruct</span></span> <span class="Keyword">end</span> <span class="Keyword">class</span> <span class="TypeName">GeoIP</span> <span class="Keyword">class</span> <span class="TypeName"><span class="Variable"><span class="Variable"><<</span> self</span></span> <span class="Variable">DB</span> <span class="Operator">=</span> <span class="LibraryClassType">GeoIPCity</span>::<span class="FunctionName">Database</span>.<span class="FunctionName">new</span>(<span class="String"><span class="String">'</span>GeoLiteCity.dat<span class="String">'</span></span>) <span class="Keyword">def</span> <span class="FunctionName">lookup</span>(<span class="FunctionArgument">ip</span>) <span class="Keyword">if</span> result <span class="Operator">=</span> <span class="Variable">DB</span>.<span class="FunctionName">look_up</span>(ip) location <span class="Operator">=</span> <span class="LibraryClassType">Location</span>.<span class="FunctionName">new</span> <span class="LineComment"> <span class="LineComment">#</span></span> <span class="LineComment"> <span class="LineComment">#</span> {:country_code=>"FR", :country_code3=>"FRA", :country_name=>"France", :latitude=>46.0, :longitude=>2.0}</span> <span class="LineComment"> <span class="LineComment">#</span></span> result.<span class="FunctionName">each</span> <span class="Keyword">do </span>|<span class="Variable">key</span>, <span class="Variable">val</span>| location.<span class="FunctionName">send</span> <span class="String"><span class="String">"</span><span class="String"><span class="String">#{</span>key<span class="String">}</span></span>=<span class="String">"</span></span>, val <span class="Keyword">end</span> <span class="Keyword">end</span> location <span class="Keyword">end</span> <span class="Keyword">end</span> <span class="Keyword">end</span>
Add some Rails caching
Combined with the above code this will give you cached IP lookups:
<span class="Keyword">class</span> <span class="TypeName">GeoIP</span> <span class="Keyword">class</span> <span class="TypeName"><span class="Variable"><span class="Variable"><<</span> self</span></span> <span class="Keyword">def</span> <span class="FunctionName">lookup_with_caching</span>(<span class="FunctionArgument">ip</span>) <span class="LibraryClassType">Rails</span>.<span class="FunctionName">cache</span>.<span class="FunctionName">fetch</span>(ip, <span class="UserDefinedConstant"><span class="UserDefinedConstant">:</span>expires_in</span> => <span class="Number">1</span>.<span class="FunctionName">month</span>) <span class="Keyword">do </span> <span class="FunctionName">lookup_without_caching</span>(ip) <span class="Keyword">end</span> <span class="Keyword">end</span> alias_method_chain <span class="UserDefinedConstant"><span class="UserDefinedConstant">:</span>lookup</span>, <span class="UserDefinedConstant"><span class="UserDefinedConstant">:</span>caching</span> <span class="Keyword">end</span> <span class="Keyword">end</span>
Alternatives
If you’re unable to install the C extension you might want to have a look at the geoip gem, which is a pure Ruby library that can read the MaxMind’s geoip database. It’s slower but easier to install: http://geoip.rubyforge.org/
![](http://img.zemanta.com/pixy.gif?x-id=01b8d41a-fc39-8d6e-aab8-edc0436980d9)