Archive for the ‘Rails’ Category

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.

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.

More Capistrano 2 goodies: A Radiant recipe library

This is a followup to “A Couple of Capistrano 2 Recipes Libraries”:http://tempe.st/2007/09/a-couple-of-capistrano-2-recipes-libraries

It’s official: I am a Capistranoholist, and I can’t deploy any Rails application without using Capistrano anymore. A few days ago I had to setup a Radiant site for a client and I couldn’t resist writing a small capistrano recipe library (is there an official name for this kind of collections?) with callbacks dedicated to radiant and tasks that help managing radiant installations.

As usual you can get them from the recipes repository .

After you get the recipes load them from Capfile:

load 'deploy' if respond_to?(:namespace) # cap2 differentiator
load 'config/deploy'
load 'lib/recipes/medlar'
load 'lib/recipes/radiant'

Now you will have one more callback and an overridden deploy:cold task:

  after "deploy:migrate", "deploy:radiant:migrate:extensions"
 
  desc "Overridden deploy:cold for Radiant."
  task :cold do
    update
    "radiant:bootstrap"
    start
  end

The overridden task bootstraps radiant during deploy:cold (but assumes you use it only the first time you deploy!), and the callback migrates radiant extensions whenever you migrate your db.

If you don’t need the radiant recipes but you are using the medlar namespace I suggest you update from svn, there have been a lot of fixes to the recipes.

A couple of capistrano 2 recipes libraries

I kept playing with Capistrano 2 after my last article, and I’ve refactored quite a bit my recipes, finally moving them in their own subversion repository. This allows much quicker deployment with my new rails applications. Here’s how I do it:

$ rails my_new_application
$ cd my_new_application
$ capify .

Then I edit Capfile:

load 'deploy' if respond_to?(:namespace) # cap2 differentiator
 
load 'lib/recipes/site5' # This is my site5 recipe
load 'lib/recipes/medlar' # The general use recipes
 
load 'config/deploy'

The site5 and medlar namespaces hold default configuration values, define some callbacks and the following tasks:

cap deploy:medlar:rails:freezer:edge   # Fetch Rails edge and puts it into sh...
cap deploy:medlar:rails:freezer:stable # Fetch Rails stable and puts it into ...
cap deploy:medlar:rails:link           # Links Rails to application/vendor
cap deploy:medlar:rails:update         # Updates the fetched version of rails.
	

cap deploy:site5:kill_dispatch_fcgi # Kills Ruby instances on Site5
cap deploy:site5:link_public_html # Links public_html to current_path/pu...

Last but not least, here’s the simple, clean and elegant deploy.rb:

set :application, "my_new_application"
set :user, "the_username"
 
set :repository,  "repo_address"
set :deploy_to, "/home/#{user}/apps/#{application}"
 
role :app, "server.com"
role :web, "server.com"
role :db,  "server.com", :primary => true

Quite readable, isn’t it? :)

The recipes are available via anonymous subversion: https://svn1.hosted-projects.com/medlar/recipes/

Enjoy and let me know if you found them useful.

Rails and JavaScript: page.call gotchas

Ruby on Rails has wonderful out of the box javascript support, but sometimes implementing dynamic user interfaces is not so easy as it seems.

In an application I’m working on I have a list of people with a checkbox each. In the load event of the page I add a click handler to every checkbox using this javascript code:

  $$('.ConfermaInvitati').each(function(element) {
    Event.observe(element, 'click', clickHandler);
  });
 
// ... 
 
function clickHandler(event) {
  var e = Event.element(event);
  new Ajax.Updater(e.up(), 'my/invited/toggle', {
    parameters: { id: e.up().up().id },
    onLoading: function() { e.src = "/images/admin/spinner.gif"; },
    onSuccess: function() { new Ajax.Request('my/refresh', {}) }
  });  
}

This works fine until I add a new person via an Ajax call. That person won’t have a clickHandler because the element wasn’t on the page when I called the click handler. So I thought it was time to test page.call in the render :update block I had in the rails application.

I tried this code:

render :update do |page|
  # Do stuff that creates the new objects and adds it to the page
  # The data I need is in @invited
  page.call("Event.observe($$('##{@invited.permalink} .ConfermaInvitati').first()), 'click', clickHandler)")

Obviously that didn’t work, and it turned out I have to read documentation before doing fancy things :)

The rails docs told me that I had to use call passing the function name as the first argument and an array of parameters as the second argument, the problem is that call turns all the parameters into strings—this means I could not pass the clickHandler function to Event.observe.

I found the solution in <<. If you do page << "foo", foo will be evaluated as raw javascripts. This meant I was able to do

page << "Event.observe($$('##{@invited.permalink} .ConfermaInvitati').first()), 'click', clickHandler)

and finally have the functionality I was looking for. So remember, don’t page.call if you need to pass javascript variables to your functions.

Sudoless Rails Stack on OSX

More and more of my developer friends are switching to OSX as time goes by, and they keep asking me for directions on the best setup for rails development: how to install ruby, how to install the missing libraries, and so on.

My professional development life started on a PB Titanium running OSX 10.2, and continued through a PB Aluminium and a Macbook Pro. Each time I changed laptop I also reinstalled everything again, and each time I tried to come up with a better setup.

Now I’ve finally found a setup I’m comfortable with, and it has the following advantages:

  • Sudoless: everything runs from my user directory
  • Non-system-tampering: doesn’t touch files in the original osx installation
  • Crash-proof-easy-reinstall: you can just delete everything and reinstall without fear of rendering your system unstable
  • Fink based: uses everything it can use from the fink repositories

Now that I’ve sold you on my setup :) it’s time to explain how to implement it.

Step one: XCode and Developer Tools

If you don’t have XCode already installed you can install it from the disks you got with your Mac or, better, download the newest version from Apple Developer Connection. Once you got it installed you can proceed to the next step.

Step two: Fink

Go to the Fink Download Page, and get the package that works best on your Mac (intel or powerpc). Follow the installation instructions and install the base fink system. After you’ve done that add these lines to your /.profile

# Dev Enviroment
 
LDFLAGS=-L/sw/lib
export LDFLAGS
 
CPPFLAGS='-I/Users/your_username/unix/include -I/sw/include'
export CPPFLAGS

_/Users/your_username/unix/include_ doesn’t exist yet, but we’ll create it when installing ruby.

Step three: Ruby from Sources

This is the first tricky part. Before OSX 10.4.6 the Ruby version shipped by Apple didn’t work with rails, so you were on your own. Now it works, but I prefer to have ruby in my home directory so I can mess with the sources and with the gem files and upgrade painlessly. So download the latest ruby sources and unpack them wherever you wish (I like /src).

Now you’re ready to go. First make sure you have readline and readline5-shlibs installed via fink so you can have a comfortable irb environment:

user$ fink install readline readline5-shlibs

After that it’s time for ruby:

user$ cd <sub>/src/ruby-1.8.6
user$ ./configure <del>-prefix=/Users/your_username/unix
user$ make && make install

The whole magic (and it’s not a big magic btw) is in -prefix. Installing ruby will create the /Users/your_username/unix path. Now it’ time to add the unix dir to $PATH. Edit /profile once again and add these lines:

PATH=/Users/your_username/unix/bin:/usr/local/mysql/bin:/usr/local/sbin:$PATH
export PATH

Step four: MySQL

Quite simple with the packages from mysql.com.

Step five: Last but not least, rubygems

Fetch the gem package from RubyForge and install it:

user$ cd src/unpacked-rubygems-directory
user$ ruby setup.rb

Step six: This is the real last step :) – Rails and a little hack

Now you’re free to install rails (and friends) using

user$ gem install rails --include-dependencies
user$ gem install mysql-ruby
user$ gem install capistrano --include-dependencies
user$ gem install mongrel --include-dependencies

The small hack I was talking about is a symlink:

user$ sudo mv /usr/bin/ruby /usr/bin/ruby-apple
user$ sudo ln -s /Users/your_username/unix/bin/ruby /usr/bin/ruby

This way applications that insist on using /usr/bin/ruby (TextMate’s RubyMate for example) will work fine.

Have fun with your self-made rails stack :)

Capistrano 2 Callbacks

Converting the callbacks in my cap recipes to the new capistrano 2 format wasn’t as easy as I thought it would be. It turned out I had to use fully qualified task names in the after callback instead of the non-namespaced-names.

Here’s a sample of working callbacks:

namespace :deploy do
  after "deploy:setup", "deploy:sposivip:create_galleries", "deploy:sposivip:freeze_rails"
  after "deploy:update", "deploy:site5:link_public_html", "deploy:sposivip:link_rails", "deploy:sposivip:link_galleries"
end

Here I have two callbacks. The first one runs after setup, creating shared paths and freezing rails in the shared directory, so I avoid having a copy on rails in each release.

The second callback runs after deploy:update, so it will be called whenever I do a simple deploy or a cold deploy, it links back various directories in the shared path and it links the shared rails in vendor.

Capistrano 2 on Site5

I finally took the time to browse the capistrano 2 sources, and after reaching enlightenment I was able to write a deploy.rb file (yes I still use capify + deploy.rb instead of Capfile) that works really fine and really sweet on Site5.

Without further ado, I introduce you to deploy.rb extreme Site5 version :)

# Necessary to run on Site5
set :use_sudo, false
set :group_writable, false
 
# Less releases, less space wasted
set :keep_releases, 2
 
# The mandatory stuff
set :application, "YOUR_APP_NAME"
set :user, "SSH_USERNAME"
 
set :repository,  "URL_FOR_YOUR_REPOSITORY"
 
# SCM information
set :scm_username, "SCM_USERNAME"
set :scm_password, Proc.new { CLI.password_prompt "SVN Password: "}
 
# This is related to site5 too.
set :deploy_to, "/home/#{user}/apps/#{application}"
 
role :app, "SERVERNAME"
role :web, "SERVERNAME"
role :db,  "SERVERNAME", :primary => true
 
 
# In the deploy namespace we override some default tasks and we define
# the site5 namespace.
namespace :deploy do
  desc <<-DESC
    Deploys and starts a `cold' application. This is useful if you have not \
    deployed your application before, or if your application is (for some \
    other reason) not currently running. It will deploy the code, run any \
    pending migrations, and then instead of invoking `deploy:restart', it will \
    invoke `deploy:start' to fire up the application servers.
  DESC
  # NOTE: we kill public_html so be sure to have a backup or be ok with this application
  # being the default app for the domain.
  task :cold do
    update
    site5::link_public_html
    site5::kill_dispatch_fcgi
  end
 
  desc <<-DESC
    Site5 version of restart task.
  DESC
  task :restart do
    site5::kill_dispatch_fcgi
  end
 
  namespace :site5 do
    desc <<-DESC
      Links public_html to current_release/public
    DESC
    task :link_public_html do
      run "cd /home/#{user}; rm -rf public_html; ln -s #{current_path}/public ./public_html"
    end
 
    desc <<-DESC
      Kills Ruby instances on Site5
    DESC
    task :kill_dispatch_fcgi do
      run "skill -u #{user} -c ruby"
    end
  end
end

May your deploys be merry and bright and I wish you all your applications be white :)