smallroomsoftware.com

mod_rails makes life easier

Posted on April 25, 2008

Every six months there seems to be a new, preferred way to deploy a Rails application. Mongrel+modproxy has worked OK for me for a while, but it's a bit fiddly to set up and quite tricky to make robust across reboots or crashes. Even normal restarts of my mongrel clusters sometimes fails. Now there is modrails which seems to provide most of the advantages of a mongrel cluster with the simplicity of an apache module that spawns rails processes on demand. This is definitely becoming my preferred deployment method. I might even move rentability to mod_rails when I get the chance as it'll simplify our server configuration a lot.

Flowy page layouts

Posted on February 18, 2008

For a long time we've wanted to get better looking property adverts onto Rentability. We thought about offering several layout templates that the owner could choose from. Then we thought about totally "floating" layouts where the user creates several "blocks" of content (text or photos) that are designed to flow down the page and be happy with different page widths and font sizes. The user doesn't have precise control over the placement of blocks. Instead, they have a bunch of blocks, of different dimensions that slot together down the page (from right to left and top to bottom) filling all available space. With fixed element widths (say a third or two thirds of the page) you can get a nice magazine/newspaper style look. Of course, a simple CSS float:left won't work all that well - you get a lot of holes where elements sort of clear:right as they wrap around down the page. So I tried to create this 'flowy' layout with javascript. The javascript measures each block, figures out how it slots into the layout, then positions it absolutely.

In the editing UI I've used scriptaculous to animate the reflowing of reordered blocks. This isn't always entirely intuitive - try the demo below - but then the whole approach is a compromise of sorts.

It's not beyond the realms of possibility that a layout will flow slightly differently on two different browsers. Actually, Sim's advert for the Pigeonnier lays out differently on IE than it does on Safari. Both looks great though! It's interesting how robust this technique can be.

Unless I've shown you a preview, you're probably wondering what the hell I'm going on about. Here's a demo page that you can play with (the changes you make are only visible by you).

Fix for missing authors problem with svnsync and trac

Posted on February 15, 2008

Anyone pointing trac at a subversion repository built with svnsync might run into the same problem as me - trac shows commits but they show with author set to the username passed to the svnsync command and not the real author from the source repository. My best guess is that this happens because trac detects the checkin before synsync has copied over the revision properties (that includes changing the author). So to fix it, just add a post-revprop-change hook script (to the target repository) that tells trac to resync the revision concerned:

#!/bin/sh
REV="$2"
TRAC_ENV="/path/to/trac/environment"

/usr/bin/trac-admin "$TRAC_ENV" resync "$REV"

I also add the call to the contributed trac-post-commit-hook in there too (rather than putting it in post-commit):

/usr/bin/python /path/to/trac-post-commit-hook -p "$TRAC_ENV" -r "$REV"

Update: 19/02/08:

After receiving a lot of duplicate emails from trac I realised that the above approach isn't quite good enough. svnsync updates revision properties more than once per commit (once for each property I guess) so you end up resyncing redundantly and, in my case, calling trac-post-commit-hook several times (resulting in duplicate ticket updates). So I decided not to use the subversion hook scripts and to do what I needed to do to trac directly after running svnsync. Here's the script I run with cron:

#!/usr/bin/env ruby

# This is script is run every 5 minutes. It syncs the svn repository onto the
# trac server. It also manually tells trac to resync each revision and calls
# the trac-post-commit-hook for each revision.

SVN_USER = "rails"
FROM = "svn://www.example.org/project"
TO = "file:///path/to/repos-mirror/"
TRAC_ENV = "/path/to/trac/environment"
POST_COMMIT_HOOK_SCRIPT = "/path/to/trac-post-commit-hook"

sync_output = `svnsync sync --non-interactive --username "#{SVN_USER}" "#{TO}" "#{FROM}"`

unless sync_output.empty?
  revisions = sync_output.scan(/Committed revision (\d+)/).flatten
  puts sync_output

  for rev in revisions
    puts "Telling trac about revision #{rev}..."
    puts `/usr/bin/trac-admin "#{TRAC_ENV}" resync "#{rev}"`
    puts `/usr/bin/python "#{POST_COMMIT_HOOK_SCRIPT}" -p "#{TRAC_ENV}" -r "#{rev}"`
  end
end

Trac 0.11 installation on Centos/Fedora

Posted on February 14, 2008

How come every time I install trac I do it a different way? Mostly, I'm installing on Centos or Fedora these days. The RPMs available rpmforge only go up to trac 0.10. It seems sensible to go with the 0.11 beta for a new installation - the trac admin web UI is included by default, there are fewer dependencies and the default ticket workflow has changed a bit. After some fiddling about with the manual download, realising that trac.cgi is missing (maybe it's deprecated) I realised that installation is fairly straightforward if you get setuptools (some strange python "egg" installer) and use modpython rather than cgi/fcgi (it's more efficient and easier anyway).

So here are my steps so I don't forget them:

# As root
wget http://peak.telecommunity.com/dist/ez_setup.py
python ez_setup.py

easy_install Pygments
easy_install Genshi
easy_install Trac

trac-admin /srv/trac/myproject initenv
chown -R apache:apache /srv/trac/myproject

nano /etc/httpd.d/trac.conf

# And add something like:

<Location "/trac">
  SetHandler mod_python
  PythonInterpreter main_interpreter
  PythonHandler trac.web.modpython_frontend 
  PythonOption TracEnvParentDir /srv/trac
  PythonOption TracUriRoot /trac

  # The following gives basic HTTP authentication
  AuthType Basic
  AuthName "Trac"
  AuthUserFile /srv/users.htpasswd
  Require valid-user

  # ... which should be used with SSL:
  SSLRequireSSL
</Location>

service httpd restart

Brilliant anti-(blog)spam technique

Posted on January 03, 2008

This works brilliantly!

transport.js with prototype 1.6

Posted on January 03, 2008

Recently I've been working on embedded enquiry forms that Rentability users can place on their own website to take enquiries via their Rentability account. We wanted the person enquiring to remain on the owners homepage after submitting their enquiry (rather than be redirected to our site) so an AJAX submission was called for. Of course, when you try to make an ajax request from one domain to another (even if the code that is making the request came from the target domain), you find it doesn't work - most browsers veto the request for security reasons. The workaround is to use SCRIPT tags to emulate AJAX requests. Luckily, Thierry Schellenbach has written a plugin for Prototype to make Prototype AJAX requests use this SCRIPT tag technique.

Unfortunately, there are some major limitations here. For a start, you're obviously limited to using the GET method. In our case this is quite irritating as it's an "update" action that's being called (which is normally only accessible via a POST). Also, the request is going to be made with whatever request headers the browser decides to use. IE is typically braindead in this respect and will probably cause you pain if you have both JS and HTML responses for your action (as we do).

Also, the transport.js only works with prototype 1.5 out of the box. I fiddled for a while trying to fix it for 1.6 but I didn't feel much like wasting hours getting my head around how people hack OO into javascript. That stuff just freaks me out. So I just ended up copying and pasting the AJAX.Request constructor from prototype. Here you go:

    1 Ajax.Request.prototype = Object.extend(Ajax.Request.prototype, {
    2     initialize: function(url, options) {
    3         this.options = {
    4           method:       'post',
    5           asynchronous: true,
    6           contentType:  'application/x-www-form-urlencoded',
    7           encoding:     'UTF-8',
    8           parameters:   '',
    9           evalJSON:     true,
   10           evalJS:       true
   11         };
   12         Object.extend(this.options, options || { });
   13 
   14         this.options.method = this.options.method.toLowerCase();
   15 
   16         if (Object.isString(this.options.parameters))
   17           this.options.parameters = this.options.parameters.toQueryParams();
   18         else if (Object.isHash(this.options.parameters))
   19           this.options.parameters = this.options.parameters.toObject();
   20         
   21         this.transport = (!this.options.crossSite) ? Ajax.getTransport() : new scriptTransport;
   22         this.options.asynchronous = (!this.options.crossSite) ? this.options.asynchronous : false;
   23         //turns of the timed onLoad executer
   24         this.transport.respondToReadyState = this.respondToReadyState.bind(this);
   25         this.request(url);
   26         }
   27 });

Reuse existing partials in ActionMailer HTML emails

Posted on November 18, 2007

This might be useful for anyone wanting to reuse action/controller templates/partials in HTML emails sent with ActionMailer. It causes templates/partials to be resolved relative to the 'views' directory rather than relative to the relevant mailer templates directory (which is the default).

module ActionMailer
  class Base
    # We have to go root relative here because of the change below
    def render_message(method_name, body)
      render :file => "#{mailer_name}/#{method_name}", :body => body
    end

    # This allows root relative partials to be located correctly
    def initialize_template_class(assigns)
      ActionView::Base.new(template_root, assigns, self)
    end
  end
end

SuperDuper and Un-FileVaulting

Posted on September 30, 2007

SuperDuper is missing a key feature - the ability to read and backup the files in the current user's home directory even if the user is using Apple's FileVault feature. It should give two options: Option one should be to mount the spare image on the backup drive and do a smart update to it. Option two should be to write directly to the backup volume and turning off FileVault on the backup volume (presuming you're using FileVault because you carry your machine about with you during the day but you don't carry your backup drive).

SuperDuper can't do either of these things but you can sort of approximate option 2 by backing up in two phases to two different volumes. First backup the entire disk minus your home directory and your sparse image (stored somewhere like /Users/.tom/) then backup your home directory to another disk/partition. If you end up booting from the backup, obviously your home directory is going to be missing (or rather, it's backup up to another volume) so you'll want to create at least one non-FileVaulted account that you can log into and recreate your home directory. Actually, you could even alter the backup system so that it sees the home directory backup as your home directory. Then you don't have to do any copying/fixing to get up and running again (at least until you want to restore your internal drive). You could get SuperDuper to run a script after backup. Something like this:

nicl -raw /Volumes/SystemBackup/var/db/netinfo/local.nidb \
    -create /users/tom home /Volumes/HomeBackup
nicl -raw /Volumes/SystemBackup/var/db/netinfo/local.nidb \
    -delete /users/tom home home_loc

I tried this and it seems to work. The only odd thing that happened was that after rebooting my "HomeBackup" volume was set to "Ignore Permissions" which I had to then switch off in the Get Info window.

Simple java properties style rails localisation with Gibberish

Posted on July 23, 2007

I had a session preparing the Rentability front-end for localization the other day. Luckily I found the Gibberish rails plugin which makes localizing the static text fairly painless - you can store the translated strings in yml files, and the syntax is very simple. I made a slight patch to gibberish so it'll print the key next to each string on the live app so that the human translator can see where each key is used on the running site:

module Gibberish
  module Localize
    alias_method :old_translate, :translate
    def translate(string, key, *args)
      str = old_translate(string, key, *args)
      "#{str} <span class=\"translation-key\">#{key}</span>"
    end
  end
end

You can put that in environment.rb (maybe if RAILS__ENV == 'development'). Here's some CSS:

span.translation-key {
  font-size: 11px;
  font-weight: normal;
  color: black;
  background-color: yellow;
  font-family: geneva;
  border: 1px solid #444;
  padding: 0 1px;
  text-transform: none;
  font-style: normal;
}

Perfect High Street

Posted on June 26, 2007

Here's a nice animation from the idealists (or maybe complete fantasists) at Monocle magazine. I'd be happy if Woolworths came back to Cambridge...

First real enquiry!

Posted on May 28, 2007

Yesterday turned out to be a landmark for Rentability. We processed our first real, genuine enquiry from a real, genuine prospective holiday-maker! To quote Dr. Emmett Brown, "It works! It works! I finally created something that works!".

This actually happened quicker than we'd predicted and it was helped along by the fact that we have a much larger number of properties in Whistler, BC than everywhere else in the world put together.

Talks, mainstream parallel computing

Posted on May 28, 2007

Last week I watched a video on Stanford on iTunes by Dave Patterson about the end of Moore's law for uniprocessor speed increases and the sea change in chip designs that's going to happen as we move towards massively parallel systems for the mainstream market. One possibility seems to be that we'll have computers with, say a hundred simpler smaller CPUs and 1 fast CPU to run some tasks that can't be parallelised (a lot of OS responsibilities I guess). It's definitely worth checking out - it's pretty dense, he covers a lot of ground but he's a great speaker. His book was one of my favorites as an undergrad. By coincidence there was a seminar in the computer lab here on the same subject that i went to on Wednesday.

I'm curious as to when the current SMP approach will move to a new, very parallel design. How many x86 CPUs will be packed into a single machine before the chip manufacturers cave and seriously push a totally new design?

Gizmo gets a bit better

Posted on May 23, 2007

I'm always singing gizmo's praises but that's because I think it's a great bit of software! They released an update a few days ago and I just spotted the best new feature. It's not the bridging calls to MSN, AIM and Yahoo Messenger (which is nice but not a lot of use to me). No, it's that when I change my iChat status, gizmo changes it's status too! Fan-bloody-tastic!

Oh, one thing I couldn't manage to do was to add an ichat user (which AFAIK are really just AIM accounts) to my gizmo buddy list.

Broken MacBook iSight - fixed!

Posted on May 09, 2007

A few months ago the iSight in my MacBook suddenly stopped working. iChat just told me that I didn't have a camera attached and the iSight was no longer listed as being on the USB bus in System Profiler. I'd borrowed my old PowerBook from my sister and was all set to send my MacBook in for repair (along with it's orignal crashed hard disk) when i thought i'd have a go at fixing the problem myself in case it was just a loose connector. I had thought that I'd have to practically dismantle the whole machine to get at the wires but you can at least get to one connector by just unscrewing and lifting back the screen. I found this posting on removing a MacBook iSight and this article from ifixit.com that better explains how to remove the bezel. After I removed the bezel and unscrewed the screen I found the connector and, to my delight, it *was* loose. The USB cable zigzags up the back of the screen and is taped down in several places. It looks as though it is just a few millimeters too short to be taped to the surface 100% flat. I adjusted it a little and connected it properly.

Powered up my machine, and low and behold my iSight is back! This is excellent as it was certainly going to be a little painful to lose this machine for a week.

Monocle magazine

Posted on April 06, 2007

Yesterday I discovered what looks like an excellent new magazine in Borders. It's called Monocle, is on it's second issue (of 10 a year) and covers global affairs, business, culture and design (although apparently shops don't know quite where to put it) and seems to be chock full of interesting articles and very nice photography. A dense collection of random but very interesting articles. I'd be surprised if they can keep it up. It costs £5 an issue and is on sale globally. Looks like quite an ambitious venture by the guy who created Wallpaper. I hope they succeed but I imagine founding a magazine is one of the riskiest ventures you could take on. The website seems to be a big part of their idea - it contains interviews and micro-documentaries.

Hosting by site5.com