How to set the Expires header with Apache 2 and mod_expires
First you need to enable the mod_expires module:
1 a2enmod expires
Next add this to your configuration:
1 ExpiresActive On 2 # Set Expires header to current time by default 3 ExpiresDefault A0 4 5 <FilesMatch "\.(flv|ico|pdf|avi|mov|ppt|doc|mp3|wmv|wav)$"> 6 ExpiresDefault "access plus 30 days" 7 </FilesMatch> 8 9 <FilesMatch "\.(jpg|jpeg|png|gif|swf|bmp|)$"> 10 ExpiresDefault "access plus 7 days" 11 </FilesMatch> 12 13 <FilesMatch "\.(txt|xml|js|css)$"> 14 ExpiresDefault "access plus 1 day" 15 </FilesMatch>
Now restart Apache:
1 $ sudo /etc/init.d/apache2 force-reload
Check that the proper headers are set with Firebug, Yahoo YSlow or Google Page speed.
How to support conditional gets in Rails
From the When to Tell Your Kids About Client Caching RailsConf presentation
1 def fresh?(response) 2 def stale?(:etag => @object, :last_modified => updated_at.utc) 3 def not_modified?(modified_at) 4 def etag_matches?(etag)
1 class PeopleController < ApplicationController 2 def show 3 @person = Person.find(params[:id]) 4 response.last_modified = @person.updated_at.utc 5 response.etag = @person 6 return head(:not_modified) if request.fresh?(response) 7 8 respond_to do |wants| 9 #... 10 end 11 end 12 end
1 response.etag = @person # => “5cb44721b6ce18857ff6900486dc4aba” 2 @person.cache_key # => "people/5-20071224150000"
1 class PeopleController < ApplicationController 2 def show 3 @person = Person.find(params[:id]) 4 if stale?(:etag => @person, :last_modified => @person.updated_at.utc) 5 respond_to do |wants| 6 #... 7 end 8 end 9 end 10 end
Geolocation with MaxMind's GeoIP and the geoip-city RubyGem
Install GeoIP library
1 wget http://www.maxmind.com/download/geoip/api/c/GeoIP.tar.gz 2 tar -zxvf GeoIP.tar.gz 3 cd GeoIP 4 ./configure --prefix=/opt/GeoIP 5 make && sudo make install
Install the geoip-city gem
1 gem install geoip_city -- --with-geoip-dir=/opt/GeoIP
Test the bindings
1 curl -O http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz 2 gunzip GeoLiteCity.dat.gz
Fire up IRB and try the following code:
1 require 'rubygems' 2 require 'geoip_city' 3 db = GeoIPCity::Database.new('GeoLiteCity.dat') 4 result = db.look_up('192.143.34.23') 5 p result
Another option is to use hostip.info’s database, as described in this article.
Create a wrapper
1 require 'rubygems' 2 require 'geoip_city' 3 require 'ostruct' 4 5 class Location < OpenStruct 6 end 7 8 class GeoIP 9 class << self 10 DB = GeoIPCity::Database.new('GeoLiteCity.dat') 11 12 def lookup(ip) 13 if result = DB.look_up(ip) 14 location = Location.new 15 16 # 17 # {:country_code=>"FR", :country_code3=>"FRA", :country_name=>"France", :latitude=>46.0, :longitude=>2.0} 18 # 19 result.each do |key, val| 20 location.send "#{key}=", val 21 end 22 end 23 24 location 25 end 26 end 27 end
Add some Rails caching
Combined with the above code this will give you cached IP lookups:
1 class GeoIP 2 3 class << self 4 def lookup_with_caching(ip) 5 Rails.cache.fetch(ip, :expires_in => 1.month) do 6 lookup_without_caching(ip) 7 end 8 end 9 10 alias_method_chain :lookup, :caching 11 end 12 end
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/