May 28th, 2009

Rails IE Bug with Excel Mime Types

Came across a pretty interesting issue this week while trying to do some bugfixes for a project in IE6 and IE7.

The Rails project I’m working on has one action that accepts an *.xls format to deliver an excel spreadsheet to the user. Getting this to work is very simple, but for the sake of explanation here is the process.

Simply register the mime type in an initializer file

# config/initializers/mime_types.rb
Mime::Type.register "application/vnd.ms-excel", :xls

Then in your controller action’s respond_to block you can simply reference the new format:

# app/controllers/my_controller.rb
def my_action
  ...
  respond_to do |format|
    format.xls { send_file ... }
  end
end

Now, back to the “bug” or issue with IE6 and IE7. Every time a user hit that specific action with a regular request (not requesting the *.xls format), they received both the expected HTML output AND the xls format output.

Naturally, I don’t want to bombard the user with an IE popup dialog asking them to download an Excel file when they aren’t requesting one.

A quick trip to Google turned up this old Rails ticket. The issue appears to be with how IE6 and IE7 set their HTTP_ACCEPT headers:

image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, */*

There are two easy work-around solutions.

First, you can manually check and set the format param for IE requests:

before_filter :hijack_ie_default_format

def hijack_ie_default_format
  if request.user_agent =~ /MSIE/ and params['format'].nil?
    params['format'] = 'html'
  end
end

However, be careful when doing this. If your action can also accept AJAX ( text/javascript ) requests, then this will override that as well and set the format to return as HTML which is obviously not what you want. You would then end up spending a few hours wondering why IE6 and IE7 where returning javascript “Syntax Errors” on your AJAX actions.

What I ended up doing, since I needed that specific action to respond to javascript requests as well, was to simply extract the excel request into its own action. Sure this may be overkill and not very DRY, but it is still a proper solution. I just created a new #excel_export action and updated my excel links to point to the new action.

May 24th, 2009

Using Cerberus to Build Rails

Over the weekend, I released version 0.5 of the Cerberus gem.

The release included some better test support, as well as support for git branches and custom settings file inclusion for the Maven2 builder.

No, sooner had version 0.5 been released, then we began work in earnest on version 0.6

Mike Gunderloy has been working on adding features to Cerberus to support using a custom Ruby script as a project builder. The main reason, is to build the Rails project using their ci_setup.rb, CruiseControl build script.

The current code with Ruby builder support can be found in the ruby_builder branch of the main repository at the moment.

In addition, Mike wrote an excellent blog post on how he is building Rails using Cerberus and multiruby.

May 20th, 2009

Setting Static Asset Expires Headers with nginx and Passenger

On a recent project I launched, I’m using the combination nginx+passenger for my front end my my Ruby on Rails application.

While trying to boost my YSlow score, I was having a bit of difficulty trying to figure out how to set HTTP expires headers on all the static assets served by my app.

Rails will set an internal time stamp on static assets (images, css files, javascript files, etc…) by appending a integer value onto the end of the asset filename

http://mydomain.com/images/asset.png?12345678

My original attempt at getting nginx to set an expires header for these assets had me adding the following location block to my nginx config file:

location ~* \.(ico|css|js|gif|jpe?g|png)\?[0-9]+$ {
    expires max;
    break;
}

The regexp I was using was incorrect however as it didn’t take into account that the ?timestamp might not exist in the URI.

Hongli Lai kindly pointed me in the right direction and showed me the proper regexp that he is using.

location ~* \.(ico|css|js|gif|jpe?g|png)(\?[0-9]+)?$ {
    expires max;
    break;
}

May 19th, 2009

Rails plugin install from specific git branch

Recently while working on a Ruby on Rails plugin, I came across the need to install the plugin from a specific git branch instead of the default master branch.

./script/plugin install -h

yielded the helpful information I needed. Last July, a patch was committed to Rails that added the -r ( or –revision ) parameter.

The -r parameter will take either the name of a git branch or a tag string to checkout instead of the default master branch.

So issuing the following command:

./script/plugin install git://github.com/cpjolicoeur/my_plugin.git -r BRANCH_NAME

will checkout the BRANCH_NAME branch of the git repository.

Very helpful when testing and debugging new features and bugfixes in a plugin.

May 17th, 2009

Getting Capistrano’s web:disable To Work With Nginx

Recently I deployed a new Ruby on Rails application using nginx+passenger instead of the usual apache+passenger configuration.

As a result, I couldn’t use the traditional Apache rewrite rules to display the maintenance.html page if it existed.

Here are the necessary corresponding rewrite rules for nginx to achieve the same effect.

if (-f $document_root/system/maintenance.html) {
    rewrite ^(.*)$ /system/maintenance.html break;
}

Place this inside your server {} block and you should be able to run cap deploy:web:disable and cap deploy:web:enable with no problems.

© 2017 Craig P Jolicoeur.