Sunday, December 2, 2007

My Best Yet Bread

I baked bread tonight and I'm thrilled with the result: A golden, thin, crisp crust filled with tiny bubbles like a baguette and a light resiliant crumb. When I pulled it from the oven it sounded like rice crispies in milk as the steam continued to escape from the loaf. Bread is a deceptively simple thing. If you look at the ingredient list: flour, water, yeast, salt you might think, 'well this won't be hard.' Maybe I'm a slow learner, but I've made hundreds of loaves and this is the first one where I think I have come close to greatness. I've made bread too dense, too dry, too wet, crust too thick, undercooked, overcooked. Even the worst loaves got eaten, mostly. There are so many variables and I wanted to just record what I was paying attention to this time:
  • Moisture. A moist oven is critical for crust formation. Without moisture crust forms too soon and the bread doesn't rise properly, also the crust can become too thick. Put a pan of water in the bottom of your oven.
  • Heat. Start hot. Decrease heat over time to mimic the cooling of a wood fired oven.
  • Dough texture. Don't add too much flour. The dough should be shiny and elastic. If it is too dry the bread will be too dense.
That being said, here is my recipe. I don't have any measuring equipment in my kitchen so all measurements are in coffee mugs and pinches.

Ingredients:
1 coffee mug warm water.
3 - 4 coffee mugs flour.
1/2 packet yeast.
A 4 finger pinch of sea salt.
A 4 finger pinch of sugar.

Process:

Mix the sugar in half of the warm water. Add the yeast, but don't stir it into the water. Wait 15 minutes. It will be frothy looking at the surface. Add the rest of the water to a large bowl and start adding flour 1 mug at a time. Add the salt. Keep stirring and adding flour. When it is just dry enough that it doesn't stick too much take it from the bowl and knead it on a floured surface for a few minutes adding flour as necessary. Make a ball, put it in a floured bowl, cover and let it rise in a warm place until doubled, about an hour and a half. Remake your ball and put it on a flat pan covered in parchment paper. Let it rise another hour. Get the oven ready: fill a pan with water, put it in the bottom of the oven and heat the oven to 240 C. Just before the bread goes in wet your hand and rub the surface of the dough. Don't be afraid to get it nice and wet. Quickly open the preheated oven and put the bread in. When I open the door to my oven, steam comes out. After 15 minutes turn down the heat to 200 C. After another 15 minutes (sometimes sooner) it will be done. When you tap the bottom of the loaf it should sound hollow.

Let's hope this works out again the next time. And next time I am going to remove the pan of water with 10 minutes to go, just to see what happens. My only criticism this time was I wanted the crust to be a touch heartier. FYI I did this in a convection oven so, nothing wrong with baking bread in a convection oven.

Links:
Bread baking tips
Yeast bread recipe
Sourdough recipe

Monday, November 26, 2007

Down with IF!

Campagna Anti-IFTHEN what?
A friend of mine sent me a link to an Italian website sponsoring a campaign to rid software of the IF statement, especially 'type determinant' IF statements. Here is a link to the original page, and here is my rough translation of it:

The anti IF campaign. "if you want to stop, you can"

Objective:

The objective of this campaign is to make developers more aware of how to use object oriented design principles effectively. The first step towards reaching this objective is becoming aware of the consequences of using IF statements and other imperative programming techniques in the context of an Object Oriented language. The major objective of the campaign is to help developers understand how principles of OO design make it possible to achieve software that is more effective in terms of flexibility, changeability, descriptiveness, and testability.

Why:

The campaign was born from one of Francesco Cirillo's ideas: "Many teams want to be agile but don't know how to reduce the complixity of their code. We can start to achieve this by using OO techniques to remove type-determining IF statements from our code."

The fundamental problem is that type-determining IF statements create dipendencies, and coupling across modules (methods, objects, components, etc.) and increase the possible paths through the code, reducing the readability. The IF statement seems to be a quick and easy way to achieve functionality but with complex IF statements you quickly get to apoint where the software is no longer able to be modified and is full of duplication. Here is a simple example

// classe Bond
double calculateValue() {
if(_type == BTP) {
return calculateBTPValue();
} else if(_type == BOT) {
return calculateBOTValue();
} else {
return calculateEUBValue();
}
}

Every additional type added to the system obliges us to modify this piece of code. Objects give us an effective instrument for making sure that changes in requirements don't translate into changes in our code.

// classe Bond
double calculateValue() {
_bondProfile.calculate();
}

// classe AbstractBondProfile
abstract double calculate();

// classe BTPBondProfile
double calculate() {
...
}
// classe BOTBondProfile
double calculate() {
...
}
// classe EUBondProfile
double calculate() {
...
}

What is the advantage of removing the IF? In the fact that tomorrow the request for a new type of Loan requires simply creating a new class with the only additional logic being how to calculate the value of the loan.

The solution isn't always to create a new abstract class of interface, but the solution always increases the flexibility, descriptiveness, and testability of the software.

I agree with them, but would like to point out that you don't need that stinky old abstract class! If you apply a dynamic language such as Ruby to the problem, Duck typing removes the need for such an empty construct. And meta-programming can go a long way in removing unnecessary IF statements from your code. These guys should definately have some meta-programming examples of reducing cyclomatic complexity on their page.

Thursday, November 22, 2007

Zip A Directory With Ruby


I just found a new gem called rubyzip. Which is nice for creating regular old zip files. Ruby comes with zlib, but that is for gzip files which are not too friendly for windows users. Not that I condone the practice of using windows... Anyway. To install just type 'gem install rubyzip'. Here is some code I am using to zip up an entire directory called 'src', excluding hidden files. Just remove the Find.prune line if you don't want to exclude anything.


require 'rubygems'
require 'zip/zip'
require 'find'
require 'fileutils'
include FileUtils

root = pwd+'/src'
new_zip_file = pwd+'/newzipfile.zip'

Zip::ZipFile.open(new_zip_file, Zip::ZipFile::CREATE)do |zipfile|
Find.find(root) do |path|
Find.prune if File.basename(path)[0] == ?.
dest = /src\/(\w.*)/.match(path)
zipfile.add(dest[1],path) if dest
end
end


By the way sorry for no code formatting or highlighting. If only blogger had support for such useful stuff. I bet devastatin dave has code highlighting on his website. Hmm, I wonder what devastatin dave has been doing since 1986?

Monday, November 19, 2007

Sound Opinions Dissed on Guitar Hero


There is a wonderful program on public radio these days. Sound Opinions is the only radio show I still tune in for. Jim DeRogatis and Greg Kot are the Siskel and Ebert of rock and roll in pedigree, only better. So I was listening to their last show. They were on one of their favorite topics, talking about musicians licensing their music for commercial purposes, and suddenly, they were talking about the rolling stones licensing "Paint it Black" for use in Guitar Hero. They gave off their usual negative vibes about licensing art for commercial purposes and then they proceeded to mock the game and call it a waste of time. I have to call them out on this one. These guys clearly don't know the first thing about video games, particularly this one. Who hasn't listened to their favorite music and imagined themselves in the band? Guitar Hero is simply an electronic extension to that fantasy. And guys, when you add buttons to that air guitar it means you can compete against your friends. I wonder who does a better Kieth Richards, Jim or Greg. I guess we will never know. I'm just saying, but if anyone should be all for rocking out in all of its forms, the defenders of Pete Townsend's Windmilling right arm should be for it. But alas no, their advice is to stop wasting your time and learn guitar for real. Thanks gramps, I won't have time for that because I'm too buys saving up to buy GH III for my Wii.

Sunday, November 18, 2007

Move over VMWare


I just found out that I will need Visual Studio for my next project. That means I need Windows. In the past when I needed windows I installed it as a virtual machine and keep Ubuntu as my host OS. VMWare worked great, but setup and maintenance were a hassle because it wasn't installed with the package manager, and any time I got a kernel upgrade I would have to recompile the VMWare kernel module. Fortunately InnoTek, a competitor of VMWare, has recently GPL'd their virtuilization software called VirtualBox. Installation is as simple as
apt-get install virtualbox-ose
I had XP up and running atop Ubuntu in 20 minutes. After a couple weeks of use I can say it is fast, stable, and network configuration is much easier than VMWare. Bring on the kernel updates! Also you will note from my screenshot that individual windows apps run in their own windows, which is much nicer than having to interact with all your windows apps in one window. Now I'm not claiming virtualbox is the greatest, but in my experience, it is waaaay better than virtual PC, and for ubuntu users and many other GNU/Linux users it is superior to VMWare. Dig it.

Monday, November 5, 2007

You aren't computer literate unless you can understand a computer language


I was watching a lecture by Alan Kay entitled "Doing with Images Makes Symbols: Communicating Wit h Computers". At one point Kay is talking about teaching children to program and the importance of computer literacy. For Kay, literacy isn't understanding computer jargon or being able to check your email. He defines three aspects of literacy: Access Literacy is reading. You must be able to understand the intent of others. Creative Literacy is writing. You should be able to express your intent. Literature gives us genres and different ways of understanding what you are reading or writing. Kay's ideal is that people should be able to make the computer do anything they can think of. If it doesn't already do something, you should simply be able to tell it how to do that new thing. Such a high level relationship requires communicating with your computer at a high level. The three basic mouse gestures (point, left-click, right-click) aren't practical for conveying such complex ideas*. That is where programming languages come in (Smalltalk was his answer to this). Although I think all languages fall short of this ideal, you have to hand it to him, it is a ambitious goal, and in trying to achieve it he has done some amazing things.

More to come on various languages that attempt to bring computer literacy to the masses. In the meantime check out the video. I'm talking about a small little part of what Alan talks about. His work on creating GUIs is fascinating.

Here is the video 1hr 36 mins:


*(Although gestures can be a medium for a powerful language, even for computers, the Wii proves that)

Tuesday, October 9, 2007

CruiseControl Subversion labelincrementor


A while back I had a post about accessing the subversion revision in cruisecontrol. The code for the plugin I posted there was incomplete (err... broken) and has been a source of confusion. I'm posting some updated code here.


Here is the plugin. Very brief instructions on how to configure CC are in the comments at the top. The tests follow.

package net.sourceforge.cruisecontrol.labelincrementers;

import org.jdom.Element;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/*
To properly set this up put the following in your cruise config.xml



Make sure cat-ant.jar is in your cruise lib directory and restart cruise.

*/
public class SVNLabelIncrementer extends DefaultLabelIncrementer {
private String workingCopyPath = ".";

public String incrementLabel(String oldLabel, Element buildLog) {

String SVNRev = "";
try {
SVNRev = getSvnRevision();
}
catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}

String label = getNonSubversionPartOfLabel(oldLabel);
if (SVNRev == null || SVNRev.equals(""))
return oldLabel;
return label + "." + SVNRev;

}

protected String getSvnRevision() throws IOException {
String rev;
Process p = null;
try {
p = Runtime.getRuntime().exec("svnversion " + workingCopyPath);
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(p.getInputStream()));
rev = stdInput.readLine();
}
finally {
if (p != null)
p.destroy();
}

return rev;
}

public boolean isValidLabel(String label) {
return super.isValidLabel(getNonSubversionPartOfLabel(label));
}

private String getNonSubversionPartOfLabel(String label) {
int index = label.lastIndexOf(".");
if (index == -1)
index = label.length();
return label.substring(0, index);
}

public void setWorkingCopyPath(String path) {
workingCopyPath = path;
}

public String getLocalWorkingCopy(){
return workingCopyPath;
}

public void setLocalWorkingCopy(String wc){
workingCopyPath = wc;
}
}



package net.sourceforge.cruisecontrol.labelincrementers;

import junit.framework.TestCase;
import org.jdom.Element;
import junit.textui.TestRunner;
import junit.framework.TestSuite;

public class SVNLabelIncrementerTest extends TestCase {

public void testReturnsOldLabelWhenRevNotPresent() {
SVNLabelIncrementerStub inc = new SVNLabelIncrementerStub();
assertEquals("FE6.0.1", inc.incrementLabel("FE6.0.1", new Element("foo")));
}

public void testReturnsRevisionWhenRevPresent() {
SVNLabelIncrementerStub inc = new SVNLabelIncrementerStub();
inc.setSvnRevision("3");
assertEquals("FE6.0.3", inc.incrementLabel("FE6.0.1", new Element("foo")));
}

public void testReturnsIncrementedLabelWhenRevNull() {
SVNLabelIncrementerStub inc = new SVNLabelIncrementerStub();
inc.setSvnRevision(null);
assertEquals("FE6.0.1", inc.incrementLabel("FE6.0.1", new Element("foo")));
}

public void testProducesValidLabelWhenRevNotPresent() {
SVNLabelIncrementerStub inc = new SVNLabelIncrementerStub();
String label = inc.incrementLabel("FE6.0.1", new Element("foo"));
assertTrue(inc.isValidLabel(label));
}

public void testIncrementsCorrectlyOverSeveralIterations() {
SVNLabelIncrementerStub inc = new SVNLabelIncrementerStub();
inc.setSvnRevision("1");
assertEquals("FE6.0.1", inc.incrementLabel("FE6.0.1", new Element("foo")));
assertEquals("FE6.0.1", inc.incrementLabel("FE6.0.2", new Element("foo")));
assertEquals("FE6.0.1", inc.incrementLabel("FE6.0.3", new Element("foo")));
inc.setSvnRevision("3");
assertEquals("FE6.0.3", inc.incrementLabel("FE6.0.4", new Element("foo")));
}

public static void main(String[] args){
TestRunner.run(new TestSuite(SVNLabelIncrementerTest.class));
}

public class SVNLabelIncrementerStub extends SVNLabelIncrementer {
private String revision = "";
protected String getSvnRevision(){
return revision;
}

public void setSvnRevision(String revision){
this.revision = revision;
}
}
}

Saturday, October 6, 2007

Coming Soon to a Refrigerator Near Me, "Iron IPA"


Today I'm brewing my first IPA. I'm planning on dry hopping to give it plenty of hoppy kick. The label, created with inkscape, is all ready. Check it out.

Thursday, September 27, 2007

Remote Build Light

The machine that our CC build runs on is stashed away in a server room somewhere so we can't use the CC x10 plugin to control our build light. To get around this we have a cron job on one of our pairing stations that runs a jRuby script. The script does a screen scrape of our cruise status page and turns the build light on (when any project is failing) or off (when all projects are happy). Three cheers for the build light!

Code for this is here

Monday, September 10, 2007

Sketchy documentation for people too lazy to tie their own Shoes


Shoes is so new and in flux that unless you are comfortable cracking open some Ruby code and lots of C you will have a hard time getting started. I made a little cheat sheet of Shoes for the presentation I gave. It is moth eaten and worse yet, half-assed. I apologize in advance, but maybe somebody will find it useful.

The format here is that method names are in bold. An english description of the arguments (I know it is a terrible thing to do) follow, along with a description of what it does.

background - takes a filename or a color created by rgb(r,g,b) followed by an array of :top, :right, :left, :height, :width

rgb - takes 3 parameters 0-254 and returns a color

width - gives you the width of an element

button - takes a String label and a block and draws a button to the screen

show - make an element that is not visible, visible.

alert - takes a String message and pops up an alert box.

title - takes a String and prints out some big text

contents - returns an array of the elements in this Shoes Object.

ask_color - takes a String which is the title of the color selection dialog that pops up

width= - looks like it lets you set width (but it doesn't resize the base window)

rect - Takes a hash :left, :top, :width, :height and draw a rectangle

quit - close the application window

click - I think it takes a block like keypress, but i just get segfaults.

incident - causes your app to segfault

rotate - takes a number of degrees. and rotates things you have drawn.

height - gives you the height of the element.

flow - A style element that takes an array with :margin and a block consisting of other elements to display. Flows fill left to right. When they hit max width they go down to the next row.

gray - takes an integer, but nothing happens.

border - takes a rgb() color and an array of :strokewidth

display - not sure, think it has something to do with debugging messages you see.

list_box - a selection element that takes a hash of :items where items is a list of options.

height= - set the height of the element.

clear - can take a block. clears what was drawn in the block or without a block clears the whole window.

ask_open_file - takes a string which is the title of the file open dialog box.

goto - takes a url string and redirects you to some url.

text - take a string and display it on the screen. Understands html markup.

subtitle - takes a string and displays some larger than normal text.

stack - A style element that takes an array with :margin and a block consisting of other

image - Takes a filename and displays that image.

edit_line - An element that takes text input

nostroke - the things you draw won't have a stroke line.
release

smalltitle - takes a string and displays some largeish, but not too large text.

stroke - i don't know what parameters it takes, i think maybe a stack or flow, but it sets the stroke width on things you draw.

strokewidth - takes a number that becomes the width of the strokes you make

hide - make an element invisible

animate - takes a number representing frames per second and a block that happens that many times per sec.

confirm - takes a string that is the confirmation message for the confirmation dialog that pops up.

progress - show a progress bar. I have no idea how to fill it up.

keypress - takes a block that gets the value from the keypress. pretty much segfaults every time.

If you have the time I really recommend digging into the C code behind shoes. It practically documents itself!

Why Shoes? at The Ruby User Group


Presentation at chirb tonight. Huge audience! (Over 50 people braved the rain.) Ryan Platte gave an eye opening talk on using GWT with Rails, and Mike Ward talked up a very cool Rails Gem/Plugin called Argible. It took me a few minutes to understand what Argible was about, but once I saw how it was using annotations to get rid of that business of accessing the parameter map in rails I was impressed.

My talk on Shoes was rough going: I put off the research, no documentation for it yet, and half the audience couldn't get the code to compile. Everyone made the best of it. (Thank you everyone!) I gave them a good taste of what a fun little toolkit Shoes is, and some interesting discussion resulted. A question that came up over and over was "Why Shoes?" The following were the major benefits as promulgated by myself, Andy, and Evan
    • You work purely in Ruby, so there aren't other languages or technologies to learn.
    • Your application can be nice, neat OO code.
    • Simple syntax, small files.
    • Apps are easy to distribute since shoes is entirely self contained.
    • Apps are easy to distribute because of the .shy files.
We talked several times about the point of shoes, and if you consider that the intended application is in Hackety-Hack. The above benefits are trememdous. But Why brings up the following in the Shoes README

Shoes is strictly inspired by stuff like REBOL/View, HyperCard,
the web itself and, of course, Processing and NodeBox.

I don't like the bulkiness and the layers and layers of wxWindows,
FOX, QT, GNOME. They are big, big libraries and all the apps look
identical, devoid of spirit.

The unique thing about the web is that it gives you very few
controls, but people are able to build wildly different pages
with it that are still immediately accessible to people.

My final position on Shoes is that I'm all in, and I can't wait for it to pump life back into HacketyHack.

mr_luc Broke Ruby's Heart


Doing research for a presentation on Shoes in a forum. Sifting through matted layers of conversation. Listlessly digging, late into the night, in a manner not quite like shuffling your feet through fallen leaves, just for the sensation of it. When suddenly from the decay I turn up a lover's handwritten note! In this case a certain mr_luc was confessing his own duplicity, confessing to Ruby that his heart belongs to another.

I now reproduce the promiscuous mr_luc's confession as overheard here
I like Camping.

I think I will probably like Shoes.

The reason I like them? I am having a very hard time fighting between my Lisp side and my Ruby side. My little language side and my object side. My OMGrails side and my OMGmacros side.

So right now I'm a total code slut.

I'm exclusive and classy (no Microsoft rednecks or Perl stoners, please), but everything has got to be totally commitment-free. I have this respite, I don't know for how long, where I'm not coding for money and I get to play around with stuff I like and decide how I really want to code. I don't want to invest in a massive architecture, and when compared to Lisp, Ruby's syntax and macrolessness and object centrism are already big mental investments without learning the Rails architecture, as you really need to do to be capable with it.

Lisp lets me work as elegantly as I can think. Ruby lets me be productive and play with the latest toys with a lower amount of pain than other options, but it doesn't have my heart.

And for that reason, I like Camping and (probably) Shoes. I'm a whore right now, Ruby. I'm not really yours.
This is as intimate as it gets with programming languages. Thank you for coming clean Mr. Luc, whoever you are.

Wednesday, August 29, 2007

Shed a Tear for Edward Lear

Edward Lear was a British Author and Poet I have recently re-discovered (You might know the poem "The Owl and the Pussycat"). I love his books of nonsense. They are wonderfully illustrated and are full of limericks, A form of poetry I am particularly fond of. In fact the limericks in Lear's nonsense books are given credit for popularizing the form. Lear was also an accomplished illustrator who's work was compared favorably with Audubon.

Here are some of his limericks and accompanying illustrations:


There was an Old Man of Coblenz,
The length of whose legs was immense;
He went with one prance from Turkey to France,
That surprising Old Man of Coblenz.





There was an Old Man on whose nose
Most birds of the air could repose;
But they all flew away at the closing of day,
Which relieved that Old Man and his nose.



There was an Old Person of Dutton,
Whose head was as small as a button;
So to make it look big he purchased a wig,
And rapidly rushed about Dutton.




All of the above examples are from project Gutenberg's collection of Lear's works. Here is a link to the book containing all the above limericks and illustrations.


I have written a quick limerick to commemorate this zany, zany giant of a limericker. Ahem.

Edward Lear was a man with a craze.
He would write only lim'ricks for days.
Then he'd drink himself silly
In a suit that was frilly.
And stumble back home in a haze.

Saturday, August 25, 2007

Using Regexp in Mocha Expectations

A while back I was writing some tests for a rake file. The task I was testing was just a bunch of file manipulation and one of my tests looked something like this:




Dir.glob('weblogic/adomain/*') do |file|
next unless test(?d, file)
FileUtils.expects(:cp_r).with(file,'new_server/weblogic/adomain')
end

To be honest I didn't want to be setting expectations in a loop. A regex would handle this much better, right? Turns out you can pass a block to the with clause that has a regex in it. That would look something like this:

FileUtils.expects(:cp_r).with() { |value| /new_server\/weblogic\/adomain/ === value}


That isn't too bad, but I think this is nicer:

FileUtils.expects(:cp_r).with(/new_server\/weblogic\/adomain/)

Unfortunately Mocha, the mocking framework I am using here doesn't let you. Sooo, I submitted a patch. I'm really hoping this makes it in as I think it is a handy thing. As a side note, it turns out there was already a similar patch against Mocha for this submitted by Wolfgang Schnerring. It had been ignored for about 7 months! I incorporated the best of both into one. Let's hope this one gets looked at sooner.

Wednesday, August 22, 2007

Home Sweet /Home


Have you ever re-installed your OS? Often that means setting up preferences again for all your applications, getting that look and feel back that you like, copying a bunch of files around. It is annoying and can take days! With Linux you don't ever have to deal with this if you know a simple trick. This probably doesn't come as a surprise to many Linux users, but it might to the newbies. The trick is to put your /home directory in a separate partition or even on a separate disk. I haven't had to setup any of my preferences for any of my applications in years, even across multiple Linux Distributions.

Here is an example of how awesome that is. Earlier this week I had a hard disk fail and it took my OS with it. Fortunately, I had my /home directory on a separate hard disk. I reinstalled my OS on an empty partition on the disk and pointed /home at my old /home partition and I was back up and running as if nothing had happened in half an hour. All my old preferences and files were there. My desktop was just as messy as it ever was! Home sweet home.

I'm not going to get into the details of how to partition a disk, but a hint is to go to the "manual partitioning" step on installation. I usually have 3 partitions: /,/home and a swap partition. Ping me if you need more info than that.

Tuesday, August 14, 2007

A Diagram is worth 1000 words.

My presentation at OSCON forced me to learn how to create diagrams. Now that I have a couple under my belt I find myself just doodling diagrams in my spare time. Here is one that incorporates an attempt at calligraphy, a cartoon, and a diagram.

Thursday, August 2, 2007

Unofficial Ubuntu


If you use Ubuntu you should definately have this link in your bookmarks: http://ubuntuguide.org/wiki/Ubuntu:Feisty This is a guide to making your OS do what you want it to do. I routinely copy and paste the commands from this page into my console when I am setting up a fresh Ubuntu machine. The biggest trick of all is just getting the proper set of apt repositories Once you have that being able to watch movies in your browser, getting the flash plugin, and other stuff becomes as simple as apt-get install package-name. Check it out!

OSCON Retrospective

Update 11/14/2008. Andy just sent me a link to this summary of our talk. Not too negative. Cool! It has been a week since Andy and I gave our presentation at OSCON so I thought it high time I got around to thinking about how things went. Happily the "Pimp My Build" presentation went very well. From the get go there were plenty of audience questions, and people with good experiences to share. The audience was mostly long time CruiseControl users and a handful of newbies. With that in mind we glossed over our introductory material and focused on some of the more advanced material. One area of interest was staged builds. There are several reasons why you might need a staged build, the most common being a functional test suite that takes a long time to run. In that case you have a quick build or set of smoke tests to give you early feedback, followed by a second stage that runs the full battery of tests. There was lots of discussion in this section and I hope that means people were getting some new ideas The other hot topic was visualization and sharing metrics with the team through cruise. Unfortunately we didn't have good demonstrations prepared for this so it was mostly just us describing the concept, but it seemed to give some people ideas.

This brings up some of the things we could have done better. We prepared for an audience that included more newbies, but they didn't show up. Had we known, we could have spent more time preparing content on things like metrics and visualization. Also there was my cc.rb sms notifier plugin debacle. I wrote a plugin for cc.rb that sends build status changes out to your phone via sms, however the sms were taking several hours to reach people. It was kind of embarrassing that my live demo (which was supposed to send messages to several audience members) didn't work. Although it was kind of funny to think that several hours after the presentation people were getting build status notifications.

Overall it was a great experience and it gave me a real appreciation for the amazing keynotes I watched on Friday morning. Jimmy Wales of Wikipedia fame gave an interesting talk on WikiaSearch which is supposed to be an open source Google killer. Another talk by James Larsson called Pimp My Garbage (I ripped off the name first, I swear!) was amazing. Here is a video that unfortunately doesn't show the videos James used to illustrate his hacks, so it is missing the best part: where you get to see him shooting at an old plotter with a BB gun, a steady hand game with 75,000 volts, and a hacked version of pong called "Leather Fetish Pong" that uses kinky women's boots as controllers.

Aside: Of the two conferences I have attended this year I really appreciated OSCON's breadth over the focus of RailsConf.

Annoying Weirdness in Mailcap File


Ever since i upgraded to Ubuntu 7.04 I have been unable to successfully run all the unit tests for our project. One particular test kept failing in a part of the system that handles email. Here was the stack trace I kept seeing.

java.lang.NullPointerException
[junit] at java.lang.String.concat(Ljava.lang.String;)Ljava.lang.String;(Unknown Source)
[junit] at com.sun.activation.registries.MailcapFile.parseLine(MailcapFile.java:234)
[junit] at com.sun.activation.registries.MailcapFile.parse(MailcapFile.java:198)
[junit] at com.sun.activation.registries.MailcapFile.createMailcapHash(MailcapFile.java:158)
[junit] at com.sun.activation.registries.MailcapFile.(MailcapFile.java:47)
[junit] at javax.activation.MailcapCommandMap.loadFile(MailcapCommandMap.java:171)
[junit] at javax.activation.MailcapCommandMap.(MailcapCommandMap.java:125)
[junit] at javax.activation.CommandMap.getDefaultCommandMap(CommandMap.java:54)
[junit] at javax.activation.DataHandler.initDefaultCommandMap(DataHandler.java:149)
[junit] at javax.activation.DataHandler.getDataContentHandler(DataHandler.java:615)
[junit] at javax.activation.DataHandler.getContent(DataHandler.java:540)
[junit] at javax.mail.internet.MimeMessage.getContent(MimeMessage.java:1068)


I figured out that there is a file called .mailcap in my home directory. In my case, for whatever reason, the first line of the file had some junk in it. There was a -e there. By deleting that line of the .mailcap the test passes. Totally lame. I don't know if this is an Ubuntu thing or PEBKAC, but at least it is easy to fix (if you know what is wrong).

Thursday, July 26, 2007

Wanna Draw? Try Inkscape!


This is another diagram I created for our presentation at OSCON to illustrate a particular project's cruise setup. I like it much better than my first attempt. Both attempts at diagrams were composed in a marvelous little open source program called inkscape. Check it out!



Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 License.

Sunday, July 22, 2007

Continuous Integration Diagram


This is a high level diagram that I made describing how Continuous Integration works. Click on it for the full size image.








Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 License.

Saturday, July 21, 2007

The Case for Continuous Integration

Most people don't need an introduction to Continuous Integration (CI), but there are still many programmers and teams out there who haven't adopted this practice. I know some of them personally. This writeup is for them. It is a series of short and hopefully persuasive arguments for adopting CI. The idea here is to give the adopter a variety of easy to digest reasons for using CI.

First, here is a brief definition for CI. Continuous Integration is automated compilation and testing that leads to the creation of ready to deploy software any time the code base changes. Now for the arguments.

The Code you Can't Deploy Argument
Code you can't deploy is useless code. You just put the finishing touches on a servlet six months in the making. Good thing too because the customer plans to go live this weekend. It works fine when deployed on your windows workstation from Eclipse, but the customer is deploying into a clustered environment with a Linux operating system. Guess what, it is going to be a looong weekend. Had you been using proper Continuous Integration you could tell your client with confidence that there wouldn't be any big surprises.
The Cost and Quality Argument
One of the basic operations of writing software is integration. As a developer you constantly integrate with external systems, libraries, teammates' code, even the code you wrote yesterday. Programmers are natural continuous integrators! But programmers are only human, and we make mistakes (better known as bugs). CI, done properly, is a foolproof way for rapidly discovering bugs. And the faster we discover a bug, the cheaper and less risky the fix. CI makes our software higher quality and lower cost.
The Nobody's Perfect Argument
Good programmer writes a test and the code to make the test pass. Next, good programmer runs all the tests, not just the tests around the change, and only after the tests pass does good programmer check in. Why bother with a continuous integration server like CruiseControl when everyone on your team is such a good programmer? Hmm, wait a minute, did good programmer remember to check in that 3rd party lib? Did good programmer forget to do an svn add on that new unit test for that critical 3rd party interface? Even good programmers make mistakes that can be troublesome for the rest of the team and costly down the road. Really good programmers know they make mistakes and love Continuous Integration for catching those mistakes before they impact the team and the project.
The Dependent Projects Argument
Team one has a build. Team two's build depends on what team one is building. Team one creates a new drop of their system once a week for delivering to team two. This means it might take up to a week for team two to realize that team one has broken their software. Perhaps it takes another week for team one to understand and fix what they broke. Meanwhile team two is building against the last working drop they recieved from team one, which is now three weeks old! This whole mess would have been avoided had teams one and two been using Continuous Integration (CI). With CI team one is automatically creating a new drop of their system every time their code changes, and team two is automatically testing their code against team one's latest drop. Now both teams know within minutes of checking in when and why their software no longer works.
The Better Communication Argument
A Continuous Integration (CI) server like CruiseControl gives developers a centralized place to see the status of the code. They can see which classes their team members are working on, grab the latest deployable, and get notifications when the status of the build changes. This information can be a catalyst for important project conversations. One developer might notice that another has been checking in code without tests. That is a good time for a quick talk about the importance of unit testing. Two developers might realize they are working on the same part of the system, another opportune moment to talk about how they might avoid commit conflicts . CI increases the points of contact between individual developers or pairs during the day and encourages, even forces, better team communication which can head off costly problems before they have a chance to arise.
It is difficult to leave it at that. I still want to give more examples, discuss all the different levels to which you can take CI, and talk about how even small projects and small or single developer teams can benefit, but so much has already been written, so I'll conserve space on the web by just linking to a couple good sources for further reading.

Thursday, July 5, 2007

Replacing Ant With Rake


The other day, as I was tracking down a bug in a large J2EE application, I had an idea for a new ant task. Hooray automation! The excitement of the new idea quickly vanished as the reality of dealing with ant set in.

The Problem:
Programatically changing text in a file. Oh noes! I will have to create some templates, copy files around, replace words and stuff. An hour or two to figure out what I could do with rake in 5 minutes. But adding a Rakefile to the project for just one task is lame! Dutifully, I popped open our 2k line build.xml and nearly gagged. Rake it is then. But how do I keep our build consistent? One way would be Re-implement our entire ant build in rake. Crazy talk! I needed a compromise. Thinking about it for a second I wondered, "What if rake could seamlessly use all my ant tasks?"

The Solution:
Making rake ant aware! Now i'll be able to call all my ant tasks as if they were rake tasks.
rake antTask
I can even mix and match ant tasks with rake tasks.
rake antTask rake_task antTask2
Hoo boy, this is gonna be great! But how do we do it? I need something like method missing for rake tasks. Determined to figure this out I checked out the rake source code. Nothing like method missing there, but it turns out just a few addidtional lines of code gives rake ant awareness. Here is what I ended up with in the top of my Rakefile.

#Redefine the way rake works so we can have it check our build.xml
module Rake

# open up the TaskManager to make it try ant before pooping out
module TaskManager
# Find a matching task for +task_name+.
def [](task_name, scopes=nil)
task_name = task_name.to_s
self.lookup(task_name, scopes) or
enhance_with_matching_rule(task_name) or
synthesize_file_task(task_name) or
call_ant_task(task_name) or
fail "Cannot find task called '#{task_name}'"
end
def call_ant_task(task)
return nil unless system("ant #{task}")
#return a do nothing task because ant is doing the work here.
Rake::Task.new(self, self)
end
end
end



Commentary on the solution:
This solution is a bit of a hack and a better way might be to handle the ant tasks with their own class like the FileTask class does it. But my special behavior is so simple that at this point it might be overkill. Anyway, this works for now.

The Limitations:

It is important to note that a major limitation of this is the inability to pass parameters to your ant tasks. We don't have many ant tasks that you have to pass parameters to (thank you build.properties), so I'm not going to worry about this right now. But going forward it would be nice to see that work. The problem is that rake parses the command line options before it initializes your Rakefile, so I can't overwrite the handling of the command line options from a Rakefile. Too bad. I am going to post a question on the Rake-devel mailing list to see if anyone there has some bright ideas on this, and I might whip up a patch for rake that would make this possible, though I haven't really thought through everything yet. Another limitation is that each ant task is a seperate call to ant, so if you run multiple tasks that depend on the same task that prerequisite task will get run multiple times. That could also be overcome by just storing all the "unknown" tasks as a list and making one ant call at the end. I leave that as an exercise for the reader. :)

Now you will go forth with the power and simplicity of rake without losing your investment in ant!

Wednesday, June 27, 2007

OSCON 2007


Look! Pimp My Build! My first conference presentation ever! I'm really looking forward to having some fun with this. The idea behind the presentation is to share some "pimp" (in the parlance of our times), uber-useful CruiseControl customizations. Andy Slocum is my co-presenter. I don't want to give too much away (read: we haven't written the presentation yet), but some of the customizations we will describe include: adding Lucene search to your build, writing plugins, eye-popping build status indicators and custom reporting.

Our original concept was to talk about all sorts of software development tools and how to extend them such as Cruise, Eclipse, Rails, etc... But that seemed way too ambitious for a 45 minute presentation. We had a funny title for that one: "Andy and Josh, (a couple of) open source tools." Good thing we refined the topic, eh?

Tuesday, June 26, 2007

How to add search domains in Ubuntu


I have lived with the following annoyance for the past year. At work I can't resolve various server names without editing the /etc/resolv.conf file and adding the following
search www.example.com
No big deal, except every time you connect to a new network this file is automatically overwritten with information received from DHCP. This is really annoying and after adding that line by hand for a year I finally got a solution!

It is really simple. Edit /etc/dhcp3/dhclient.conf and add the following line in there:
prepend domain-name "thoughtworks.com ";
duh! Many thanks to my friendly neighborhood sysadmin and this webpage

Wednesday, June 13, 2007

Fixtures Documentation out of date.


I just wanted to mention that this documentation from the rails api is confusing and out of date. I should probably figure out how to update it myself. That is on my list of things to do, meanwhile maybe somebody else will get there first. As of rails 1.0 instantiated fixtures are disabled by default, so you can't just say @users.joe.name to get Joe's name. You need to do users(:joe).name. If you want the first type of syntax you need to set self.use_instantiated_fixtures = true. This sure confused me. There is a really nice writeup on this stuff and the performance implications here.

Are Rails Test Fixtures Good or Evil?


First let's talk about just what the heck a fixture is. In real estate they are defined as follows.
Accessories fixed to structures or land in such a way that they can't be independently moved without damage to themselves, or the property housing them.
Doesn't sound like something you want in your software does it? But I've used fixtures plenty! They aren't that bad. They are just an easy way of setting up test data. The reason for this write up is that recently I have heard lots of negative things about fixtures and I wanted to figure out for myself if they really are evil.

Now I always liked fixtures because I like my tests to be short, clean and DRY. Fixtures are a very cool, built in way to do that, and they can really speed up your test writing, which is great because it lowers the barrier to writing more tests. So fixtures keep things DRY, but at what price? Jay Fields has an interesting article that suggests tests need not be DRY. Jay argues that isolation of tests is more important than clean, dry tests. I call this the "Every test for itself" philosophy of testing. Here is a quote from that article.
It means that when writing tests you should think about them in isolation. They run in isolation and the chances are when they break (if you write robust tests) they will be fixed in isolation; therefore, abstracting anything that is 'common' actually hurts maintainability. With every test, pretend it's the first and only test you are writing in that class. If that were true, it would be hard to justify those setup or private helper methods.
This is a totally awesome observation and agrees with my own experiences. As I type this I am thinking of all the convenience methods I have put into test classes. Whoops. Well, my tests were ultra clean, short and DRY tests for at least one day. The next day somebody who didn't notice my convenience method went ahead and re implemented it. Sigh.

Still, although I am going to try going without them for my next few projects, I think fixtures are convenient and when used properly there is nothing evil about them. So how do you use fixtures properly? Well perhaps it would be simpler to describe how to use them improperly. Here is a list from Tom Preston-Werner's RailsConf 07 talk on what you don't want from your fixtures.
1. swampy (shows photo of swamp with trees with bad smells with stink-lines)
2. fixtures become an unmanageable mess
3. hard to keep track of links between data
4. things aren't easy to refactor
5. no namespacing – i.e. you need to have lots of users in different states for good testing
6. brittleness – one day you add a new column to a table, and half of your tests fail
7. no validation – there is no automatic way for ActiveRecord validation for fixture data
8. contamination – tests pass independently, but fail when running all at once
9. performance – fixtures can be slow
In practice all these, except maybe number 3 can be avoided. Gripes 4 and 6 are easily fixed by the db:fixtures:migrate task. 2 and 8 are just a matter of being disciplined about writing, updating and using your fixtures. I'm not sure what swampy means, but it sure sounds bad. 9 can definately be a problem, especially if you are not using transactional fixtures (and if you aren't using a transactional database you aren't), but any test suite that uses the database can have that problem.

In the end it comes down to what you are comfortable with. Decide for yourself. If you can't live with Jay's "every test for itself" philosophy, then maybe fixtures ARE for you. Just keep clear of the 9 pitfalls Tom mentions and you will be fine. Remember it matters how you test, but the most important thing is that you test. Quoth Testivus

An imperfect test today is better than a perfect test someday

The perfect is the enemy of the good.

Don’t wait for best to do better.

Don’t wait for better to do good.

Write the test you can today.


The problems of maintaining a large suite of unit tests is considerable. However, the benefits of a test suite, even a troubled one, far outweighs the costs.

References:
  • http://manuals.rubyonrails.com/read/chapter/26
  • http://clarkware.com/cgi/blosxom/2005/10/24
  • http://api.rubyonrails.org/classes/Fixtures.html

Tuesday, June 12, 2007

How to Migrate Fixtures in Rails

4/1/2008 - Update: check out this writeup on the fixture migrations for a more in depth analysis of the plusses and minuses of our code.

I did some work on a Rails app awhile back and we were using Cerberus for Continuous Integration. The build was in good shape. All tests passing. We turned it over to another team, but they never setup CI. Guess what happened? If you said, "The tests got stale and broken." then you are a winner. Quoth Testivus:

Tests not run waste away

Run your tests often.

Don’t let them get stale.

Rejoice when they pass.

Rejoice when they fail.


Looking at the failures it became apparent that many of the problems had been caused because of the fixtures being used. The model had been changed with migrations, but the fixtures were still old. That's easy, just change the fixtures. And I did. Yuck! Changing all the fixtures turned out to be a mind-numbing task! Consider this. Your fixtures are nothing but a representation of the data in your test database. That representation is dependent on the structure of your database. By manually changing my fixtures to use the new DB structure I had done a migration. By hand. Yuck!

I knew there was a better way, and so Jake Scruggs and I joined forces to tackle this problem. Our solution is a new rake task called db:fixtures:migrate, and you can get the code here To make it work, we have introduced the concept of a version to our test fixtures, and this makes sense because our fixtures are essentially a serialized, human readable version of the database, and in Rails the DB has a version, right? Right.

I haven't tested this task on every imaginable case, but it has worked for me on a couple of my own little projects. That is cool. Let me now bring up a side point. In learning about some of rails rake tasks I came across some thing I thought was ugly (please enlighten me if there is a reason for this). When you run tests, rails completely wipes the test db (which is good), but then instead of rebuilding the schema with migrations it just copies the schema from the development DB (which is bad). To me this is ugly, and it can cause headaches if you aren't watching out, because now your test DB depends on the state of your development DB. Two lines of code fix this, but I wanted to hear some old, wizened Rails gurus opinions on this before I went crazy with this idea. If you have a look at our rake task you can see that when we setup a test DB we use migrations to generate the schema. This is also good because if you don't run your migrations often, how do you know that they will work? You might as well run them every time you run tests. So go try it. Use it. And say what stinks.

Next up: Maybe I shouldn't use fixtures at all.

Monday, June 4, 2007

Bullet Points and Bullets, or How Television Killed Democracy


The first rule of political discourse is to be as vague as possible. George Bush, the master of this technique (or just a natural), began a war in Iraq March 20th, 2003. For two and a half years he kept the war machine running strong by sticking doggedly to this rule, saying nothing but vague yet decisive sound bytes.

I'll summarize his strategy in those years: Go, fight, win.

Good plan. Let us hope that political speech is completely bankrupt, because for political speeches to get any more meaningless or pedestrian than they already are, politicians will have to use gibberish (which I think Bush is working on).

November 30th, 2005 Bush finally put out his official strategy guide for achieving his goals in Iraq, following all the bullets with bullet points. I'm going to leave the discussion of his strategy guide for another day, but I'll say that if you are familiar with any video game strategy guides, you will no doubt be disappointed with this one. You don't stand a chance of saving the princess with it.

I'm getting sidetracked though, this isn't supposed to be about Bush. What I was really thinking about was political speech. Political speech is empty, duh, but why is that? I think if anyone is to blame it is the media, especially television. Television's tendency to sensationalize and take things out of context is poison to a democracy. A fundamental idea in democracy is that no single person has all the answers. We have to have a forum for discussion, get out different ideas and vote on the best one. This doesn't work if nobody can say anything without getting whacked over the head with a newspaper, and in Pavlovian fashion people avoid saying anything. Ideally a leader when asked a question will answer the question openly, and the media rather than trying to exploit the answer for a story can sensibly point out weaknesses and allow the leader to adjust their position accordingly during the course of the discussion. Instead, our leaders stand up at press conferences, which are a sort of TV-era running of the gauntlet.

I think I could talk about that for a long time, but I won't. I just wanted to mention a potential light at the end of this tunnel. Before Television media our democracy was still a place for people to have real debates, and ideas would go forth and do battle. Then television ruined it (go read Walter Cronkite's autobiography for more on this). Now that the internet has begun to revolutionize the media I hope that we can reclaim our democracy as a staging ground for good ideas. The internet, with its instant access to contextual information (think Wikipedia) might be just the forum for politicians to begin speaking again. The question is, who will be the first to ditch the double talk?

Down with television!

Saturday, May 19, 2007

What is your Chicago music IQ?


Chicago is a musical town, and after a year of living here I am just beginning to realize how many great musical careers intersect with the Chicago scene. Chicago's unique styles of blues, soul, jazz and hip-hop have all made me smile at one time or other. This post is a hats off and thank you to the musicians of the city of broad shoulders. Thanks!

And now a celebration of Chicago music through trivia. What is your Chicago music IQ? Did you know that Sam Cooke and Lou Rawls went to High School together here, or that both Wilco and the Smashing Pumpkins got started here? I didn't, but now I do and I decided to write a quiz to publicize some interesting facts. The study guide is first: a list of artists and links to them on Wikipedia. Study up before you take the quiz below.

The Quiz:
  1. Which two singers grew up in chicago and were in a 50's gospel group called "The Teenage Kings of Harmony?"
  2. What chicago band features the landmark Marina Towers on the cover an album?
  3. Who is the chicago filmmaker, producer and musician with an album cover featuring a naked reclining asian man getting fellatio from a small rabbit?
  4. Which Chicago born jazz drummer was the first person to use a bass drum pedal in 1927?
  5. Who was the Glendale heights kid and guitar virtuoso who's father, a jazz guitarist, refused to give him guitar lessons because he doubted his commitment?
  6. What artist got his nickname from his childhood fondness for playing in the mud? (Yeah, this is super easy, but an interesting fact.)
  7. What Chicago born music producer produced the 2 biggest selling albums of all time, and is the only african-american film producer to have been nominated for an oscar in the category of best picture?
  8. Who attended high school in Chicago (and Topeka, Kansas) before becoming the first influential jazz musician to use the tenor saxophone?
  9. This Chicago based musician was one of the first mainstream musicians to use an Apple computer in making music. His 1980 album featured sounds from an Apple II.
  10. Born in Chicago this singer's nickname is the Velvet Fog.
I guess you get a point for each of these questions. So the highest possible IQ is 10! Go brains!

Thursday, May 17, 2007

RailsConf 2007


This is my pre-RailsConf post. I've just arrived in Portland and am looking over the list of presentations for tomorrow. Here are the ones I'm considering. These better be good!

On another RailsConf note. I'm looking forward to hanging out in the ThoughtWorks lounge where they will be writing a Rails app FOR RailsConf FROM scratch. Anyone is welcome to stop by, pick up a story card and do some coding. It will be a good chance to learn some Rails-fu from some experts. I'm also looking forward to some of the announcements that ThoughtWorks will be making regarding pushing Rails into the Enterprise. Most of all I'm just looking forward to learning, hangin, codin, drinkin and playing with my new camera here in lovely Portland OR.

Stickin it to tha man.


Fidelity manages my 401k plan, and up until a few weeks ago I never thought much about it. Then I went to a save Darfur rally and candlelight vigil in Federal Plaza. There were many speakers, and one of the main topics was divestment from Sudan. One speech in particular caught my attention because they mentioned Fidelity as a major investor in foreign oil companies in Sudan. The revenue the Sudanese government is receiving from these companies has directly funded the genocide and continuing civil war there.

That got me thinking and I went to work to stir some of my co-workers up about how bad Fidelity was. Today we had a meeting with our plan representatives from Fidelity and expressed our disapproval. It was pretty satisfying even though it raised some questions about what we could really do about it. Would our entire company pull out of our relationship with Fidelity? Not likely, after all are there any of these big financial companies who are squeaky clean? Do we try and get more socially responsible funds in our plan? We also found out that Fidelity had recently sold 90% of their holdings. That took some of the wind from our sails, but we still spoke our minds, and I like to think that while we might not have directly drove a stake in the heart of this, we at least had something to do with helping divest billions of dollars from companies like PetroChina.

I'm still going to write a follow up letter to Fidelity, who claims that the sale of these stocks was just coincidental. Berkshire, you are next. Now if only I could afford even one share.

Sunday, May 13, 2007

Migrating from Wordpress to Blogger


A general trend in my techno-lifestyle has been the move from do-it-myself, to FREE services. Take my email for example. I used to refuse to use Hotmail or Yahoo! because I didn't trust my communications to some company, and I wanted to use Imap. Along came gmail, and suddenly I could get tons of space, and an awesome client for free. Bye-bye exim and bye-bye Thunderbird. What about storing my photos online? No way was I going to put all my personal photos on some company's servers. Then Flickr began offering Unlimited storage at an amazingly low price, and their software made sharing a joy. Bye-bye jigl and my ever increasing storage woes. I guess what is driving my adoption of these services is the maturity they have achieved. Ajax makes the interfaces useable and with prices hovering just above free nothing else makes any sense.

So I recently realized that I was really not using my virtual server anymore. Google gives you free subversion repositories, free email, and free blogs. So the time had come to kill my server and the only thing standing in my way was migrating my blog from wordpress to blogger. It turns out this is not a straightforward thing to do.

Step 1: see if anyone else had done it. Of course others have done it, but there is no automatic solution (that works). There are dozens of how to documents on moving from blogger to wordpress, but I found only 1 useful for doing the opposite. It also came with a pretty well made Java tool. Here is a link to the tool. Too bad it errored out when I tried migrating my blog. It wasn't open source either so I couldn't try to fix it. I think it is designed to move a wordpress.com blog, not a standalone hosted instance of wordpress. In any case after fiddling with it for half an hour I thought I had better start rolling my own solution.

Step 2: I used mysqldump to grab my WP database from my server and then imported it into a local mysql database.

Step 3: I downloaded the python version of Google Blogger API and used it to create a few dummy posts and comments.

Step 4: I wrote a python script and migrated my entire blog comments and all in about 20 seconds.

Lessons learned:
  • The Blogger API wasn't intended for migrations. You can't set the author of the comments programmatically. They belong to the person you login as. You can't backdate comments, so they appear anachronistic on the site. This is too bad, because there were some interesting folks who had left me comments. I would have liked to preserve that history.
  • Python is a nice language, but I hate, hate, hate the whitespace sensitive nature of it. I also find it a bit less elegant than Ruby. Google, you need to stop pretending that Python is so great and get your API out there in Ruby.
  • I'm never going to use this code again, thus it ain't very reuseable. My unit tests depend on my specific wordpress database. Stuff is just hardcoded in. I totally ripped off some of the sample code that came with the api download. For what it is worth I threw it up on my google code site. Feel free to check it out. It would be easy to make it better, but I don't want to mess with it.

Tuesday, April 10, 2007

SQL Views: quick fixes and complementary ORM


Generally people like to blog about really slick new technologies or smarty-pants ideas they have recently come up with. You don't get too many technology blogs where someone starts off with "I did this totally lame hack today..." That is why I had to come up with these introductory sentences. I had to get comfortable with the idea of sharing a real quick-fix, crappity-crap hack I did today. In my defense this lame hack was the right thing to do, and I promise to eventually right this wrong. Well, enough dancing, on with the details.