Posts Tagged ‘Rails’

Cookies in iFrames: how bashing my head on the table made them work in Internet Explorer

While working on our TTGPassport our valiant team hit a wall that most programmers hit sooner or later when working with iframes: cookies won’t work with Internet Explorer, and you will lose your session.

The internet is full or remedies for this unnerving problem, most of them revolving on pseudo-magically setting the P3P header. I don’t believe in pseudo-magic, so I kept googling for answers, until I found this informing post.

I diligently ran through the suggestions but we had random session losses, with no reasonable explanation. We were setting our P3P header in a before filter (Rails application), like this:

class ApplicationController < ActionController::Base
  before_filter :set_p3p
 
  def set_p3p
    response.headers["P3P"]='CP="NOI DSP LAW NID"'
  end  
end

Fearing Rails could be the culprit I changed our Apache configuration to set the header on every request, using the following directive:

Header set P3P "CP=\"NOI DSP LAW NID\""

Unfortunately even bypassing Rails didn’t help. I was even unsure of why sometimes it worked and sometimes it didn’t (basically when explorer shows the evil red eye on the bottom of the page it means it’s blocking your cookies).

I started playing around with Firebug to see what could be the problem, and finally a little lightbulb lit on top of my head: the pages that broke the session didn’t have the P3P header, and instead they had an ETag header. That means something was adding the ETag and that the browser recalled the content of the page from its cache, thus bypassing P3P and upsetting explorer. I disabled ETags in Apache:

Header unset ETag
FileETag None

Guess what? It didn’t work. Something was still setting the ETag header and bypassing my beloved and much needed P3P. The only culprit could be Ruby on Rails. I googled some more but nothing really told me how to disable ETags so I had to resort to some monkey patching:

module ActionController
  class Request
    def etag_matches?(etag)
      false
    end
  end
 
  class Response
    def etag?
      true
    end
  end
end

I asked our strong, silent project manager to test it because I was crossing my fingers too hard, and, finally, it worked, no ETags and our P3P header where we expected it.

I hope you are reading this article because you had the same problem we had, and I hope it will help you as it helped us!

The importance of being up-to-date

Since I started working in the web development business the release of Rails 3 has been the first time I really felt I had to understand what was going on because otherwise I would be left behind.

There were simpler times where just reading the feeds of the most important blogs allowed me to be up-to-date, but either I’m getting old or the information has become too fractioned, because this time the only reason for me (and everyone in MIKAMAI) to get started with Rails 3 was to resume a practice that unfortunately we left behind in the past year: the internal presentations.

Starting last thursday, and hopefully never stopping, thursday afternoons aren’t about working for others, but are about everyone sharing his knoweledge with the others.

Last thursday was obviously all about Rails 3, so a couple of us connected their macs to the big screen and demoed new features of Rails 3.

It was nice, interesting questions were asked during the demos, and the overall mood was pretty good. I look forward to the next session.

Two improvements to your Capfiles

I have lately started using a pattern that has become quite common among capistrano users: setting the server names and locations in a task. Doing this allows you to have multiple deployment environments, like development, staging, production, and so on.

desc "deploy to development environment"
task :development do
  set :deploy_to, "/var/apps/#{application}"
 
  role :web, "servername.mikamai.com", :primary => true
  role :app, "servername.mikamai.com", :primary => true
  role :db, "servername.mikamai.com", :primary => true
 
  set :user, "username"
  set :password, "secr3t"
  set :remote_mysqldump, "/usr/bin/mysqldump"
 
  set :db_user, "username"
  set :db_password, "secre7"
  set :db_name, "db_name"
end

This technique has proven itself to be really useful, especially when clients start to ask for deployments on their test servers, and you still want to be able to deploy to your development servers.

While refactoring my Capfiles I also took the time to rewrite the drupal:db namespace, adding the much needed tasks that allow you dump the remote databases and download them to your development box.

  namespace :db do    
    namespace :dump do
      desc "Deletes old database dumps, leaves only the latest on the server"
      task :cleanup, :roles => :db do
        dumps = capture("ls -xt #{shared_path}/dumps").split.reverse
        run "cd #{shared_path}/dumps; rm #{dumps[0..-2].join(" ")}"
      end
 
      desc "Dumps the local database"
      task :local, :roles => :db do
        raise RuntimeError.new("failed dump") unless system "#{local_mysqldump} -u #{local_db_user} --password=#{local_db_password} #{local_db_name} > dump.sql"
      end
 
      namespace :remote do
        desc "Dumps the remote database"
        task :default, :roles => :db do
          filename = "#{Time.now.to_i.to_s}.dump.sql"
          run "cd #{shared_path}/dumps; #{remote_mysqldump} -u #{db_user} --password=#{db_password} #{db_name} > #{filename}"
          run "cd #{shared_path}/dumps; bzip2 #{filename}"
        end          
 
        namespace :download do
          desc "Dumps and downloads the remote database"
          task :default do
            drupal::db::dump::remote::default
            latest
          end
 
          desc "Downloads the latest database dump"
          task :latest, :roles => :db do
            dumps = capture("ls -xt #{shared_path}/dumps").split.reverse
            get("#{shared_path}/dumps/#{dumps.last}", "./#{dumps.last}")
          end
 
        end
 
      end
 
    end

Legacy Path Handler, a Radiant Extension

We’re preparing to deploy the new Mikamai site (not up at the time of this post), that runs on the wonderful Rails-based RadiantCMS.

The VPS we’re deploying to runs on Phusion Passenger, and that means we can’t use mod_alias or mod_rewrite to 301-redirect the old URLs, already indexed by Google, to their new locations.

To solve this problem I wrote a little Radiant Extension, called LegacyPathHandler, that reads a simple list of URLs from a text file and does a 301 redirection on them before handling the control to Radiant’s default SiteController.

It works quite fine for us, but it has no specs/tests or documentation. Please feel free to contribute to the project if you feel you can improve it.