Rails output compression

I want to be able to compress my output text that I send to the browser from my Rails app. This will include HTML, javascript, Ajax responses, plain text etc. Lighttpd will perform this function for the static files it serves, but not for the dynamic files, so I went looking for a solution.

There is a README for ActionPack that has this snippet of code:

after_filter { |c| c.response.body = GZip::compress(c.response.body) }

I added this to my ApplicationController, and it doesn’t work.

I can’t find any GZip object in my Ruby or Rails code. I did find some output compression in the Ruby SOAP implementation, so I re-purposed that code for my after_filter. Here’s what I ended up with:

require 'stringio'
require 'zlib'

class ApplicationController < ActionController::Base
  after_filter :compress

  def compress
    if self.request.env['HTTP_ACCEPT_ENCODING'].match(/gzip/)
      if self.response.headers["Content-Transfer-Encoding"] != 'binary'
        begin 
          ostream = StringIO.new
          gz = Zlib::GzipWriter.new(ostream)
          gz.write(self.response.body)
          self.response.body = ostream.string
          self.response.headers['Content-Encoding'] = 'gzip'
        ensure
          gz.close
        end
      end
    end
  end
end
Some notes:
  • The request must be checked to see if the browser supports gzip encoding before sending the compressed data
  • I don’t do the gzip detection entirely to spec, if q=0 in the accept encoding header, GZIP should be disabled.

Since the request accept-encoding header must be checked, and the content-encoding header added, the original code example could never have worked correctly. My version of the code should work in most cases.

Update I added a check for the Content-Transfer-Encoding header. If this is ‘binary’, then don’t compress, as the response is an image, or something we don’t want to compress. Rails leaves this header off for text responses, like HTML.

Posted by Tom Wed, 07 Dec 2005 20:04:00 GMT


Make your DIVs Resizeable

<!--adsense-->

I love using Scriptaculous for its effects and drag/drop. However, I need to have my DIVs be resizable too. To make this happen, I’ve written a class called Resizeable that can be added to a DIV in the same way that Draggable can be.

This code is standalone (it needs Prototype, but not Scriptaculous), and it can be used with Draggable with one note: The Draggable handle cannot be the element that is Resizeable, you must specify a handle element when you create a Draggable to avoid confusion between Draggable and Resizeable.

This doesn’t work very well
  <div id='foo'>
    <div id='bar'>...</div>
    ...
  </div>

  new Draggable('foo');
  new Resizeable('foo');
This works nicely:
  <div id='foo'>
    <div id='bar'>...</div>
    ...
  </div>

  new Draggable('foo', {handle: 'bar'});
  new Resizeable('foo');

There aren’t many options for this object, but here they are:

The grab areas can be defined with top, left, bottom, right. Each defaults to 6 pixels. If you set this to 0 (zero), then resize in that direction will be disabled.

  <div id='foo'>
    <div id='bar'>...</div>
    ...
  </div>

  new Resizeable('foo', {top: 0, left:50} );

A callback function can be defined that will be called when the resize is over.

  <div id='foo'>
    <div id='bar'>...</div>
    ...
  </div>

  new Resizeable('foo', {resize: function(el) {
      alert('Done!');
    }
  } );

The minimum height and width of the DIV can be specified as minHeight and minWidth options.

The resizable code in action – view source to see how it works.

The Javascript source file can be downloaded here

Currently, this code doesn’t quite work in IE (the DIV can jiggle around a little), but Firefox 1.0 is working Ok.

Posted by Tom Fri, 02 Dec 2005 06:50:00 GMT


Weirdness in Napa

Last Saturday, we spent about 5 hours in Napa on a trip organized by the Seahawks. We visited two wineries – Opus One and Neibaum Coppola. We had a great time at both. Opus One took us on a tour of the facility and we had a tasting in a dining room that opens onto their impressive barrel room.

At Neibaum Coppola, they took us to a private area and we tasted wine and ate some great cheese.

Here’s where the weirdness begins. After our cheese and wine, a bunch of helicopters started buzzing around. It was quite annoying. It turns out that Christina Aguilera was getting married on a neighboring property.

The other weird thing. Whilst driving there and back, we noticed a house just off the road on a hill. It turns out it was the Napa house from this CNN article

Coincidence? I don’t think so…

Posted by Tom Sun, 27 Nov 2005 05:57:00 GMT


Seahawks sunburnt in San Francisco?

The Seahawks took us to our annual away game, this year in San Francisco. This year was the best we’ve been to so far. Once again, the Seahawks manage to get me sunburnt in November. The temperature in SF was over 70 on Sunday, and we had seats in the sun. We did manage to find a tube of SPF 8 sunscreen, and that did a good enough job to stop a full burn.

The heat may have got to the Seahawks too, as they wilted a bit in the second half.

The SF crowd were completely out of it in the first half. The player introductions were the quietest I’ve ever heard.

The 49ers had an ace up their sleeve though – at half time, bring out Steve Young and present him with his Hall of Fame ring. Now the crowd comes alive.

The final 2 point conversion attempt was right in front of us, but we couldn’t see what happened as all the photographers were in the way. It took us a while to realize they didn’t make it!

Before the game, the Seahawks got us into an invite only tailgate party that had some 49er alumni. We had at least 3 hall of famers in their cream/yellow jackets. They were doing the autograph thing, talking to fans. Very cool. Lisa recognized Y A Tittle as he was introduced on stage at half time. If only I’d known who these guys were. It seems rude to get autographs or pictures from people whose name you may not even know.

The Seahawks always treat us really well. We had a great time.

Posted by Tom Tue, 22 Nov 2005 03:48:00 GMT


Fun with Rails on the edge and RJS

There’s a new template system being built into Rails right now that helps generate Javascript responses to browser Ajax calls. The template extension is RJS.

Since I wrote a bunch of code to do something like this only yesterday, today, I sync’d up to the latest Rails to give it a go.

Well, it works! Reading the RDoc comments in the source code, and with a little experimentation, I got the thing to work, converting my rhtml file into the rjs equivalent.

Almost.

I found a bug in Prototype. Prototype will detect a Javascript return and run it automatically, without you having to setup a special handler. This code detects the Content-type HTTP header and looks for text/javascript and then runs the contents of the response.

The problem is, it checks for exactly ‘text/javascript’, but WEBrick on my Windows machine returns ‘text/javascript; charset=UTF-8’, so the auto eval() doesn’t fire.

Looking at the development log file, it looks like this method also runs faster than generating almost the same javascript in an rhtml file. Instead of seeing 30 requests/sec in the log, I’m seeing 60 requests/sec. I’m figuring this is due to the template file being pre-compiled and cached.

This type of functionality is why I love this Rails thing, always something new, and it’s always relevant to what I’m building.

Posted by Tom Sat, 19 Nov 2005 04:12:00 GMT


Didn't see Harry Potter today

Lisa spent 2 hours at Les Schwab getting the snow tires put on the car yesterday. It seems that low profile tires shouldn’t be swapped off the wheels very often, as our snow tires were damaged the first time they were taken off the car.

On the way to see Harry Potter this afternoon, a strange noise started, on inspection on the side of the road, the driver’s side rear tire was flat (and hot)

We drove the mile to Les Schwab to switch back to the summer tires. Two new rear snow tires, and a set of wheels to match, have been ordered and will be available on Tuesday.

I learnt to not try to be cheap and use only one set of wheels for two sets of tires.

Posted by Tom Sat, 19 Nov 2005 04:05:00 GMT


Browsers and % body height

If you set your page’s body height to 100% and your window is 900 pixels high, you would expect your body to be 900 pixels tall. This seems to be supported in both IE and Firefox 1.0.7.

What about 200%? IE gets this right, Firefox says 3600! WTF.

Here’s a set of readings of the body.offsetHeight property:

% Firefox IE
100% 900 900
110% 1089 990
150% 2025 1350
200% 3600 1800
300% 8100 2700

Not what I was expecting.

Posted by Tom Thu, 17 Nov 2005 18:41:00 GMT


Upgrading Rails Sucks!

Rails on my blog’s host server was upgraded to the latest RC early this morning. My Typo 2.5.5 got very upset with this, and my blog has been down all day.

Upgrading my Typo was fairly easy. Sync up the code on my dev machine, use WinSCP to sync the directories up (fix up the .htaccess file, because I failed to merge the conflicts before I started!) I had to restore my /public/files directory as the WinSCP sync deleted it.

When you run the Typo Admin code after this, it detects that a DB migration is needed and does the work for you. Sweet!

Posted by Tom Wed, 16 Nov 2005 07:09:00 GMT


Typo and code formatting in IE

Lisa noticed that my blog sidebar was all screwed up. The problem is that my blog has code snippets using the typocode filter for Typo. The CSS to layout the code works great in Firefox, but doesn’t in IE.

I changed my azure theme CSS definition for the .typocode selector to have a width – 500 pixels works for this theme – and an overflow setting of auto to cause the scrollbars to show up. The existing CSS has a selector for .typocode pre that has overflow: auto, but this didn’t work for IE. Moving this up to the .typocode selector and adding the width is the key.

Posted by Tom Sun, 13 Nov 2005 01:06:07 GMT


Rails action caching gotcha

Rails provides a simple mechanism to allow the output from an action to be cached. Unlike the page caching, this allows all the filters to be executed, then the cached results returned. The filters executing is important for authentication.

This is great until you have an action that returns something with send_data (send_file probably has the same issue). Caching just doesn’t work in this case, and sends the wrong data to the client when the cache is hit.

Don’t use Action caching on actions that return binary data.

Update: I’ve written some code that fixes this problem, and documented it here

The main issue is that the custom headers set by my action that returned the binary data were being dropped by the Action Cache. My Action Cache Upgrade fixes that issue.

Posted by Tom Fri, 11 Nov 2005 01:47:00 GMT


Older posts: 1 ... 13 14 15 16 17 18