Money Matters

Today's iteration in the series of horror stories is going to be about the transaction management system. Combined with the transaction management system is the pricing system, so I'm going to present these two systems today in a double-whammy of fun.

MySQL Sucks. MySQL, with the MyISAM storage engine does Table Locking On Writes, and has no transactions. This system dates back to the early 3.x days of MySQL where all these new features like transactions and subqueries were nothing but "unnecessary features that databases don't really need" (paraphrasing a '02 era MySQL manual there).

Our transaction management system was the system that represented the fact that a customer had committed to paying for a service rendered. The services were often delivered in real time (we resold data via the web), and a bill would be sent out at the end of the month. In order to record a transaction, a single record was written to the 'transactions'[1] table in a database on the webserver. Each record in this table recorded Who used What and How much they paid for it. The table contained about 7 more columns than it needed to in order to figure out exactly What the transaction was linked to.

I don't recall precisely, but somewhere between $100 and $1000 worth of (gross, not net) transactions were inserted into this table every minute, and at any point it contained 60 days worth of data (the rest was archived off to 'transactions_archive'[2]).

Gentle Reader. If you don't think the above is insane, I don't expect you're the audience for my blogging at the moment, regardless, I think it was nucking futs. We were trusting our 10's of thousands of dollars per hour business to a single MySQL table running on a production web server (now you understand why space on said server was at a premium).

Sure, it was replicated, but MySQL replication breaking meant you had to take down the entire website for 6 hours on a weekend to copy all the data across again so you could restart the replication. When replication broke, that meant that you were running with all-your-eggs-in-one-basket for every hour between when it stopped working and when you could get it running again.

Okay, that's the transaction management system, now lets do pricing.

Each product had a hard cost. This was the cost to us (the data supplier) to get that data. I shall not going into details here, but the data is things like government files on people or companies, or state government data on land titles. This number was in a database.

Each client had what was known as a delivery fee. This is a fee that we charge over and above the hard cost in order to 'deliver' the data to the client.

For example. A Property title might cost $20. Your delivery fee might be $2. You would have to click 'okay, charge me please' after being asked to pay $22.

If the data cost $0.50 or $0, you'd pay $2.50 or $2 respectively. If the data cost $190, you'd pay $192.

The scary thing is, because of the way the technical systems were set up, operations and sales staff simply could not comprehend a pricing schedule that wasn't based on delivery fees, and would not even entertain the concept of perhaps, charging what the service was worth to the customer.

[1] I could not make that name up. Seriously.
[2] Nor this.


Stylistic Perfection

We had a strict style-guide. In the general case, this was a Good Thing. If you have a style-guide you can open a file anywhere in a system and you have a familiar environment, variables are named the right way,

But we also had a strict style that meant a 'normal' PHP source file looked like this (this is an 'include' file, not a PHP script that is accessable via a url).



function MainTable()
   global $ColC;

   echo "<center>";
   echo "<table>";
   echo "<tr>";
   echo "This is the contents of the table";
   echo "</tr>";
   echo "</table>";
   echo "</center>";

// This line must be the last line in the file with no following whitespace

Well, yes. That's the code. God. I wish that was a joke. Let me walk through a few of the 'interesting' bits here...

<?php was the start of every file. No file used PHP interpolated with HTML code. Ever.

Yes, all HTML was printed using 'echo'. All of it.

Now, $ColC is the doozy. I couldn't make this up. In a database there was a table that included every CSS class we had created, the idea was that you could through a web based form maintain the CSS stylesheets. Every CSS class had styles for things like Table Cells, Headers and Paragraphs. In order to make this easier to use, each CSS class was inserted into the global namespace as a PHP class instance.

'$ColC' was a name that came out of a database and was dynamically assigned to a global variable in order to make things easier.

I had a vim macro that would produce the 25ish style classes that I would put at the top of every function, it saved programmer time in the long run not having to worry about changing 'global $ShadeL;' to 'global $ShadeR' to the top of a function when I wanted to have a shaded table cell that was Right aligned as well as the Left aligned one.

When we had a new employee start in 2003 who had actually used PHP before, he asked, "Why don't we use a templating engine" and was given one of those responses along the lines of, "Be quiet, don't ask questions like that, you don't understand how we do things around here."

He whipped up a system that used a templating engine anyway. He didn't believe that that kind of response really mattered in the long run. Templating systems are just how you write code so that it's not entirely unmaintainable. But no, he was told to rewrite the system to follow the coding standards.

Going back to the example cited above, The last two lines are verbatim what you would expect to see in the codebase. There was a really good reason for this. PHP, if you included a file that output any data, such as a blank line, it would start output to the browser. Once output to the browser was started, then methods like 'header()' which set apache headers don't work anymore, they print an error to the screen (yes, readable by the user of the web application, it was configured so that the error was visible to the user).

If you had a PHP file that ended in "?>\n\n" instead of"?>\n", it would screw up any code that set HTTP headers.

It was 2004 before the innovation of omitting the optional ?> tag was introduced into the codebase. When the unnecesery closing tag was removed the issue was mooted.

So to conclude. You could be satisfied that if you opened any file in the codebase, you were guaranteed to be shown a consistent style, methods named in ways you recognise, HTML generated in the same manner everywhere, and you knew that if anyone deviated from this style, then management would pull them up on it.

And that you'd never be faced with anything newfangled like a template.


The new critically acclaimed series

Yikes. I'm writing this series on my experiences in hell for myself. It's part of the healing process that's required after living through terrible experiences.

But yow. The response I've received from my readership is incredible:
I want to read more of that.
I figure it's okay to derive satisfaction from your past suffering.
Your blog post is terrifying
Dear sweet lord, i would have killed myself
Oh boy i can tell you've got some doozy entries coming along.
I'm keen to see it, so hurry up with the therapy!
... You are getting therapy, aren't you?

I guess I should start making a few notes and improve my quality of work. My first post was just a ramble about the generalities. There really are some 'doozy' entries in the pipeline.


First of the Horror Stories

Okay. It's time. I've decided to talk about it.

It's been a long time coming, and some friends of mine have been waiting with bated breath. I'm going to talk about my time at my previous job. I should make it clear to my readers that this is a an accurate depiction of what I was doing. There's no hyperbole, and while I am not going to anonymise anything, I'm also not going to be naming any names that I don't have to.

I really hate PHP. I don't really hate PHP because I read a few lines once and I didn't like the punctuation marks used by the language, I really hate PHP for some really good reasons. The language stinks and I used it for a number of years. Since I started my job in mid 2001 as a casual programmer to when I finished up in Janurary 2005. That's 4.5 years.

I'm going to be writing a series of articles on my time at that job, so I'm going to start with the basics: What was I doing? I was working for a credit reporting agency as a programmer working on their internal and customer-facing web systems. It wouldn't be accurate to call that an 'Intranet' and an 'Extranet' because they were all pointed to the same single webserver.

As a developer, I had a login on that linux webserver, and a series of linux MySQL database servers. There was an Oracle box running SCO unix. I worked on source code using ''vi''. All my coworkers at the time of my employment in '01 were using ''vi'' for all their development because it provided file locking. We all crawled around the same sourcetree simultaneously, and we knew not to edit a file because ''vi'' would warn us.

Of course, no one was using vi, we were all using vim and had been since forever. Redhat ships with a minimal, cut down, version of vim without syntax highlighting or anything useful for software develompent enabled. It was a month or two before I broke down and compiled my own version in my home directory so I could access modern innovations like colour in my editor.

Now, you might be curious as to what we used for source control, or horrified that we would edit code on a live webserver. No, sir reader, we didn't edit code live on the webserver, that would be silly. Each module had a path like /src/modulename/live and a /src/modulename/test and a /src/modulename/dev. Allowing us to edit out code in 'dev', test it, and then copy it to the live tree.

And now, my reader, here is the kicker. CVS was not used. Nor was RCS. No, no source control at all was used. If you needed to revert a file because you fatfingered an rm, you had to go to the IT manager, who was also the sole sysadmin, and ask him to restore it from tape.

I asked repeatedly for ''permission'' to use CVS, and that was repeatedly denied, because the benefit(!?) couldn't be seen, and it was too hard(!?!).

By mid 2004, we had 15 developers, 1 million lines of code in the dev trees, and 800 thousand lines of code in production. Where source control and change management amounted to "Writing your name and a comment about what you did".

I had a few safeguards in place - that is to say, whenever I moved a sourcefile to the live system from the dev system I had a system for doing diffs of the files, unifying them, and when I finally did the copy, I kept a verbatim copy of the live and dev versions, in order to allow me to revert changes. This was of course frowned upon because it meant I had hundreds of megs of data in my home directory and space on the webserver was at a premium.

Blarg. That's the intro.


I feel my ears burning

It seems there have been a fair number of submissions to LCA about the kind of cat that my submission was about. I have this feeling that my submission won't be accepted.

Not that I mind. I'm not going to LCA to hear myself talk about stuff I know about - I'm going to hear other people talk about stuff I don't know about yet.


LCA07 talk on UQDS Abstract

(What follows is my Abstract I'm submitting to LCA07 on UQDS)

Contributing to open source projects is hard. If you've found a bug, you have to go to the effort of fixing it, testing that your fix works, making a diff against the upstream repository, and submitting your patch. Once you submit your patch, you don't know if it's going to even be accepted.

In the Twisted project we now enforce strict rules, such as usage of a task tracker, branch based development using subversion, mandatory unit tests and absolutely no commits to trunk without an independent code review. This process was pioneered at Divmod.org, and brought into the Twisted project later.

Branch based development allows us to remain agile, committers can work on what they want to without fear of it clashing with anyone else's code. Once they're finished, and an independent review has passed the code, the code then may be committed to trunk. Casual committers are treated just the same as everyone else. A casual contributor may submit a patch, and get it accepted or rejected based on the same strict rules that apply to the project leader and the top committers.

Initially, we feared that enforcing these strict rules for an Open Source project would drive casual contributors away. Our finding is that our changes resulted in an engaging experience for contributors and core developers. The community as a whole was motivated to produce code of a high standard. Contributors stay around longer, and care about the resulting code far more than we have previously seen.

Contributing to an Open Source project is hard and requires a lot of motivation. By putting a strict development cycle in place, the twisted project has found that the quality has improved and, contrary to common wisdom, the rate at which contributions occur has increased.

The Twisted project lives at http://twistedmatrix.com and #twisted on irc.freenode.net

Tools we use are:
  • http://svn.tigris.org/ Subversion for source control
  • http://www.divmod.org/trac/wiki/DivmodCombinator Combinator is a tool we use for managing our branch based development
  • http://buildbot.sf.net/ BuildBot for automated builds
  • http://trac.edgewall.org/ Trac for bug tracking

More information about the Ultimate Quality Development System can be found at
  • http://divmod.org/trac/wiki/UltimateQualityDevelopmentSystem
  • http://twistedmatrix.com/trac/wiki/ReviewProcess


Ultimate Quality? What's That?

I hack on the Twisted project. I have for years now. I only just found out I have a jerub@ email address there. I've had that for years too. I've been a lot more active lately than previously. I've got pypy running the unit tests, and I've been doing the odd code review.

Twisted has a development structure that I'm growing quite fond of. The idea is to produce code that we can call the 'Ultimate Quality Development System.'. Sometimes it feels like everyone is going on a health kick, all at once. Everyone's going to the gym, working out, doing all the right things, and when someone does something wrong (eats a hamburger) they get pounced on.

What does UQDS mean? It means that we have a software process. Not just a bunch of people who commit to our SVN repo. How does it it work? Well the process looks like this:

  1. Ticket is raised in the tracker.
  2. One or more people work on this ticket. They will accomplish the task set out in the ticket (be it Task, Enhancement or Defect)
  3. A person other than one of the developers from the previous stage reviews the changes. He may reject the changes or accept them.
  4. Then, and only then, do the changes get committed to trunk

What this means in real terms, is that folks say, "Why do I need to go through this process, it's an obvious fix for an obvious bug, a one liner."

An example that comes to mind that was a real situation that happened recently. A bug was reported, the fix was given, after referral to the bag tracker and the development process a test was also given. The ticket reached review, and I reviewed the ticket.

After reviewing the ticket, I made a branch (combinator is great for python/svn work), applied the patch with the test in it, fiddled around so the test was in a more appropriate file, changed the test so it tested the abstract base class (because that was where the code being tested was defined), wrote two more tests and found another bug.

That's why UQDS is awesome. Trunk isn't allowed to be changed without it being reviewed, and reviews find bugs. It was just a 1 line obvious change, and there was nothing wrong with the change, but by putting it through the formal process someone eyeballed the code a bit deeper than it had been eyeballed previous and another bug was found.

The idea is to never let code into trunk that hasn't been reviewed and passed on. Even trivial docbugs get reviewed, and even those trivial docbugs reviews find more things that need to be fixed, and the review will either issue another ticket, or block the merge until the problems are fixed.

This process exists in other places, but I've never seen it taken so serious as in twisted. Nor so successfully.


Humbug AGM

Yay! The AGM is still going, but we've had some interesting content so far.

Mark was unopposed as president. Clinton was opposed successfully for Vice President by James Isseppi, Secretary by David Seikel, Treasurer by Rohan Smith and Librarian by Scott Wilson.

There was a big ado about nominating officers who can pack up meetings and stuff. Now the exec will nominate a group of people who will be able to setup/packup.

Apparently not enough attending humbug members are blogging. My name was taken in vain. Apparently we have too many bloggers on planet humbug are not currently attending members, and I'm one of the few attending financial members who is actually blogging. I've not blogged in ages so I found that a little surprising that my name was mentioned... That's why I'm sitting here on Elspeth's iBook writing this during the meeting. :)

Software freedom day organisation is in trouble. It's not really going ahead properly - I think it's more or less entirely stalled. We have stuff to hand out (hundreds of live-cds) but not really anywhere to hand it out. Hmmm. Any ideas? Elspeth is now the events coordinator, so she's going to have to think seriously about this.

I should really sit down and blog about my work with running twisted's unit tests on top of the pypy python interpreter.

Well, congratulations everyone who defeated Clinton in this election, Clinton, better luck next year. Clinton for President!


Keep your bits.

BitKeeper Author Prohibits Paying Customer From Creating Competing Product.

As I understand it, according to the BitKeeper license, you're not even allowed to think about writing a Source Control system while you're a paying customer of BitKeeper. This was the primary reason that the use of BitKeeper for the Linux Kernel was a pain - folks working on subversion and friends weren't allowed to use it.

Shit like this is why I advocate Free Software and Free Culture.

In a related story, I was there when at LinuxConfAu2005, Tridge took an opportunity during a keynote to 500ish hackers to say, "I'm not a genius reverse engineer, I didn't use magic powers to write SourcePuller." And told the audience the host and port of a BK repo, and asked how he'd go about reverse engineering it. It was very well staged. The responses, 'telnet hostname portnum', 'help' and 'clone' (clone was in the help text provided by the BK serevr) were all that was required to make a very impressive quantitiy of code start streaming from the BK server over the telnet session.

It evoked quite the, "well now isn't Larry a silly boy" response from the crowd.

'rati tags:


I Own My Music

American Students Reject Free Music Downloads

As a student at Cornell University, Angelo Petrigh had access to free online music via a legal music-downloading service his school provided. Yet the 21-year-old still turned to illegal file-sharing programs.

The reason: While Cornell's online music program, through Napster, gave him and other students free, legal downloads, the email introducing the service explained that students could keep their songs only until they graduated. "After I read that, I decided I didn't want to even try it," says Mr. Petrigh, who will be a senior in the fall at the Ithaca, N.Y., school.


It is exactly the sentiment of not wanting to Rent music, that is behind the I Own My Music website run by Linux Australia as part of an awareness campaign about the DMCA legislation that will be brought into Australia in Janurary '07.

Thanks to an excellent talk given by Rusty Russel at Humbug I now know a bit more about the issues in Australia, but have yet to write a letter and send it via Snail Mail to my politicians. I should do that. I think everyone who cares about the matter should too.

The legislation that is coming into Australia will provide Music companies with the power to say where and when we're allowed to play their content. To DRM it to heck, and make it illegal to own or distribute the tools required to ignore the DRM protections.

If the current laws go through, any '''protected''' works may not have their '''protection''' circumvented. Even if that '''circumvention''' is just playing a DVD under linux, cutting up a commercial DVD to use in a presentation in a school or duplicating some music so that it can be played on your MP3 player.

I don't want music they can only play in a archaic CD player, I don't want music I can only play for a restricted time period. I don't want to pay good money to 'rent' something I feel I should be allowed to own and do with As I Please.


PyPy Musings

I've been fiddling with PyPy this week. I thought I would share a few things I know, and relate my experience with getting the Twisted unit tests running under PyPy. I've only gotten the twisted.trial the unit testing framework to run its own tests so far.

Many people seem to misunderstand the PyPy project. So I'm going to explain it in very simple terms for those who have not heard of it, or have only heard a brief blurb.

PyPy is a project that aims to completely implement Python, using Python. Much like GCC is written in the C programming language and FreePascal is written in the Pascal programming language.

The structure of PyPy is interesting once you delve into it. It has that ability to run 'untranslated'. That is, PyPy's interpreter running under CPython[1]. This is *slow*. I used this technique to run 250 tests on twisted.trial and it took nearly 11 hours of CPU time. This is for a battery of tests that runs in CPython in 25 seconds.

PyPy can also be translated to a number of backends. Backends for C, llvm, Common Lisp and JavaScript all exist. This is exciting because it means that instead of running at a speed that a snail would be embarressed to be compared to, it runs python at approximately 3-10x slower than CPython. Benchmarks of various builds here.

Translating to a backend requires that platform calls be implemented correctly. This can be implemented by saying (for instance) os.stat should proxy the call through to the CPython implementation in untranslated PyPy, should call this C code when translated to C, should call llvm code when translated to llvm, and should simply not exist when translated to JavaScript.

This is a bit nasty, because you've got to implement things a few times over, so there's a new way based on ctypes that's completely awesome.

When using ctypes, you can implement a python version of the call to the system library, and when translating to C, the calls to ctypes will be translated to C as well. Meaning that it's possible to have a single implementation of a function that works in untranslated and translated PyPy, without writing a line of actual C code.

An example of some actual code I had to write in order to get the twisted tests to run.
dllname = util.find_library('c')
libc = cdll.LoadLibrary(util.find_library('c'))
def access(space, path, mode=R_OK):
  if libc.access(path, mode):
    return space.wrap(0)
    return space.wrap(1)
access.unwrap_spec = [ObjSpace, str, int]

There's some funky stuff there for wrapping/unwrapping return calls to the Application Level as this is Interpreter Level code - you're not allowed to give the application level any of the underlying interpreter level objects, but the really interesting bit is libc.access(path, mode) which will result in a call to access(2).

And the best thing is, this code will run untranslated or translated. I hope to iron out a few of the bugs I have in modules like fcntl (thanks Lawrence Oluyede for implementing this in ctypes!) that stop them from being translated, then I'll be able to run a translation and run the tests at a reasonable speed. :)

[1] CPython is the name used to unambiguously refer to Python implemented in C, and to distinguish it from Jython, PyPy, IronPython or Stackless Python. /usr/bin/python is CPython.

'rati tags: ,


Pain in the Wrist


I sometimes have really bad RSI. I say RSI, but I really mean, "my right wrist is giving me a bit of pain". And I say, "really bad" as in, it's only giving me a little bit of pain.

I have friends who have gone through a similar experience, but sometimes I discover that one of my friends has not gone through what I call bad RSI, they've gone through flaming both wrists hurt like hell all day every day.

I never let myself get that bad, but it did get to the point that I saw a doctor, took a week off work, took iboprofen, and decided never to seriously use a computer again unless I had gel wrist rests.

That's why I define 'bad' as being in a little bit of pain.

I have a friend in mind who let his RSI get so bad that he quit his job, and found other employment because he was in so much pain... I was discussing wrist pain with a coworker today, and she was saying she couldn't believe how bad it got before she took 'measures', and how it starts to hurt if she even uses her computer at home, that doesn't have wrist rests.

As such, I thought I'd just list here a few of the things that I find really helpful.

  • Gel Wrist Rests. Both mouse and keyboard. Mouse is more important for me, but I need both. I use the coloured gel ones made by 'Fellowes'.
  • Workrave. When things get bad, I turn on workrave, and it reminds me to stretch every couple of consequtive minutes of work.
  • Keyboard shortcuts. Make them if there aren't any good ones in your environment.
  • If you're like me and the mouse really hurts your wrist, move to a window manager like Ion.

technorati tags:,


If Half The Things I've Heard Are True

So I've been reading around the place various comments about the recent release of a new public beta for Vista. This has had an interesting effect. Because it's a public beta, the 'taboo' of talking about running a pirate copy of the beta has been nulled, and people have started talking about the operating system publically.

It seems everyone is trying to either a) sell vista to unbelievers, or b) explain why vista is so terrible.

Unfortunately, I end up talking to a lot of (a)'s. But I read many blogs, and there's quite a few (b)'s around. I'm one of those odd people that neither likes nor dislikes windows anymore. I'm a programmer, I write code for linux, and at home I run a mac that I didn't pay for. I didn't buy into any paradigm but the one that comes for free on the cover of magazines.

But the thing that I find interesting is that as a GNU/Linux Open Source Free Software Penguin Loving Fanatic, I'm excited about Windows Vista. I'm excited that as someone who has had to deal with, on a network level, the brokenness in Windows pre-Vista, that there might be improvement.

I'm excited that somehow, the number of viruses in the wild might drop a little.

I'm excited that trivial things like burning CDs, sharing music, finding files, talking to people, developing software and being Good People might just get slightly easier.

An aquaintance of mine, who is a developer for Microsoft, gave me a cute little snippet of information that only someone who codes would appreciate:

Not much we do nowadays is really closed. We don't obfuscate any of our IL, so it's pretty trivial to pop it into .NET reflector and look at all the source.

I don't know how that works with licensing. Does that count as reverse engineering? What does it mean that in a year or two people will have a project that's parallel to SIMBL?

Sometimes folks forget. Freedom is an attitude, not an abstract concept. Linux is free because of the freedom to open up the code, see what makes it tick incorrectly, machine a new cog, replace it, and show everyone else how to do it.

As a GNU/Linux Open Source Free Software Penguin Loving Fanatic, nothing thrills me more than Vista coming out. I think it may be one step closer to Folks being Free.

I wrote this little rant in a bit of a hurry, feel Free tell me why I'm wrong.


You'd Think It Was A Solved Problem

I got ADSL. In Australia. Therefore I had an interesting adventure, and I have decided to share it. A person with knowledge of Telstra's misadventures has estimated that 10% of ADSL provisining do not work when initially dealt with. Here is my story about being a statistic.

Monday, 29th of May, 9pm, I used an Online Form to request ADSL from Internode.

Wednesday, 31st of May, Morning, Telstra Wholesale provision my phoneline.

Wednesday, 31st of May, Evening, ADSL modem plugged into phoneline. Line Sync established. Authentication does not function.

Thursday, 1nd of June, start of business. Internode technical support contacted by Elspeth. Calls from Internode will continue through the day, running through the script for "ADSL doesn't work".

Thursday, 1st of June, Midday, Internode automated notification that my line has been provisioned, and tells me I may log in. Authentication still does not function.

Friday, 2rd of June, Internode continues to provide support. They organise a Telstra technician to visit the premesis on Tuesday, "between 8 and 12".

Weekend, 3-4th of June, continue debugging. Establish with two modems that no reponses to PPP requests are coming back at all. When talking to a support technician about this matter, the nonclamenture used in their scripts to describe this problem is "A Timeout Error".

Tuesday, 6th of June, 8:10am, Telstra Tech #1 comes at. Establishes that modem can get line sync. Establishes that all of the 'loss' on the phone line (distance to exchange, line quality, etc) is within tolerances and everything is green. Telstra Tech #1 tells us that the problem is with the ISP, and leaves.

Tuesday, 6th of June, 8:30am, Telstra Tech #1 visits the 'pit' and unplugs my phone line three times while I try to call Internode technical support.

Tuesday, 6th of June, 8:50am, Internode technical support calls me back on my mobile phone. I tell them in detail my findings from the weekend and what Telstra Tech #1 said. The kind person on the support line tells me that Telstra Tech #1 didn't do his job, and should have followed through after checking for line sync and authenticated with Telstra's Internode "Test" ADSL account against Internode's authentication server. A new support ticket with Telstra is lodged.

Tuesday, 6th of June, 1:30pm, New Telstra Tech #2 arrives on premesis, tests logging in using PPPoE. Discovers he cannot do so. Telstra Tech #2 makes negative comment about Telstra Tech #1 and returns to exchange.

Tuesday, 6th of June, 1:50pm, Telstra Tech #2 attempts to authenticate at exchange using our port. Discovers fault exists at exchange.

Tuesday, 6th of June, 2:13pm, Telstra Tech #2 plugs us into the correct port. Automatic authentication retries successfully auth with Internode. Internet starts working.

Why is getting The Internet so hard? Energex manage to turn on my power. Origin manage to deliver my gas. Telstra manage to connect my phone line. Why is getting the internet so hard? You'd think it should be a solved problem.

Wouldn't you?

What I've Been Up To

Booyah. I'm back. This feels great.

I've moved house. I love my new house. I had my housewarming last night, and that was excellent. Elspeth cooked up a storm and we ate all sorts of yummy goodies.

There's still heaps to do, but we've managed to get the bulk of our possessions moved and sorted out. I'm incredibly happy with the layout and features of our new house. Really nice kitchen (gas cooktop!) and we're really convenient for the Indooroopilly shopping center. :)

On the hacking front, I've been doing more and more code reviews for Twisted. I enjoy being able to see what's going on. Twisted uses Branch Based Development a set of scripts in a package called 'Combinator' written by glyph, which makes doing branch based development (all code gets developed in a branch, then merged to trunk after a review) sane. I've barely written any twisted code in the last couple of years, and I've committed to cvs/svn less than 10% of what I've done. Doing code reviews is a nice way of actually doing something productive.

Today Elspeth bought a copy of The Silver Spoon, a 50+ year old cookbook that's been translated from Italian. It's very very good, and she cooked a few things from it for dinner. Unlike 'Australian' style cookbooks, where each recipie takes a page or two, with detailed instructions, The Silver Spoon gives a list of ingredients, and a paragraph on how to cook them. There are 3 to 5 recipies per page, and often things like "And serve with a Green Sauce (page 73)" are found. Elspeth likes it a lot.


Painful Learning Experiences

So it turns out instead of learning a language like Erlang, Smalltalk or Lisp, I've ended up learning much more about that old clunker of a language. C.

Not just C really. I already had a passing understanding of C. What I've been learning about this week is the autotools toolchain, gcc, linkers and related kit.

I had a weird problem. This problem only occured on OSX, not on Debian, and everyone I talked to said something along the lines of "It should just work".

Here's a real brief description of the problem:

There are three object files. A, B and C. A contains symbol X. B contains symbol Y, and refers to symbol X. C refers to symbol Y.

As expected, gcc A.o B.o C.o will link these together correctly.

A and B are in an 'ar' archive libD.a, created using ar cru libfoo.a A.o B.o; ranlib libD.a. When linking C.o and libD.a, using gcc C.o libD.a symbol X cannot be found.

Reasonably complicated, but on a basic level, it seems that the linker can't resolve symbols that are in files in an 'ar' file, from other places in the 'ar' file. And that's exactly what was happening, and only on OSX.

It turns out, that much like a dear friend relates about how IBM ''fixed'' awk back in the day. Apple has ''fixed'' libtool and ranlib.

   -c Include common symbols as definitions with respect to the table
      of contents. This is seldom the intended behavior for linking
      from a library, as it forces the linking of a library member
      just because it uses an uninitialized global that is undefined
      at that point in the linking. This option is included only
      because this was the original behavior of ranlib. This option
      is not the default.

Apple have 'added' the -c flag to ranlib, to restore the behaviour that someone using the tool would expect to occur, but Apple in their wisdom have decided to remove.

Now the project in question will make a snarky remark in the ./configure stage if it detects a non-GNU ranlib, and add the -c flag.

'rati tags: ,


Zoom In Dot Com Dot Aye Ewe

Oh My Goodness. We finally have mapping that doesn't suck. Zoom In Dot Com Dot Aye Ewe is a google mapsish web service, and it completely rocks.

Here's an example map, this is a link to one of the places in Brisbane where I like eating out: http://zoomin.com.au/australia/qld/brisbane/milton/park+road. Just look at that url! Isn't it pretty? Gee, I wish some of my urls looked like that.

I'm gushing, but this is just so sexy. There aren't any ads (I don't really expect it will be long before geographical based ads pop up here, it's just a matter of time, and it makes sense)

The service is reasonably new. I have user account number 160. But apparently these blokes have been operating in Kiwi Land for a while now.

'rati tags:


Thanks Be To Sourceforge

Thanks to sourceforge, development rate has increased, we're moving nearly a dozen repositories to darcs (or other VCS's possibly), and people are getting really involved. There's a veritable frenzy of development and maintenance work being done.

This is thanks to SourceForge's CVS being down of course.


All The Safari Books

How to get all the Safari Books Online, a step by step guide.

  1. Go to a branch of the Brisbane City Council library system. Bring photo ID and proof of address. Drivers license is recommended.
  2. Go to the information desk. Request Library Membership. Provide all details requested. Receieve Library Card and PIN.
  3. Thank Library staffperson. Leave the library and go home.
  4. Use your web browser, and summon http://www.brisbane.qld.gov.au/libcatalogue
  5. Log into this website using your barcode number from your Library card, and the PIN you received in step 2.
  6. Click on Authorised Databases
  7. Click on Safari Books Online

Yay For Brisbane! I love this city.


Darcs talk at HUMBUG.

I think my darcs talk went well. Those who were interested got some information that will help them make intelligent decisions.

The talk was recorded. Don't know how good the recording is yet, we'll see.

Slides are available directly out of the darcs repo.

Public speaking always makes me nervous before and after, but during I feel like I'm flying.


Vmware hassles with vmnets and udev

Installing vmware creates a bunch of devices in /dev/ on installation. These devices are used for virtual networks (vmnets). If you're running udev, these devices will be created on install, and then the first time you reboot, they'll disappear.

for i in $(seq 0 9)
do [ -e /dev/vmnet$i ] || mknod /dev/vmnet$i c 119 $i

That's the little bash script you have to run as root to recreate those devices if they don't exist. I couldn't find this little gem anywhere, so I'm recording it here for future generations of vmware users.

'rati tags:


Touching Legacy Code

I agree with Adrian. Refactoring without tests is possibly the worst mistake you can make with a legacy codebase. I have been in situations where I really hated a piece of code I was working with, and so I mistakenly decided to refactor it. Sure, I cut the SLOC by 75%, and it worked better than it ever did before, and I found bugs and fixed them.

Doing it took much longer than I intended, introduced new bugs, and introduced a new maintenance cost. The code had been running fine, now it was not, and I had to fix it.

In my experience, legacy code should be touched as little as possible, and when it is touched, the part that's changed should be tested properly, and the entire thing should have some form of smoketest to make sure you didn't expose bugs elsewhere in the system.

I will now recite the Ode To The Maintenance Programmer

Once more I travel that long dark road
  into someone elses impossible code.
Through "if" and "switch" and "do" and "while"
  that twist and turn for mile and mile.
Clever code full of traps and tricks
  and you must discover how it ticks.
Once again I emerge to ask anew
  "What the HECK does this program DO?"
'rati tags:


Adventures using Darcs

I've been playing with darcs lately. It's a really cool version control system that has some wicked features, but somehow has managed to get not just the really insane stuff (really complicated patch management and conflict resolution) right, but also managed to get the little details right.

I had a situation yesterday that involved a conversation that went like this:

Friend: I can't find something that will let me give it a bunch of urls in a web browser and let me download all of them!
Me: I have an hour. Lets write it. Give me a PC.
Friend: Okay!

And so it was on. I had a couple hours to churn out a piece of software, and I wanted to do it in such a way as they could use it and I could maintain it. I also knew that any software I wrote in the space of an hour was going to be a steaming pile of crap.

My weapons of choice: Python, Twisted, Darcs, Putty, Vim.

I wrote most of it over SSH to my OSX machine where I had all the tools installed. Before I started, I created a new directory and typed darcs init. This initialised that directory as a darcs repo.

When I got it working for the first time (runnable code that did what was required) I typed darcs record -am "first iteration".

After a few more things I fixed up, I copied it to the windows machine I was doing my work from. I installed Python in C:\Python24, I grabbed the non-cygwin version of darcs and dragged the contents of that to the same directory. I added C:\Python24 to the $Path, and killed two birds with one stone. Now pythom and darcs were both in my path, and I could wreak havoc.

darcs get http://shiny.thorne.id.au/~stephen/Downloader/
cd Downloader
python downloader.py

It worked. I found and fixed bugs using vim on OSX, via putty. Every major change was accompanied by darcs record.

To test the code on win32, I used darcs pull -a to suck down the new patches.

Great. It worked. I wanted to show the person I wrote the code for how to use darcs, just in case he made modifications (he intended to do so), so I showed him how to run darcs record and darcs send. I operated under the assumption that because he was on a win32 box without an MTA set up correctly, it would fail horribly.

darcs send is a killer feature. Anyone with a checked out darcs repo can use it to send all the darcs patches upstream via SMTP. The patches arrive in the mailbox of the leader developer, and he can choose to apply them or not. No commit access is required. Anyone can do this. People can trade patches in high-fidelity, version controlled fashion.

I could not have been more wrong. It worked fine. It launched thunderbird and sent the email after simple thunderbird "[x] don't ask me again, okay/cancel" click through.

Oh Emm Eff Gee. It worked. Not only did it work, it REALLY worked. No hacking, no unix software installed, no SSH keys or logins. It just worked.

I intend to give a talk at humbug about darcs at some point in the next few weeks. I just have to write up some slides I guess.

'rati tags:


Mr Doctorow

Rohan has been ranting and raving about this Cory Doctorow dude for ages. I finally had a chance to go to his website and read some of his works. In fact, I've only read just one

It happened to be about my favourite topic. Printers.


Exporting from SF CVS to Darcs

Branedump of the steps to export from cvs.sf.net to darcs:

darcs get --partial http://darcs.arstecnica.it/tailor
cd tailor
chmod +x tailor
export PROJECT=netrek
export MODULE=client
export CVSUSER=jerub
export WEBPATH=$HOME/Sites
./tailor --verbose --source-kind=cvs --repository=:ext:$CVSUSER@cvs.sf.net:/cvsroot/$PROJECT --module=$MODULE --target-repository=$PROJECT-$MODULE > $PROJECT-$MODULE.config
sed -ie "s@$(pwd)@$WEBPATH@" $PROJECT-$MODULE.config
./tailor --configfile $PROJECT-$MODULE.config

Now. Once sf decides to accept my SSH key and stick it on the CVS servers, I'll be able to dump CVS to darcs, in both senses.

Updated: added the annoying looking sed command.

'rati tags:


Firebug for all

Debugging web pages is a pain. I have two major tricks I use:

  • Mochikit Logging Pane
  • Select stuff on the page->Right Click->View Selection Source...

But today glyph introduced my to the bees knees of firefox javascript debugging, FireBug.

I just wish the bastard had told me earlier.

If you develop for the web, firebug is for you.

'rati tags: ,


Hamachi vs. The Pink Robots

So, I had a good experience with technology tonight. Not a stellar experience, but good. I set up Hamachi on my OS X machine tonight. Hamachi is a VPN system that does lots of interesting stuff. If you haven't heard about it, please go read about it.

Hamachi isn't properly supported on mac, probably because of little things like driver installation and the non-existant GUI, but it works, and this is how to set it up:

Download tuntap (tiger) and hamachi ( There may be more a more up to date hamachi in the osx directory so check there if you want to.

I put those in ~/src/, so I did this:

# extract files into ~/src/tuntap_tiger and ~/src/hamachi-
cd ~/src/
tar -zxvf hamachi-
tar -zxvf tuntap_tiger.tar.gz

# hit next etc on the installer
open ~/src/tuntap_tiger/tuntap_installer.mpkg

# install hamachi
sudo ~/src/hamachi-

# config the tun device
sudo ~/src/hamachi-

# initialise, start, login, set a name
hamachi start
hamachi login
hamachi set-nick Jerub

# create a network, join it, go online, list other users...
hamachi create mynetwork mypassword
hamachi join mynetwork
hamachi go-online mynetwork
hamachi list

As I said. It wasn't incredibly painful. My initial test was initiating a VPN between two NAT'd connections, then contacting another host participating in the connection. I was running OS X and she was using the windows hamachi client.

'rati tags: ,


Twill and py.test

The latest tool in my arsenal is twill. It's a cross between a web browser and a testing framework. I think it works quite well, and I use it in conjunction with Buildbot, for testing the web interfaces at work.

Twill allows you to use a trivial little script like:

go http://myhost.exmaple.com/
formvalue login username "stephen"
formvalue login password "secr3t"
code 200
find "Login Successful"

And that will go to a website, login, check that a HTTP 200 code was returned, and verify that the words "Login Succesful" appeared on the page. If any problems occur, then the script will exit with a non-zero exit code, and an error message. This makes buildbot integration trivial.

But why twill is so nice, is because it's completely python. Instead of taking the long way around and invoking twill-sh, you can access the web browser directly, like so.

from twill import commands as web web.go('http://myhost.exmaple.com/')
web.formvalue('login', 'username', "stephen")
web.formvalue('login', ' password', "secr3t")
web.find('Login Successful')

And where this comes in real handy, is when I use it in a test suite such as py.test. py.test -ls --looponfailing is a command that really needs a post of its own to both explain and exclaim about, but suffice it to say that it's lifechanging.

import py.test
from twill.errors import TwillAssertionError
from twill import commands as web

def test_login():
    web.formvalue('login', 'username', "stephen")
    web.formvalue('login', ' password', "secr3t")
    web.find('Login Successful')
    py.test.raises(TwillAssertionError, web.find, 'Login Failed')

def test_failed_login():     web.go('http://myhost.exmaple.com/')
    web.formvalue('login', 'username', "stephen")
    web.formvalue('login', ' password', "notmypassword")
    web.find('Login Failed')
    py.test.raises(TwillAssertionError, web.find, 'Login Successful')

There, tests. Easy to write, easy to read, all written in python, I can leverage as much of python as I want, while still having a quick and easy way of puppeting a website, and testing that it works.

'rati tags: , ,


Yay, Yay I Say!

An RPython Extension Module Compiler has finally been released. RPython is fast. RPython is real fast. RPython is a restricted python variant that doesn't have much of the dynamic behaviour that a python programmer is used to. But It's Fast.

The net result is, if you have python code that isn't especially dynamic, but CPU heavy, you can compile it as a RPython extension module, and see it fly. Yay!

'rati tags: ,


First Steps In The Darc

I had an excellent open source experience today, I have been writing test code for work, and have been progressively replacing year old PBP test code with Twill test code.

When I first looked at twill, less than a week ago, I knew it was the successor, to PBP. I knew what to expect, a nice little interface that allowed me to pretend I was a web browser using a commandline interface.

What I expected was to find a source distribution either using Subversion and Trac, or just straight tarballs.

What I didn't expect was to find that it was distributed by a source control system I had heard of, but never used, Darcs. I fell in love with darcs nearly immediately, because of the following documentation found on the twill website:

To propose a change to the lead developer, make the changes and then do a 'send':
darcs record -am "explanation of change"
darcs send -a

I immediately saw the potential of this system. From the very outset, I didn't feel like I often feel with other projects (Not to single anyone out, but I feel this way with TurboGears and SQLObject), where I feel I can make valueable contributions, but the steps are:

  1. Find problem
  2. Fix problem
  3. Prepare patch from local version
  4. Submit bug in bugtracker
  5. Upload prepared patch
  6. Maintain local fix until patch is applied

I should note at this point that my development host's MTA was broken,
and so the darcs changes I sent didn't get sent immediately

  1. Find problem
  2. Fix problem
  3. Commit Change
  4. Submit Change

Awesome! I already have about 7 patches committed to my local copy of twill, 3 have been sent upstream, but the best bit was talking to the author of twill, we had this (abridged, paraphrased) exchange:

Me: I found a bug, you can't deselect checkboxes.
Titus: Dang, I guess my patch yesterday didn't fix it, can you provide a nonworking example?
Me: I've modified the unit tests, and I'm going to send them to you via darcs.
Titus: I'm going to have blog about it if you do that, fair warning ;).
Me: Oi! I wanted to blog about this first!

The point is, I found a problem in the program, and instead of complaining about it and filing a bug and having to have it triaged and wait for someone to look at it. I found a bug and was able to prepare a unit test. The test gets committed, and no one wants to leave a failing unit test in their code. So it'll get fixed. It'll get fixed good.

This is the way Open Source developers can serve their (technical) users. Allow them to skip the bug tracker. To go straight to the repeatable unit tests.

'rati tags: ,


Dear John

Dear Paypal,

I received a phishing scam today, it wasn't any good, but it got through my spam filter. I make a note of passing on phishing scams to those services that are being phished. It's quite simple usually, I think, "who do I tell?" and the answer comes to me immediately! abuse@service.com! Of course. Why not, they'll read the email I got, find the host that's responsible and shut them down.

Well. Not Your Service Apparently Apparently PayPal, instead of clicking Forward, typing abuse@paypal.com and Send. I'm supposed to go to the your paypal website, give you my email address (they already have this), the reason I'm contacting you (as if THAT isn't obvious) and - of all things - I have to log in with my paypal password.

Not just that, but you send me these instructions with a handy link to the paypal website. Telling me to click on the link to take me to your website.

Great going Paypal! Are you trying to encourage phishing?

Yours Truely,
Stupified Customer.

spoof@paypal.com is the correct email address to send phishing mails to. Paypal told me when I logged in to crosscheck my VISA bill against paypal's records.


I Try To Explain This To People

What I have ascertained is not that PCs as we know them lack good design, but that PCs as we know them have hardly any design to speak of. I'm not trying to be insulting. Use a Mac for a week, and we'll talk again.

Penny Arcade! - The Forbidden Fruit

technorati tags:



Q: What is a dialog box called if it doesn't have any buttons?
A: A monolog box.


Views in SQLObject

I've been doing lots of turbogears hacking lately, and one of the things that i've found it necessery to do, is arbitarily complicated queries, without the help of the magic of SQLObject's query building syntax. So I present, the way to implement SQL Views as SQLObject abstracted objects. Views are database specific, and I have only ever tested with postgresql, YMMV, no warranty implied, etc.

This is not a real example, my use-case for a view had left-joins on 6 tables, not just one, but it's a good template to work from. Note that you always have to have an 'id' column come back, and it has to be a valid primary key for the view, otherwise SQLObjet magic blows up.

class SiteSearch(SQLObject):
    name_one = StringCol()
    name_two = StringCol()
    somebool = BoolCol()

    def createTable(cls, ifNotExists=False, createJoinTables=True,
                    createIndexes=True, applyConstraints=True,
        conn = connection or cls._connection
        if ifNotExists and conn.tableExists(cls.sqlmeta.table):

        sql, constraints = cls.createTableSQL()

        # Treat the view like a constraint, only create it
        # after all the other tables have been created.
        return [sql]

    def createTableSQL(cls, createJoinTables=True, createIndexes=True,
        return """CREATE VIEW %s as (
        SELECT table_one.id as id,
               table_one.some_name as name_one,
               table_two.some_name as name_two,
               SOMEFUNC(table_one.foo + table_two.bar) as somebool
            LEFT JOIN table_two ON (table_one.foo_id = table_one.id)
        ) """ % (cls.sqlmeta.table,), []

I hope someone finds that useful. :)


World of Warcrack

World of Warcraft teaches the wrong things, is a little article on gamasutra that really caught my eye. It's not extremely well written, the author tries to set it around an enumerated list that turns into a mindless rant, but it's worth listing the two major things that WoW gets wrong:

  • Investing a lot of time in something is worth more than actual skill.
  • Group > Solo

David Sirlin talks about the side of gaming that's very close to my heart - getting good at games. I played pinball for a long time, and I got quite good at it - but I walk up to occupied tables and see masters at work. The kind of pinball player that I could quite easily call sensei with a straight face.

Do these people exist in World of Warcraft? I've seen quite a few good WoW players, and the one thing the good ones have in common is the ability to spend lots of time on the internet.

There are alternative worlds that value skill and knowledge. If I wanted to play with lots of other people on the internet, I would go to one of those alternatives.


Incoming python variant

Matt Palmer wrote up a ruby thingy, I'm going to quote him verbatim...

A method to find all files in a directory INCOMING which end with .changes, prepend INCOMING to the matching files, and return the results as an array (desk checked, but not run in anger yet):

def changes_files
Dir.entries(INCOMING).find_all { |f|
f =~ /\.changes$/ }.map { |f| File.join(INCOMING, f) }

Line noise it may be, but I'll be damned if it isn't succinct line noise.

I have a competitive streak a mile wide, so I feel compelled to present the Python version of this little doohickey.

import glob, os
def changes_files(incoming):
  return [os.path.abspath(f)
          for f in glob.glob(os.path.join(incoming, '*.changes'))]

I think I captured the specification, but I didn't implement the algorithm the same way. Also not used in anger, only very trivially tested. :)

On reflection, if relative paths are okay too, then this will work fine too:

import glob, os
def changes_files(incoming):
  return glob.glob(os.path.join(incoming, '*.changes'))

'rati tags:

Little Known Fact of the Day

The unix utility grep is an essential part of the toolkit of any shell denizen, I can't imagine living without it.

The origin of its name is from the beginning itself. From ed, the editor.

grep is an abbreviation of the ed command:

:global/regular expression/print

or, more succinctly expressed as:


Vim, vi, etc, still have the same commands, and the same syntax works today.
'rati tags: vim


Slight Discomfort

To The Gnome Project.

May The Wrath Of The Fleas Of A Thousand Camels Be Visited Upon Your Collective Behinds, For The Wonderful And Glorious Design Decision Of Making All Gnome Terminals Run In One Process.

On FedoraCore2, I've decided to move to 'aterm', on the basis that a) it works, and b) was the only terminal emulator that didn't make me want to throw up after doing valgrind --tool=memcheck $TERM.


technical documentation

I really wish us open source geeks would get a clue and do better technical documentation, glyph linked to his girlfriends Amazing tutorial on setting up a development environment for the twisted and divmod.

Well, specifically, a Windows development environment. From "How to install SVN" to "How to get the Emacs customisations up and running." With screenshots of more technical things (Yes, to a windows user, using cmd.exe to install a python lib using distutils is technical, there's no GUI for this).

Colour me impressed. I especially like how no assumptions are made about the existance of tools like "something to decompress bzip2." Far too many tutorials I've suffered through have had glaring omissions of basic tools that you are assumed to have.

rati' tags: , ,


Great Engineering Netgear!

This was absolutely astounding. I bought a WGR614 from umart today, brought it home, plugged it in, configured my router so that I could route to the network (necessery to get to the web interface), then attempted to get to http://admin:password@ in my browser. javascript redirects to http://www.routerlogin.com/welcome.htm

http://www.routerlogin.com/welcome.htm 302 redirects to http://www.netgear.com/welcome.htm

http://www.netgear.com/welcome.htm is a 404.

Great work netgear!

if("1" == "1")

Going to seems to work. I'm going to go finish setting up my router now.

On review of the docs, apparently netgear routers hijack the DNS for routerlogin.{com,net}, which is insane. Blah.

technorati tags: , ,


Changing Python Process Titles

Davyd wanted a way of changing process titles under python.

I've always used proctitle, which can rewrite the sys.argv, aka, char**argv array. And was quite aggravated that I didn't know how to change the process command name, which is what killall uses to kill processes.

So after being shown how to do it in mono, I found the docs, wrote it in C, put it in proctitle's _proctitle.c and created a patch for 0.0.2.

Now I can stop using pkill -f and go back to using killall. :)


Time to sit down and learn?

Something I've completely neglected to do for a few years, is learn a new language.

In the past, I would flit between languages bit. I went from LOGO to Pascal to C to Javascript to Perl to Java to Haskell to PHP to Python.

At python is where I've stayed. I did PHP for 4ish years, for work, and by the end of it I was saying very rude things about those who develop the language. For about 2 years of that, I was a secret python developer. It was just such a nice language in comparison, and I was so glad to leave that job and start where I am right now.

Now that I've been where I am for more than a year, and especially now that I've been enjoying programming for a bit, maybe it's time I learned a new language.

There's this smalltalk thing, which is apparently quite good, and there's a free implementation. I'm seriously tempted to sit down and see if I can make it useful.

technorati tags: , ,


Penguin Gloves

For bath time, in the linux-only household:

Aren't they cute?

My mother-in-law got wind of "Stephen Likes Penguins" a while back, and managed to get me, in no particular order:
  • A tall penguin Beer glass
  • A penguin shotglass
  • A penguin childrens book, with penguin stuffed toy
  • A penguin polo shirt
  • A penguin calendar
  • A penguin tea-towel
I've managed to convince her I don't like penguins all that much... It's a bit much getting showered with all those gifts...

I could use another wall calendar though.


60 Years of Happiness

Good News Day. My grandparents are celebrating their 60th wedding anniversary today, Feburary the 1st.

Gramps has done a lot in his life, and picked up speed post-retirement. He was the director of Aborigional Affairs in Victoria at some point in the distant past, and since then has worked for Prison Fellowship. Now he's just another retiree in a retirement village. They gave him an Order of Australia medal a few years ago for his work with Prision fellowship.

His first year at the retirement village, he wrote a Microsoft Access database for the village to use to manage the site. That was a few years ago. 1999. Since then he's moved into the Hostel, at the same village.

He still drives. 80+ and sharp enough to drive his old Mazda 929, which he keeps regularly serviced.

I don't think I remember meeting my Grandmother on my father's side, she died many years ago. My Grandfather on my father's side had a heart attack and died a few days after my wedding in 2001. I didn't go to the funeral because I was on my honeymoon at the time. He was taking his caravan on a tour around australia every second year at that point.

I'm looking forward to being old enough to blog about my great grandchildren.

Saying No

Something that's important to learn, is learning to say no. And I think I have learned this lesson quite well. This very day I said no to someone.
[robk] anyone interested in a senior lead programmer job? :)
[robk] must have solid php experience with honkin' big apps
* robk looks at Jerub
[Jerub] I hope I don't offend you personally when I tell you.
[Jerub] robk: Fuck Off.

It's just one of those things. I'm happy where I am.. Even if I wanted to kill myself every time I opened my email, I am secure in the knowledge. I don't have to write PHP.

On another note, a bookstore was closing down. I couldn't resist. I got goodies.


Joining the Flock

What's the idea behind Flock?
We believe that it should be easy for everyone to contribute to and participate on the web. To that end, we've started with integrating tools that make it easier to blog, publish your photos and share and discover things that are interesting to you.


I'm sold. I just closed Firefox and am now running Flock.

technorati tags:

Vim stuff


I often see other bloggers talking about vi and I want to jump up and down and demonstrate how to do what they're talking about, but without the pain of using vi-ish ways.

What Brad suggests doing in vi is something along the lines of:
  • Go to where you want the comment block to start
  • Hit mm
  • Go to where you want the comment block to end
  • Hit mn
  • Type :'m,'ns/^/#/
Which is archaic! Manaully doing marks like that is so 1980.

The two techniques that are possible in vim, which are both reasonably good, but I like the second one best are:
  • Go to where you want the comment block to start
  • Hit v
  • Go to where you want the comment block to end
  • Hit :s/^/#/
When in visual mode, pressing : will make vim automatically enter in the code that means "from the mark which is the start of the visual block to the mark that is the end of the visual block", or :'<,'>. This will essentially do the same thing as what Brad suggests, but using visual mode instead of manual marking.

The second technique, which as I mentioned, is my preferred one, is using Visual Block mode.
  • Go to where you want the comment block to start
  • Press Zero 0 so that you will be in the very first column
  • Press Control-v
  • Go to where you want the comment block to end
  • Press I#<esc>
This will insert the # (or whatever other text you wish to insert) at the beginning of each of those lines.

Even better, using the last technique I showed you, you can uncomment the same code by selecting the comments in the same way (using Control-v) and hitting x to delete them.

All Software Sucks

Some people tell me, when I suggest they use vim, that vim is an outdated editor, and they want something more modern.

sthorne 19238 0.0 1.5 27760 16056 ? Ss Jan17 11:51 vim controllers.py

I've been running it since the 17th - approximately 14 days. I've been using it for most of my programming since that date. 73 files have been edited at some point by that one process, during that time. It's used 12 minutes of CPU time. I have code folding, syntax highlighting, code completion, whole line completion, exuberant ctags and a kick arse set of macros.

And it's this version: VIM - Vi IMproved 6.3 (2004 June 7, compiled Jan 11 2005 10:58:40).

I hear the latest version has some bugfixes, and some new features.

I love my vim.

Linux Virus

After LCA'06, lots of the delegates have come down with a horrible disease or something, on the irc channel there have been a few quotes like:
[Gman-] 'First Linux Virus wipes out several geeks'
[Remosi] Gman-, 'first linux virus threatens developers'
[pfenwick] I can see a new keynote for next year: Linux Virus
counter-measures: How soap can improve your computing
[jk-] s/soap/SOAP/
Well. I thought it was funny. :)


My Stove

It's the stove of the future!

The drawer slides out!

Isn't that amazing..