2008-12-30

Sneak Attack - Awesome R Resource

Back to flipping out...

2008-12-17

Sneak Attack: world war vi

Back to flipping out...

2008-12-09

An R Cheatsheet

R is seriously cool. Unfortunately for me, I only use it about once a year, so I can never remember how to do anything; hence the cheatsheet.

Read a CSV file into a variable

users_usergroups <- read.csv(file="dev/users-per-group.csv", sep=",", head=TRUE)
Generate a boxplot

boxplot(users_usergroups$NUM_OF_USERS)
Generate a histogram

hist(users_usergroups$NUM_OF_USERS)

Back to flipping out...

2008-12-07

A Python Kōan?

The Zen of Python teaches us Explicit is better than implicit. BDFL GvR made variable declarations implicit.

Back to flipping out...

2008-11-01

Flexjson Hijinks

Flexjson has been good to me, but every once in a while it jumps up and surprises me. Here's an example.

AbstractFoo.java


abstract class AbstractFoo {
        private Float a = 1.1f;
        private Float b = 2.2f;
        private Float c = 3.3f;

        public Float getA() {
                return a;
        }

        public Float getB() {
                return b;
        }

        public Float getC() {
                return c;
        }
}

Foo.java


import flexjson.JSONSerializer;

public final class Foo extends AbstractFoo {
        public static void main(final String[] args) {
                final Foo myFoo = new Foo();
                final JSONSerializer serializer = new JSONSerializer();
                System.out.println(serializer.serialize(myFoo));
        }
}

Can you guess what you're going to get when you compile this and run java Foo? If you guessed:


{"a":1.1,"b":2.2,"c":3.3,"class":"Foo"}

you were wrong. You really get:

Exception in thread "main" flexjson.JSONException: Error trying to serialize path: [ a ]
        at flexjson.JSONSerializer$ObjectVisitor.bean(JSONSerializer.java:603)
        at flexjson.JSONSerializer$ObjectVisitor.json(JSONSerializer.java:471)
        at flexjson.JSONSerializer$ObjectVisitor.visit(JSONSerializer.java:435)
        at flexjson.JSONSerializer.serialize(JSONSerializer.java:222)
        at Foo.main(Foo.java:7)
Caused by: java.lang.IllegalAccessException: Class flexjson.JSONSerializer$ObjectVisitor can not access a member of class AbstractFoo with modifiers "public"
        at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
        at java.lang.reflect.Method.invoke(Method.java:578)
        at flexjson.JSONSerializer$ObjectVisitor.bean(JSONSerializer.java:579)
        ... 4 more

It appears that Flexjson's serialization algorithm can't handle a package-visible, abstract super class. Changing it to public fixes the problem and you can continue on your merry way.

Back to flipping out...

2008-08-23

Syntax Highlighting on Blogger

You may have noticed that my code blocks are suddenly syntax-highlighted. You might think a tech-oriented company like Google would have included something like this by default, but you'd be wrong.

My new syntax highlighting comes courtesy of highlight.js, which is great, although it does have some holes in the English documentation. Unfortunately, I couldn't find any place it was being hosted for free, so I needed to set up a place of my own from which to serve it. Enter Google App Engine.

App Engine is Google's entry into the cloud computing space; if you write your application according to their guidelines, Google will run it on their infrastructure. As such, it is massive overkill for serving up static files, but it's free and it has good reporting tools. So I followed the "Hello, World!" documentation, added the files for highlight.js, then modified my app.yaml to add a handler for serving static files.


- url: /
 static_dir: highlight

Note that this conflicts with the handler from "Hello, World!"; since I didn't want to serve up "Hello, World!" anyway, I just deleted the other handler.

According to the documentation for highlight.js, all you should have to do now is add one simple block of Javascript:


<script src="$PATH_TO_HOSTED_JS" type="text/javascript"></script>
<script type="text/javascript">
 hljs.initHighlightingOnLoad();
</script>

This is technically accurate: this will cause the markup to be modified to support syntax highlighting. That's not enough for the highlighting to take place, however; you still need to bring in the CSS for the highlighting style you want to use (my two favorites are Sunburst and IDEA).


<link href="$PATH_TO_HOSTED_CSS" rel="stylesheet"/>

On Blogger, the best place to put this code is in the raw HTML for your template; that way you don't have to remember to include it in individual posts that will have code blocks. One final note: your code blocks have to be marked up by nesting a code tag inside a pre tag—if you've done something silly like nesting a pre tag inside a code tag, it won't work. Here's an example of the proper style:


<pre><code>
#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-
print "Hello, World!"
</code></pre>

Back to flipping out...

2008-08-12

Grails, The Acegi Plugin, and HTTP Basic

If you've heard of Rails but can't use it for whatever reason—my reason was an incompatible legacy schema—maybe Grails will work for you. Unashamedly inspired by Rails, Grails is written in Groovy and aims to provide the convenience of Rails while adding configurability and leveraging the power of the massive number of libraries written in Java and the other JVM languages (including Ruby, thanks to projects like JRuby). Between what comes with Grails out of the box and the available plugins, it can really take a big chunk of the grunt-work out of webapp development.

One of my favorite plugins so far is the Acegi plugin. It bolts Spring Security onto your Grails project and provides for configuration using the concise Groovy DSL for Spring. Just type grails install-plugin acegi to install the plugin, and start leveraging Spring Security.

One of the nice things about Spring Security is its built-in support for HTTP Basic authentication. Just change basicProcessingFilter from false to true in your SecurityConfig.groovy file and your application will now look for user credentials in the HTTP Authorization header. The only problem is that if no Authorization is sent, the server sends a 302 Found response to send the client to a login page. To be fully compliant with the HTTP Basic spec, it should be sending a 401 Unauthorized response with details on the supported authentication mechanisms. If you've read the documentation for the HttpBasicProcessingFilter—which you enabled by setting basicProcessingFilter = true earlier—you might expect Grails to be using HttpBasicProcessingFilterEntryPoint, but you'd be wrong. In an effort to be user-friendly for human users, it is instead using an AuthenticationEntryPoint that sends the 302. To change to the strict HTTP Basic mechanism, you'll need to rewire the AuthenticationEntryPoint Grails is using by adding the following to your resources.groovy file:


authenticationEntryPoint(org.springframework.security.ui.basicauth.BasicProcessingFilterEntryPoint) {
    realmName = 'YOUR REALM GOES HERE'
}

Back to flipping out….

2008-08-10

Exercises from Section 1.2 of SICP

Here's a quick update from my ongoing study of SICP.

Exercise 1.9

I didn't have access to a scanner to show the results of my expansion, but my answer to the recursive or iterative question was: recursive. The inc function in the definition of + is deferred until the recursive call to + is complete, so the function has a linearly recursive shape.

Exercise 1.10

  • (A (1 10))1024
  • (A (2 4))65536
  • (A (3 3))65536
  • (define (f n) (A 0 n))2n
  • (define (g n) (A 1 n))2n
  • (define (g n) (A 2 n))22n

Exercise 1.11

Recursive

(define (foo n)
  (cond ((< n 3) n)
        (else (+ (foo (- n 1)) (* 2 (foo (- n 2))) (* 3 (foo (- n 3)))))))
Iterative

There were a few false starts, but after Eli Bendersky's tip about solving the problem using a for loop, it all began to fall into place.

for Loop

function fooFor(n) {
    var a = 2;
    var b = 1;
    var c = 0;
    var result = n;
    for (i = 3; i <= n; i++) {
        result = a + 2*b + 3*c;
        c = b;
        b = a;
        a = result;
    }
    return result;
}
Tail-Recursion

(define (bar n)
  (cond ((< n 3) n)
        (else (bar-iter 2 1 0 n))))

(define (bar-iter a b c count)
  (cond ((= count 2) a)
        (else (bar-iter (+ a (* 2 b) (* 3 c)) a b (- count 1)))))

Back to flipping out…

2008-08-02

cd maven2 && mvn archetype:create fail-whale

I like the premise behind Maven, I really do. In the real world, though, Maven is an attractive nuisance: it lures developers in with its promises of an easier, consistent build process and quality project documentation, then ambushes them with constant breakage.

My latest brush with Maven breakage came this weekend when I tried to use the Archetype plugin. The archetype plugin creates a new project from an existing template. Typical archetypes do things like create the necessary directory structure and download dependencies, and some go as far as creating source code for a functional skeleton, e.g., the AppFuse Archetypes.

Sounds great, right? Well, it would be if it worked. Instead, when I tried to use the archetype plugin, it started downloading dependencies and broke. It turns out, they released my version of the plugin with dependencies that don't exist in the central repositories (specifically, commons-collections version 3.2 and commons-lang version 2.1). This is fairly widespread among third-party maven projects, but I didn't expect it from something as central as the archetype plugin.

I was irritated, but I wasn't ready to give up. Luckily for me (I thought), the error message suggested a workaround: manual installation of the dependency into your local repository. It even provided instructions: mvn install:install-file -DgroupId=commons-lang -DartifactId=commons-lang -Dversion=2.1 -Dpackaging=jar -Dfile=/path/to/file and mvn install:install-file -DgroupId=commons-collections -DartifactId=commons-collections -Dversion=3.2 -Dpackaging=jar -Dfile=/path/to/file. The straw that programmer's back came when I tried to follow those directions.

[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Error installing artifact 'commons-collections:commons-collections:jar': Error installing artifact: /Users/hankgay/.m2/repository/commons-collections/commons-collections/3.2/commons-collections-3.2.jar (No such file or directory)

At this point, I didn't have the energy to dig any deeper, but that error message is monumentally frustrating. Of course /Users/hankgay/.m2/repository/commons-collections/commons-collections/3.2/commons-collections-3.2.jar doesn't exist; the whole point of the command was to create it!

The moral of the story? Maven is the sort of tool that only a masochist could love.

Update

I really wanted to use the maven-archetype-webapp, so I started futzing about with those dependencies again. Eventually I fixed the problem by manually mirroring the proper versions from ibiblio. I hope the Maven team works on this soon, because these are some pretty absurd lengths to go to make something work that comes in the box.

Back to flippin' out…

2008-07-28

Structure and Interpretation of Computer Programs, section 1.2

Today's post is just a brief overview of Section 1.2, but don't worry: I'll be covering the (numerous) exercises soon. Section 1.2 covers a lot of ground in a little space. It introduces the ideas of recursive processes (both linear- and tree-recursive processes, a distinction that was new to me, but blindingly obvious in hindsight) and iterative processes (via tail-recursion), Big O analysis (though it doesn't call it that), and mentions probabilistic methods in passing. I was a bit surprised at the early introduction of Big O; even though I consider it one of the more important things I learned in school, my intro CS course didn't cover it until the end of the quarter, and the coverage was hardly in-depth.

Linear vs. Tree Recursion

This one is actually pretty straightforward: any time you only need to recurse once on each pass of the process, you've got linear recursion; if you need to recurse multiple times, you've got tree recursion.

Big O Analysis

SICP calls it order of growth. The basic concept is to analyze your algorithm/process/whatever in terms of one aspect (typically time or space) and determine how that aspect grows with respect to some aspect of the input (typically, the number or size); think limits from math class. For instance, the running time of BubbleSort grows proportionally to the square of the length of the array/list being sorted, so its Big O is O(n2).

Probabalistic Methods

The basic idea is that often a tight-enough approximation, which isn't even all that tight in a lot of cases, is just as good as an exact answer, and a lot cheaper to compute. There's a lot of ongoing research concerning probabalistic methods right now.

Back to flipping out…

2008-05-12

SchemaSpy: A Maintenance Developer's Best Friend

If you've ever joined a team that maintains a large legacy database, then you've probably wished for something like SchemaSpy. There are a lot of tools that let you get visualizations of a legacy database, but what sets SchemaSpy apart is that it generates HTML output so you can run it once and then view the results in your browser instead of having yet another desktop application open. I like to tell people it's sort of like Javadoc for databases.

There is one small problem, though, at least if you want to use it on an Oracle database—the terminology from the SchemaSpy documentation doesn't exactly match up with Oracle terminology. Here's an example of the command to invoke SchemaSpy against an Oracle db: java -jar schemaSpy.jar -t orathin -u username -p password -o schemaspy-output -cp path-to-ojdbc14.jar -db sid -host localhost -port 1521 -s schema-name

Back to flipping out...

2008-04-25

Sneak Attack: BigMac Programmers

Moral of the story: Don't do it—don't be that guy. People hate that guy.

Back to flipping out...

2008-04-24

You Didn't Think I Had Forgotten, Did You?

This is the first post in my long-promised series of follow-up posts to The Education of a Programmer. I must have started this post at least 5 times, and each time I read the first sections of SICP. I can't really come up with words to do it justice—Abelson and the Sussmans describe the core concepts of computer programs in the most concise yet understandable way I've ever seen. Since I can't improve on the original, these posts will just be a sort of running diary as I work through the exercises in the book. And without further ado...

Exercise 1.1

In this exercise, we are asked to manually evaluate each expression as if we were the interpreter.

Results from SICP Exercise 1.1
My Answer Interpreter's Answer
10 10
12 12
8 8
3 3
6 6
(this was sort of a trick question - the interpreter doesn't print anything when it evaluates define expressions)
see above
19 19
#f #f
4 4
16 16
6 6
16 16

Exercise 1.2

In this exercise, we are asked to convert a mathematical expression into prefix notation.


(/ 
   (+ 5 4 
      (- 2 
         (- 3 
            (+ 6 
               (/ 3 4)))))
   (* 3 
      (- 2 6) 
      (- 2 7)))

Exercise 1.3

In this exercise, we are asked to define a procedure that takes three numbers as parameters and returns the sum of the square of the two largest parameters. If you were starting from scratch, the solution would look something like this:


  (define
    (sum-of-squares-for-two-largest a b c)
    (cond ((and (< a b) (< a c)) (+ (* b b) (* c c)))
          ((and (< b c) (< b c)) (+ (* a a) (* c c)))
          (else (+ (* a a) (* b b)))))

But that's not very clean. There's too much repeated logic, and it's not very readable; we need some abstraction. The most obvious abstraction is to create a square procedure that looks something like this:


  (define
    (square a)
    (* a a))

Using this new procedure, we can rewrite our sum-of-squares-for-two-largest procedure like so:


  (define
    (sum-of-squares-for-two-largest a b c)
    (cond ((and (< a b) (< a c)) (+ (square b) (square c)))
          ((and (< b c) (< b c)) (+ (square a) (square c)))
          (else (+ (square a) (square b)))))

That's better, but there's still room for improvement: we can create a sum-of-squares procedure that looks like this:


  (define 
    (sum-of-squares a b) 
    (+ (square a) (square b)))

Look at that: we created the same procedures that the authors walked us through creating earlier this chapter. Using our new sum-of-squares procedure, sum-of-squares-for-two-largest looks like this:


  (define
      (sum-of-squares-for-two-largest a b c)
      (cond ((and (< a b) (< a c)) (sum-of-squares b c))
            ((and (< b c) (< b c)) (sum-of-squares a c))
            (else (sum-of-squares a b))))

Exercise 1.4

In this exercise, we are asked to describe what the following procedure is doing.


  (define (a-plus-abs-b a b)
    ((if (> b 0) + -) a b))

This procedure is summing a and the absolute value of b. The (if (> b 0) + -) tells us that the procedure being applied to the arguments will differ based on whether b is positive or non-positive. When it's non-positive, b will be subtracted from a, which is functionally equivalent to always adding the absolute value of b to a.

Exercise 1.5

Applicative-order
This will result in an infinite loop, because the interpreter will attempt to evaluate p, which is recursive and has no exit conditions
Normal-order
This will return 0 because the interpreter will evaluate the if before deciding whether to evaluate p or return 0, and the results of the if will indicate it should return 0

Back to flipping out...

2008-04-15

Test Suites as Quality Diodes

Larry O'Brien recently wrote a a post about relative sizes of tests vs. the code they exercise wherein he mentions an interesting concept: tests as quality diodes. For those of you who've forgotten how diodes worked, or never learned in the first place (lucky you), here's a decent introduction to diodes.

The immediate point of the metaphor was obvious - code that passes tests is quality, code that fails tests is not. But once upon a time, I was expected to know about the inner workings of diodes (thanks, ECE 3040), so I've taken the metaphor and run with it. To start with, I'll be comparing entire test suites to a single diode.

Warning: The following paragraphs contain scenes of graphic metaphor-stretching. A metaphor may have been harmed in the writing of this post.

One of the distinguishing characteristics of diodes is known as peak inverse voltage. If the voltage across a diode is going the wrong direction (a situation called reverse bias) and exceeds the diode's peak inverse voltage, then you get an unpleasant effect known as avalanching. This is pretty similar to what happens when you try to slap a test suite on a lot of traditional applications. Test suites are traditionally expected to be passing most of the time, and the code they exercise is supposed to be more good than bad (or it wouldn't be passing the tests most of the time). If instead you have more bad code than good, you've reverse-biased your quality diode. If there is pressure to get this dev-task done and get back to work—after all, it's not like you're adding a feature to the product—then you may have just exceeded your test suite's peak inverse voltage. The resulting avalanching tends to gut your test suite, either because you stop running it (you already know it will have failures) or because you throw it out entirely (why maintain code that isn't doing anything for us).

What should we do about a situation like this? In the world of electronics, one solution is the Zener diode. Instead of avalanching, it breaks down in a more controlled fashion, though it usually does so at much lower inverse voltages. We'd like our test suite to exhibit this same resistance to avalanching—a Zener test suite, so to speak.

One way to build a Zener test suite would be to reduce your test coverage. Sure, the first casualty will likely be the code that needs the most help, but some test coverage is better than none, and none is what you wind up with if your test suite starts avalanching. Once you have your Zener test suite in place, you can work on transforming it into a more traditional test suite by slowly increasing the coverage. That's not entirely accurate, though: if you do it right, your test suite will grow to have the benefits of the classic test suite while retaining the resistance to avalanching of the Zener test suite, and that's our real goal.

Back to flipping out...

2008-04-08

CSV from Oracle

Ever wanted to dump data out of Oracle and into a CSV? Perhaps you needed to export some info to QA or create a chart. Have no fear, there's a surprisingly straightforward way to do it. I know: Oracle and straightforward don't appear in the same sentence often, but in this case it really is simple. Just use set colsep , and you're in business.

Back to flipping out...

2008-03-27

How do you counter an Acid(3 test)? With a Base(3 test)

Lately, the WebKit and Opera devs have been racing to see who can make their browser pass the Acid3 test first, or at least be the first to get 100/100 (for more details on why this is not the same thing, please visit the earlier Wikipedia link). Notably absent from this entertaining contest was Firefox. If you're saying "Hold on! They're probably just trying to get Firefox 3 out the door," you're right... sort of. While that is supposedly the main argument put forward by shaver in his recent blog post on Acid3, it takes less than 2 paragraphs to completely exhaust that idea and 7 (mostly massive) paragraphs to bag on the Acid3 test for focusing on the wrong things. Meanwhile, Rob Sayre dismisses the test in his blog post on the subject without even offering the excuse (which I believe is totally valid) that Firefox 3 is the top priority. So, since it seems to be the consensus among Firefox devs that Acid3 focused on the wrong things, why don't they publish a test that they think focuses on the right things? Dan_Farina over at the programming subreddit suggested this "Base3" test and I think he's right.

Back to flipping out...

2008-03-20

Sneak Attack: More Performance Tips from YAHOO!

Back to flipping out...

2008-03-05

C/C++ Programmers

Thanks for suffering so I don't have to. - paulzork said it on the programming subreddit about C/C++ programmers, and I don't think I've ever seen a better summary of my feelings for C programmers.

Back to flipping out...

2008-03-04

JavaScript Idioms - Copying an array

Ever see a block of JavaScript that looks like this?


var myArray = [];
for (var i = 0; i < source.length; i++) {
    myArray[i] = source[i];
}

That's the popular way to copy the elements of one array (note the lack of capitalization) into a new Array (note the capitalization - we'll get to this in a minute). I wasn't really a fan, at first, but it's almost as pervasive as

while(*dst++ = *src++);

in C, so eventually I decided to stop worrying and love the bomb. Basically, JavaScript will automatically grow your Array for you, so you can dispense with all the sizing you might expect when using something called an Array.

Now for the notes you took on capitalization. Not everything that acts like an array in JavaScript is actually an Array. One example is the arguments property available to Functions. If you happen to need the stuff in arguments as an actual Array (because you want to use it as a key for a map/dictionary/hash, let's say), you will find yourself writing some code like that above. Just remember that sometimes stuff is "Array-like" without actually being an Array, because it can really bite you if you're not careful.

Back to flipping out...

2008-02-22

How Do You Balance Different Target Audiences With Different Needs

The Problem

Different groups of your users have different ways of handling the same basic tasks and/or data. Names and addresses are just two of the more common types of data that vary from culture to culture. So how do you handle this situation? The immediate impetus for this post is this programming.reddit comment about names. I can see at least 3 different approaches, each of which has drawbacks.

The Strategies

Ignore the needs of the smaller target audience(s)

This is the one I've seen most often in the wild, and I can see it's appeal: if you are only giving up a few potential users/customers, you'll probably offset your losses by increasing your saturation in the supported target audience(s). Still, I always get a vague feeling of unease when I knowingly make it harder/impossible for a group of people to use my work.

Limit your functionality to the common subset

I don't know that I've ever encountered this, and I can see why. When you get right down to it, there's not much about this world that is constant across all places, people, and cultures. That doesn't leave you a lot of functionality to put into your app. Even when there is a common subset, your different target audiences will probably still prefer to use an application/site that supports the nuances they are used to, and nuances are the first thing to go when you start chopping functionality down to a common subset, pretty much by definition.

Hide the functionality that is subject to change until you know which variant to expose

There are a couple of problems here:

  • You may not even need the information for anything else, and users are famously reluctant to give out personal info. If you do ask them for the information, please keep Application Design Mistake No. 9 in mind.
  • It may not be feasible to hide the functionality in question. If your application/site doesn't require users to register, for example, then it might be hard to keep track of the information necessary to know which variant of the functionality to expose.

The "Solution"

In reality, I imagine most developers would prefer some mixture of the strategies listed above, though I could be wrong. What does your solution look like?

Back to flipping out...

2008-02-18

Sneak Attack - Crunch Mode

Sneak Attack: The Crunch Mode Paradox

Back to flipping out...

2008-02-13

Buildix in Anger, part 2 (follow-up)

According to the response on the forum, you basically can't do what I wanted to do (unless you feel like reverse engineering the webapp, which appears to be obfuscated), but Mingle 2 will have an API that should allow it.

Back to flipping out...

2008-02-12

Buildix in Anger, part 2

Something to note: the Buildix project creation logic uses the blank Mingle template. If you want to use a template, such as the Scrum template, you'll have to create a Mingle project manually. Originally, I was going to walk you through the process for modifying Buildix to use the template you told it to use, but I couldn't figure that one out in time for this post. I've posted a question to the Buildix forums, and if I learn anything further, I'll update this post accordingly.

Back to flipping out...

2008-01-16

Using Buildix in Anger

About Buildix

Buildix is an all-in-one solution for most of the essentials of agile software engineering:

Buildix also offers an optional software package for Project Management: Mingle. Mingle is the for-pay PM software created by ThoughtWorks, who also created Buildix. While Mingle isn't free, there is a currently a free 30-day trial (or 1 year if your team contains 5 or fewer members). To tie it all together, the ThoughtWorks team has glued things together to make these tools integrate more seamlessly.

Buildix itself is available in a number of form factors: VMWare image (which I will cover in this post), LiveCD, AMI for Amazon's EC2 service, or .deb package for Debian-based Linux distributions. The VMWare image and LiveCD are both Ubuntu with the Buildix package installed.

Installing Buildix

We'll be discussing the VMWare image of Buildix 2.0.1 in this post, so installation is pretty straight-forward. Just download the bzipped tarball, extract the image to your directory of choice, fire up VMWare, and choose to open the image. Voila! VMWare tells me that the VMWare Tools are out of date, but I'll deal with that later. It is important to note that the VMWare image is not secured in any way. If you plan to allow access to the image from outside your firewall, ThoughtWorks recommends creating your own secure Ubuntu server and then installing the Buildix package via aptitude.

Configuring Buildix

Figuring out how to access Buildix

The first step is to figure out how to access your new Buildix install, since it's hard to configure something when you don't know where it's at. Pop over to the console in VMWare and attempt to log in as ifconfig, no password. This should display the IP address you should use to access Buildix. As an important aside, note that there are two default user/password combos, and that the password is the same as the username for both: root and cruise (see this forum post about default users).

Enabling Mingle

When you first visit your new Buildix install's site, you'll receive a message informing you that "Mingle is installed but not configured." So, click on the handy link to finish the installation (or you could uninstall it, but Mingle is reputed to be cool, so we'll be finishing the installation).

The first step in finishing up the installation is a simple overview page that outlines the steps we'll be taking. Since this is Buildix, the first two steps (1. Connecting Mingle to your database and 2. Setting up the database) have been handled for us. Next up is 3. Configuring the SMTP connection. Just fill in the required fields and click through. Up next is 4. Accepting the ThoughtWorks End User License Agreement. If you agree, click the checkbox to enable the button, and click through. Now we see 5. Setting up the first user account - you'll be prompted to create the first user (who, unsurprisingly, will be an Administrator). Create this user and move on to 6. Importing project templates. All you can do here is continue. Then you'll be informed that Mingle is not registered. Go to the website and register to get a registration key in your email. Copy your key, go back to Mingle and choose to register, then paste in the key. You should be all set.

User configuration

Next up: adding your users. Here is the first time you get to really use that ThoughtWorks glue I mentioned earlier. They've added a very simple User Manager tool that will create users for the various applications making up Buildix so that they are consistent, e.g., hgay can log in to all of the applications with the same credentials (with the possible exception of Mingle - those logins are created with a default password and you must update them separately). Just go to the address indicated in the ifconfig login indicated above. That should get you to the homepage for your Buildix install. From there, click on the User Manager link, provide the username and initial password, and Buildix takes care of the rest.

Project configuration

Project creation

Next up: adding your project. Here again, we get to enjoy some ThoughtWorks magic. You go to your Buildix homepage, provide a project name, and click the Create Project button, and Buildix will create the projects in the member applications, including a project in the subversion repository. Actually, scratch that. What it does is create a new subversion repository at /var/svn/$project_name, which makes things interesting if your pattern before was to have one repository with many subprojects. And that brings us to our next step: getting your existing project into Buildix.

Old project info migration

SVN Import

Once Buildix has created your SVN project, you can use svnadmin dump and svnadmin load to get your previous work into Buildix. If you are unfortunate enough to still be using CVS, you'll first need to get your project info out of CVS. A tool like cvs2svn should do the trick, but it is rather complicated to get it configured exactly as you might want. I recommend testing against a temporary svn repository until you get it exactly the way you want.

Summary

If everything went well above, you should have some users and projects waiting for you. Now it's up to you to use Buildix effectively.

Miscellaneous

Back to flipping out...