Tom Insam

I like writing small self-contained applications, and I like writing then using nice high-level application frameworks like Django and Rails. Alas, I also like being able to run these services for the foreseeable future, and that’s a lot harder than writing them is. Running a single Rails or Django application consumes an appreciable chunk of the memory on my tiny colo, and I currently have about 5 projects I really want running all the time (this could easily grow to 50 if I had a sufficiently good way of hosting them). Ideally, I’d never stop hosting these things. Otherwise what’s the point?

It’s sometimes tempting to just write all my toys in PHP. I’m certain that PHP has the mind-share that it does primarily because it’s so incredibly easy to deploy. Ease of development is utterly trumped by ease of deployment for anything not written for internal use only for a large company. tar is easier to use than mongrel, so there are more deployed PHP apps than Rails apps. But I’m not that desperate. I like my nice frameworks.

I tried Heroku as an external host for my apps for a bit, and it’s great. Very easy to start things, very easy to leave them up, and the free hosting plan is perfectly adequate for your average web application. Alas, there are a couple of raw edges that only really became apparent after using them for a few weeks. Firstly, they want to charge me for using custom domains, and I’m not willing to park my apps on domains that don’t belong to me. Secondly, their service goes through odd periods of 500 errors. This doesn’t bother me - what does bother me is that there is no official reaction to any of the complaints about it on what seems to be the official mailing list. Finally, quite a lot of the things I do need cron scripts, for polling services, etc, and the heroku crons (a) aren’t very reliable that I’ve found, and (b) cost money. So I’m edging away from them recently. Would still recommend them for prototyping, not sure I’d want to host anything Real there just yet.

(An aside - I’m not unwilling to pay any money at all. I will happily pay money for things that matter. But these apps are toys. The average number of users they have is ‘1’. I’m not willing to pay a fiver a month per application to be able to host them on my domain rather than Heroku’s domain. A fiver a month for all of them at once? Sure. But the Heroku payment model assumes that you have a small number of apps that you care about, rather than a large number of apps that you don’t.)

Anyway, my current attempt at solving this problem is Phusion Passenger (via mattb), which does exactly what I want, for Rails apps. It’s an Apache 2 or nginx module, and it’s trivially easy to install, unless you’re using Debian, which I am. Short verison? It was a lot easier to totally ignore the debian packaging system except to install ruby, then build rubygems and everything else I needed from source. Sigh. I understand there are horrible philosophical differences underlying this pain. But it’s still pain.

Once installed, you can just point your domain’s DocumentRoot at a Rails app’s ‘public’ folder, and the Right Thing happens - files in public are served directly, other requests will cause a rails process to be started, and serve your app. Enough idle time, and it’ll shut down again. Magic. My favourite part is that it’ll start up the application server as the user who owns the ‘environment.rb’ file of your application, meaning that your app is running as your user, and can do things like write files into temp folders that don’t need sudo to be able to delete again.

Not all of my projects are Rails apps, though. jerakeen.org is a Django app, for instance (this week, anyway). Unexpectedly, it turns out that Passenger will do the same thing for Django apps, though it’s not as well documented. I have a file called passenger_wsgi.py in the root folder of my Django application folder. It looks something like this (if you use this, you’ll need to change the settings module name):

import sys, os
current_dir = os.path.dirname( os.path.abspath( __file__ ) )
sys.path.append( current_dir )
os.environ['DJANGO_SETTINGS_MODULE'] = 'mydjango.settings'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

And in my Apache config file, I have this:

<VirtualHost *:80>
  ServerName jerakeen.org
  ...
  DocumentRoot /home/tomi/web/jerakeen.org
  PassengerAppRoot /home/tomi/svn/Projects/mydjango

and thus are all my toy projects now brought up and down on demand. I’m happy again. Till next week, probably. ONWARDS.

birthdays


blech oh, today is the actual birthday? that explains all the cake!
blech hyvää syntymäpäivää, as someone said on Facebook
blech I hope they weren't just being rude in Finnish

the future


The i'm in a taxi on a laptop on irc with broadband. and frankly, i don't see why this is excessive in any way.

Wrote this for work, threw it away again in favour of using an actual gem that someone else will maintain,
but I thought I’d put it here anyway, because it might be useful. Also, the gem is written in C and therefore
hard to deploy sometimes.

#!/usr/bin/env ruby
# pure-ruby geohash decoding function

# default is the example from http://en.wikipedia.org/wiki/Geohash
geohash = ARGV[0] || "ezs42"

# convert geohash into a bit sequence
map = "0123456789bcdefghjkmnpqrstuvwxyz" # silly custom base32 mapping
bits = geohash.split("").map{|c|
    i = map.index(c) or raise("bad geohash (#{c} not permitted)")
    sprintf("%05s", i.to_s(2)).gsub(" ","0").split("")
}.flatten

# even bits are longitude, odd bits are latitude.
# probably a better way of doing this part, feels non-ruby-like..
lat_bits = []
lng_bits = []
bits.each_with_index{|b,i|
    if i % 2 == 1
        lat_bits << b
    else
        lng_bits << b
    end
}

# subdivide the world according to the bit sequences
def decode(bits, range)
    range = [ range.to_f * -1, range.to_f ]
    for b in bits
        if b == "1"
            range[0] = (range[0] + range[1])/2
        else
            range[1] = (range[0] + range[1])/2
        end
    end
    return range
end

lat_range = decode( lat_bits, 90 )
lng_range = decode( lng_bits, 180 )
puts "lat is range #{ lat_range.inspect }"
puts "lng is range #{ lng_range.inspect }"

This is a script I use on a few sites to automatically count the number of
delicious links to various urls. There are a few of these that I’ve found, but
I quite like writing JavaScript like this. Also, mine will combine as many
urls as possible into a single request to the delicious API, because it’s
faster that way.

<!-- jquery -->
http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js
<!-- this is md5 implemented in javascript -->
http://pajhome.org.uk/crypt/md5/md5.js


$(function() {

  // This function gets called per link, this depends on the template.
  // In my case, I'm walking up to the surrounding 'post' object, then
  // down to a pre-prepared p tag I want to populate with link data.
  // You will want to change this if you use this code.
  //
  // Markup is approximately:
  //
  //   
// .. //

//
function markup_delicious_link( element, data ) { var html = ""; html += data.total_posts + " delicious link(s)"; html += ""; element.parents(".post").find(".deliciousinfo").html(html); } // Find all links with class 'delicious' var hashes = []; var elements = {} $('a.delicious').each(function(index, link) { if ($(link).attr("href")) { // sanity check var hash = hex_md5( $(link).attr("href") ); elements[ hash ] = $(link); hashes.push( "hash=" + hash ); } }); // Make calls to the delicious feed API to get details while (hashes.length) { // delicious permit a maximum of 15 hashes per request var subsection = hashes.splice(0, 15); var url = "http://badges.del.icio.us/feeds/json/url/data?"; url += subsection.join("&") + "&callback=?"; $.getJSON( url, function(data) { for (var i=0; i

Conversation


Mark It's the '00s now right?
Tom yep.
Tom still.
Mark Does that mean I can telecomute my flipping out?
Tom No, that was early '00s.
Tom now you just outsource it.
Mark No, that was the mid '00s
Mark Now you crowdsource it
Mark you let your users flip out for you