Part 1 of this article was posted on onlamp.com. Due to changes in the format of O’Reilly’s online publications, they are no longer interested in Part 2, so I have published it here. Sorry for the delay but it took some time to contact the editor to learn all this.

In my previous article, I described the process of building a RESTful web API from the server-side. This article will describe how we write the client to the web services server provided in the first article.

The source code for the client and server described in this article is available by saving the following links:

There are some instructions on how to install them in comments in each file, but you’re mostly on your own. I can say that they should work (do work for me) if installed correctly and if the required libraries are in your library path. Sorry that I haven’t made these easier to use.

RESTful Client

The RESTful server written in the previous article helps me track the books in my library. This time, we’ll look at a command-line client that takes advantage of the API. The principles applied to build this client could be used to in your web application to access web services APIs provided by third parties or to build a GUI application or anything else. A command-line just provides a good simple interface with which to demonstrate.

The command-line script is named book and operates through the use of subcommands. For example, to list all the books in the system, you can run:

./book list

In cases where a new book is added or updated, you specify the update using a file name.

./book update 1-56563-833-6 treasury.yml

Without further ado, let’s jump into the code.

Setting up the Client

Before getting into the commands themselves, there is some setup performed by the script. First, we have a constant named HOST. This constant is just the URL of the REST API server.

I’ve also defined to additional helper functions that will show up throughout this article. The first, barf(), is called whenever an error occurs. Since our server returns errors as HTML, it just pulls title of the error from the HTML header and the rest of the error message from the first paragraph. The second helper function is slurp(), which is really nothing more complicated than the function by the same name in File::Slurp (but without an additional dependency, which I was attempting to avoid for the simple demo). All this does is suck in all the text of a named file and return it as a scalar.

The last bit of setup is that I’ve initialized one global variable named $ua for convenience. This variable contains a freshly initialized LWP::UserAgent object. The only library from CPAN this program depends upon is libwww-perl. I’ve also imported some helpers from HTTP::Request::Common, but we’ll get to that in a bit. Now, let’s talk about the interesting stuff.

Listing Resources

The first and simplest command we can run is to simply check to see what resources are available. You can see all the books stored by listing them using this command:

./book list

Calling this command causes the script to run this bit of code:

# GET /=/model/book
my $response = $ua->request(GET HOST.'/=/model/book/id');

# On success, find the link and print them out
if ($response->is_success) {
    my @links = $response->content =~ /\bhref="([^"]+)"/gm;
    for my $url (@links) {
        my ($id) = $url =~ /([\d-]+)$/;
        print "$id: $url\n";
    }
}

# On failure, barf
else {
    barf $response;
}

Our first act is to grab a list of books from /=/model/book/id. As we saw in the previous article, this should return an HTML file containing a list of links as part of the API. If the request was successful, we pull all the links out using a regex and print them out to the end-user. If the request fails, we barf.

The key bit of code here is all within the call to the GET subroutine, which is exported by HTTP::Request::Common. This performs all the extra work we need to build a standard GET request. Another alternative is to use the get() method of LWP::UserAgent, which would look very similar:

my $response = $ua->get(HOST.'/=/model/book/id');

Moving on, I hope I haven’t turned your stomach with how cheaply I’ve parsed the URLs and IDs out. This is not an ideal use of regex, but it does the job. If I were to do this the right way, I ought to use a decent HTML parser (I’m partial to HTML::TreeBuilder) to pull the links out more carefully. Another solution would be to alter the server so that it returned the links as a YAML file and then parse apart the YAML data to get the information I want. I’m lazy and wanted to avoid additional module dependencies at all cost on the demo, so I do it this way.

This will return a list of IDs, which may be ISBNs or just a number, which isn’t terribly useful. A simple enhancement to the system would be to change this to use the “title” field or something else, but we’d also have to enhance the server to support that.

Reading a Resource

Once we know what resources are available by the list, we may then want to check one out to get more information about it. We can do this using the read command. For example,

./book read 1-56563-833-6

As you will recall from the server, we can read data using a GET request to /=/model/book/id/<ID>. This is what we do when the read subcommand is executed:

my $id = shift @ARGV;

# GET /=/model/book/id/[id]
my $response = $ua->request(GET HOST.'/=/model/book/id/'.$id);

# On success, print the file
if ($response->is_success) {
    print $response->content;
}

# On failure, barf
else {
    barf $response;
}

We grab the ID given from the command line and use LWP::UserAgent to GET the book resource we’re interested in. On success, we then print the file out and on failure we barf.

If we wanted to do something more interesting with the resource file like present in a form or place it into a table or something, we’d use a YAML parser to decode the data and then manipulate it.

Creating a Resource

Now that we’ve looked at the resources in our (presumably empty) database, let’s look at how we added them. The first command to consider is, of course, for creating a new book record.

./book create treasury.yml

The command slurps up the file you name and uses it to POST to /=/model/book to create a new book record. We then scan the response to make sure we know what ID was assigned (because, if you’ll remember, not all books have ISBNs, so the server provides an alternate identifier in such cases).

my $file = shift @ARGV;

# Slurp up the contents of the given filename
my $book_data = slurp $file;

# POST /=/model/book
my $response = $ua->request(POST HOST.'/=/model/book',
    'Content-Type' => 'text/yaml',
    Content        => $book_data
);

# On success, return the new ID assigned to the resource
if ($response->is_success) {
    my $url = $response->header('Location');
    my ($id) = $url =~ /([\d-]+)$/;
    print "$id: $url\n";
}

# On failure, barf
else {
    barf $response;
}

As you can see, this is exactly what we’ve done. Since the server requires the data to be specified as text/yaml, we’ve made sure to note that here. If the file is improperly formatted, the server will take note and return an error status, so I’m not being too picky about making sure the data is clean. However, you might want to make sure your data is sane before sending it in such cases just to avoid potential problems.

The other important detail to notice here is how we get the ID assigned to the resource back. We do it by checking the Location header. This is because we’ve build the server to return a 201 Created status with a Location header referring us to the new resource location. If we were to perform an immediate GET on that location, we’d get the resource record we just saved back from the server. In fact, that’s probably what I should do. Instead, I’ve just ripped the ID off of the URL since I know what it will look like. This is a minor breach of the opacity principle of URIs, so if you’re sensitive about such things, I recommend you take the extra step and pull the ID from the returned YAML data.

Updating a Resource

Once we’ve added a resource, we might want to update to correct a typo or submit additional information to the record. We can do this by running:

./book update 1-56563-833-6 treasury-updated.yml

As with the create command, we slurp up the file argument, but instead of a POST, we PUT. We also use the URL included the given ID.

my $id   = shift @ARGV;
my $file = shift @ARGV;

# Slurp up the given file name
my $book_data = slurp $file;

# PUT /=/model/book/id/[id]
my $response = $ua->request(PUT HOST.'/=/model/book/id/'.$id,
    'Content-Type' => 'text/yaml',
    Content        => $book_data,
);

# On success, just announce success
if ($response->is_success) {
    print "Updated $id\n";
}

# On failure, barf
else {
    barf $response;
}

We again make sure to let the server know we’re passing it a YAML data file. Here the returned result is pretty empty, so we ignore everything but the status code and just return a success message. If we had implemented resource renaming on the server using PUT, we would probably need to watch the response for the new resource URI to make sure we fetch the new ID. Since we didn’t, we don’t.

Deleting a Resource

Finally, when a book gets lost or sold or just thrown away, we can delete it from the library. From the command line, this looks like this:

./book delete 1-56563-833-6

Internally, the command is again very simple. This time, we just need to post a DELETE to the appropriate URL, /=/model/book/id/<ID> and check to see if it succeeded or not.

my $id = shift @ARGV;

# DELETE /=/model/book/id/[id]
my $response = $ua->request(
    HTTP::Request->new( DELETE => HOST.'/=/model/book/id/'.$id )
);

# On success, announce it
if ($response->is_success) {
    print "Deleted $id\n";
}

# On failure, barf
else {
    barf $response;
}

First thing to note is that here we build the DELETE request ourselves. This is because HTTP::Request::Common did not provide a shortcut for DELETE when I originally wrote this client. I am happy to say that such a method exists as of 5.814 of libwww-perl. In case you aren’t able to use the latest version for some reason, this is how you live without. Fortunately, it’s not difficult to do this on our own.

Other than that, this should look remarkably similar to the update command minus the slurpy file action.

RESTful Resources

Now that you know the basics of building a REST server and client, you’re ready to move on to bigger and better things. Here are some resources for learning more about REST, for creating and using REST interfaces without building one yourself, for enhancing your REST interfaces, and some existing REST interfaces you might want to model or take advantage of.

RESTful Documentation

If you’re going to build a REST interface or work with one, it is helpful to find some details on what such things are commonly involved with. The most commonly referred to reference on the subject is the REST Wiki. If you want to know the general outline of how some guys that think a lot about REST APIs in the abstract, this is a good resource.

An even more vital resource is the actual HTTP standard. It describes what the various request types and response types are for and how user agents should expect to deal with them. Since REST is tightly bound to HTTP, sticking to the proper behavior in HTTP is very important. Therefore, I recommend becoming familiar with RFC 2616, which helps define the HTTP 1.1 protocol itself.

RESTful Tools

If you don’t want to mess so much with the code and just build on the foundation already laid by someone else, the best tool for the job that I currently know of is OpenResty. This is a stand-alone REST database system. It’s essentially a middleware platform for accessing a database over HTTP and is a pretty good system to emulate.

I’m also partial to the REST plugin provided by Jifty. Just by implementing a few models and actions with Jifty, you get a REST interface to them for free. See Jifty::Plugin::REST for the server side implementation and Net::Jifty for the client.

Both of these tools have a very similar feel to the implementation I’ve built for these articles because I like the style of Jifty’s implementation.

RESTful Extensions

The most significant RESTful API add-on I know of is OAuth. This tool provides a standard mechanism for sharing protected data between disparate services via REST. For example, let’s say you’re building an application that automatically sets the profile photo for several different social networking sites. You don’t want to store these photos, you just want to grab them and update a bunch of social networking sites. You could use OAuth to allow your users to grant you permission to access their Flickr or Picasaweb accounts without asking them for their username and password, which is one of those things that serious privacy advocates go bonkers over.

There are some big players implementing this and I’d love to see more mashups using this rather than asking me for my username/password to get photos or load contacts.

RESTful Services

There are lots and lots of RESTful web services available. However, I will highlight just a few that I have worked with personally in the past.

The first two are by Amazon. Amazon’s Web Services are all REST based, but the one’s I’ve worked with include S3 and EC2. With Amazon S3 you can store files on their services and be charged in micro-payments for just the storage you use and the amount of data transfer (which is pennies per Gigabyte). With Amazon EC2 you can run Linux-based servers that are started and stopped and manipulated using a REST-based service. These are again paid for in micro-payments by the hour (starting around 10 cents / server hour last I knew). There are Perl libraries already available for manipulating both of these without having to know the API directly too (search CPAN for Amazon).

The other I have worked a little bit with is Intuit Quickbase. Quickbase allows you to build database applications with a point-and-click interface. You can then pull and push data in and out of the system using a RESTful interface.

The last one I want to mention is Hiveminder. Hiveminder is a web site for managing your to-do list. Hiveminder provides a number of different interfaces including an IMAP server interface and a RESTful Web API. Hiveminder is built with Jifty and has a special sub-class of Net::Jifty available for accessing the web API, Net::Hiverminder.

Conclusion

There’s more that could be said and more resources I’d like to refer, but I think this article is plenty long. Adding a RESTful interface to a web application is a relatively simple thing to do and is great for giving folks a clean way to access and manipulate your application’s data.

Please feel free to comment here and I will try to answer any other questions or comments.

Cheers.

Every few months, I modify and/or reinvent the ways in which I stay organized. More recently, these inventions have been more like iterative improvements than total rewrites. In the past, I’ve had a calendar online and a task list (most recently managed by Hiveminder), but as I analyzed the way I work, I realized this has inadequately dealt with my work style. I have then tried several solutions over the past month to see if I could find something different that worked. The closest to fitting is still Hiveminder or, if I want to pay $80, OmniFocus. Stikkit also comes very close to what I want.

I considered paying the $80, but I decided that unless the solution was close to exactly right, that money probably wouldn’t be serving me in six months. Since I couldn’t find anything that was compelling enough to fit me the way I want, I’ve decided to put together my own attempt at a productivity tool. After a week of hacking, I’m actually getting to be happy with what I’ve done. I’m going to outline the requirements and then describe the implementation and where I’m planning to go with it. At this time, the tool is strictly my own toy and I have not shared the code, but if you’re interested, I can probably post it to a code repository somewhere.

Requirements

Why did all the other online solutions fail me? They were oriented around the lists of items to do, not around the work I’m doing. If you watch me work (as I’ve tried to do over the past month), you will see me taking lots of notes. I usually have a 10 squares/inch cross section pad in front of me with lots of scribbled notes via a Zebra Sarasa gel-ink pen. These notes usually are used to help me remember what I’m doing for the day or two they are needed and then got tossed in the box to be shredded after I’m done (shredded because work related notes are considered confidential by default).

Simple Journal

So, what I really need is not another to-do list manager, though that’s important, but a journal that works the way my notes do. Therefore, I need the following:

  • Each day I need to be able to create a series of journal entries. Each journal entry is used to log my activities and take notes on things I’m working on.
  • Each journal entry has a start and stop time associated with it to help me track when I worked and how long I’m spending on a project or group of tasks.
  • Each journal entry has a series of comments attached that are timestamped so that I can see what I was doing and when I came across certain notes.

That was the first set of things I did.

Task Management

The next aspect of things is that I need a way to organize a hierarchy of tasks. This was the primary draw of OmniFocus for me. Some of my ideas here have been borrowed from there. When I attack a problem, I state a goal to be achieved. Then, I add a bunch of steps to the goal. Some of these may be broken down further. I need a way to do this. Thus, I came up with the following:

  • I need to be able to group all of my tasks into projects. A project is really just a task at the top level that may or may not have sub-tasks.
  • I need to have a special project at the top-level for being a holding pen for general tasks that don’t really belong to a project at all or don’t yet. (Several task lists managers I’ve looked at include an “Inbox” for this purpose.)
  • I need to be able to add tasks to a project and then be able to freely arrange and rearrange those tasks into groups of tasks.
  • For maximum flexibility, it should be easy to turn any task type into any other task type and seamlessly.

This latter feature is what I would have gotten from OmniFocus for $80. Hiveminder does not handle hierarchies of tasks, though, there are some hierarchical aspects (e.g., dependencies). However, I wanted this tightly integrated with the journal, which brings me to…

Project Management meet Simple Journal

Now that I have each of these separate, I need to cross-communicate between these two features. This is where my organizer will become interesting:

  • Each journal entry should link to a particular project. All the comments on the journal entry should be treated as if they belong to that project and a collection of all the comments should be shown on the project page.
  • Each comment may have one or more tasks attached to it. The purpose here is that a comment might be made noting the completion of a task or might create a new task or might just be noting some progress made toward the completion of that task. Again, each task would have a list of comments that have been attached to it for history and tracking.

The links go both ways here and flowing back and forth between sides of the application should be relatively fluid. At least, that’s the hope.

Implementation

I’ve put together my implementation using Jifty. At this time, it has no access controls at all (I’m the sole user running the web app on my local machine) and I have only themed the work I’ve done and not done much beyond the vanilla Jifty application styling (which is very vanilla).

Journal

Here is a screenshot of the journal:

Screenshot of the Journal Screen

Since it’s just Jifty, it is heavy on the Ajax. By clicking the Start button, a new entry box is immediately visible at the top. The timer immediately starts and the stop time and hours counter update on screen. You can save the fields other than the comment by clicking the Save button after starting and entry and you close an entry by clicking the Stop button. As of this writing, you cannot modify anything but what you see and a stopped entry can’t be reopened. I plan to change all of that, but this is just a few hours hacking so far.

You create new comments by filling in the text box and clicking on the Post Comment button. It’s my intention to make it so that Enter or some keystroke submits, but since I haven’t decided which would be handier or had time, I haven’t messed with it yet. Oh, and the comments can use Markdown formatting in case you need to link or bold or create lists or something.

You can also use the date browser to look at past entries.

Project Manager

Moving on to the project manager, it looks about like this:

Screenshot of the To Do List/Project Manager

Here you create a new task just by typing the text description into the box and clicking enter. This adds a new task to the “none” project. You can then drag and drop (using jQuery’s UI plugin) the tasks around to change them into projects (the whitespace at the top becomes a target for projects), add them as subtasks to another task, etc.

Sorry, I’m not showing off the full power here in that screenshot, but my test site currently has confidential information from work on it. (I’m actually using it with some success).

That’s about all there is to it.

Cross Linking

There’s really not much of this yet. I’ve created the link from journal entry to project so far, but that’s pretty much it.

I intend to make it so that you can add a new task in the entry’s project in a comment by typing:

[ ] A new task

and hitting save. You could place an “x” in the box to mark it as completed. I haven’t yet figured out exactly how I want to handle existing tasks, but I’m thinking of using a nickname format like:

[x] @123

where “123” is the tasks ID. A UI to help look up these task nicknames would be a requirement.

Messing with multiple tasks in a hierarchy could even be manageable via:

[ ] A new task group
- [ ] Sub task 1
-- [x] Sub sub task A
- [ ] Sub task 2

Similarly, I’d like to use a combo box or something to be able to create new projects on the fly or even have a way to make the journal entry name become a new project by prefixing it with a special character.

The Future

In the future, I’d also like to incorporate other activities like automatically pulling SVK commits into comments, exporting the timestamps for use with work related time tracking, exporting certain comments to Twitter, and I’ve even considered sticking a wiki or something on the side that could be used to help collect the notes I want to keep longer term.

We’ll see where it goes. I’m kind of hacking it 5 minutes at a time a couple times a day during breaks between tasks and a couple hours a night and on weekends (about all the time I have to spare for such things). It’s been fun and useful so far.

Cheers.

Well, I’m not going to make it to Yet Another Perl Conference, North America this year as I had hoped. I decided to switch jobs, so I may not be going to any cool conferences this year. However, at my new job, they asked for volunteers for the recruiting campaign. My employer, Grant Street Group, is a Silver Sponsor this year and they hope to find more Perl developers. We’re offering a great opportunity to work from home. I volunteered for the photo op’ (by my wife, actually).

Thus, if you want to see me at YAPC, visit the booth and take a flyer. Be sure to say “Hi!” to the guys there and tell them they made a terrible mistake in not bringing me along.

Just so you know, I wouldn’t have offered to be the poster child if I wasn’t extremely happy with the position. Grant Street is not perfect (who is?), but I was pretty picky about who I considered during my job search. GSG surpassed my requirements. So, if you are in the job market, I highly recommend talking to our guys at YAPC or to Tom Welsch back at headquarters.

Cheers.

This is crazy. Someone must have made a mistake. I just got my work monitor shipped to me today and was plugging it in and testing it. After I did so I could not get wifi to work. Wifi has worked flawlessly from my office using a similar Macbook Pro in the past. The wifi was working for my wife’s Mac Mini downstairs. Why did the wifi suddenly stop? The answer: the monitor is killing my wifi connection.

I still see “4 bars” of wifi on the little gauge at the top, but nothing is going across that excellent connection. I can repeat this experiment over and over and the pings tell all:

64 bytes from 192.168.1.1: icmp_seq=100 ttl=64 time=1.563 ms
64 bytes from 192.168.1.1: icmp_seq=101 ttl=64 time=1.555 ms
64 bytes from 192.168.1.1: icmp_seq=102 ttl=64 time=1.333 ms
64 bytes from 192.168.1.1: icmp_seq=103 ttl=64 time=1.507 ms
64 bytes from 192.168.1.1: icmp_seq=104 ttl=64 time=1.585 ms
64 bytes from 192.168.1.1: icmp_seq=105 ttl=64 time=1.558 ms
64 bytes from 192.168.1.1: icmp_seq=106 ttl=64 time=1.553 ms
64 bytes from 192.168.1.1: icmp_seq=107 ttl=64 time=2.514 ms
64 bytes from 192.168.1.1: icmp_seq=108 ttl=64 time=1.571 ms
64 bytes from 192.168.1.1: icmp_seq=109 ttl=64 time=1.333 ms
64 bytes from 192.168.1.1: icmp_seq=110 ttl=64 time=1075.141 ms
64 bytes from 192.168.1.1: icmp_seq=111 ttl=64 time=78.932 ms
64 bytes from 192.168.1.1: icmp_seq=112 ttl=64 time=5909.617 ms
64 bytes from 192.168.1.1: icmp_seq=113 ttl=64 time=4909.318 ms
64 bytes from 192.168.1.1: icmp_seq=115 ttl=64 time=15571.986 ms
64 bytes from 192.168.1.1: icmp_seq=116 ttl=64 time=14588.171 ms
64 bytes from 192.168.1.1: icmp_seq=117 ttl=64 time=13587.816 ms
64 bytes from 192.168.1.1: icmp_seq=118 ttl=64 time=12587.626 ms
64 bytes from 192.168.1.1: icmp_seq=119 ttl=64 time=11590.435 ms
64 bytes from 192.168.1.1: icmp_seq=120 ttl=64 time=10591.978 ms
64 bytes from 192.168.1.1: icmp_seq=121 ttl=64 time=9592.633 ms
64 bytes from 192.168.1.1: icmp_seq=122 ttl=64 time=8593.659 ms
64 bytes from 192.168.1.1: icmp_seq=123 ttl=64 time=7594.748 ms
64 bytes from 192.168.1.1: icmp_seq=124 ttl=64 time=6595.005 ms
64 bytes from 192.168.1.1: icmp_seq=125 ttl=64 time=5601.815 ms
64 bytes from 192.168.1.1: icmp_seq=126 ttl=64 time=4603.203 ms
64 bytes from 192.168.1.1: icmp_seq=127 ttl=64 time=3603.183 ms
64 bytes from 192.168.1.1: icmp_seq=128 ttl=64 time=2603.059 ms
64 bytes from 192.168.1.1: icmp_seq=129 ttl=64 time=1612.565 ms
64 bytes from 192.168.1.1: icmp_seq=130 ttl=64 time=612.293 ms
64 bytes from 192.168.1.1: icmp_seq=131 ttl=64 time=1.103 ms
64 bytes from 192.168.1.1: icmp_seq=132 ttl=64 time=1.346 ms
64 bytes from 192.168.1.1: icmp_seq=133 ttl=64 time=1.428 ms
64 bytes from 192.168.1.1: icmp_seq=134 ttl=64 time=1.459 ms
64 bytes from 192.168.1.1: icmp_seq=135 ttl=64 time=1.516 ms
64 bytes from 192.168.1.1: icmp_seq=136 ttl=64 time=1.444 ms
64 bytes from 192.168.1.1: icmp_seq=137 ttl=64 time=1.599 ms

You can see my normal pings to my router are around 1.5 milliseconds. The hop up to 1 second happens at icmp_seq 110 when I plugged the monitor in. You’ll note from there that nothing got out until I unplugged it after about 15 seconds. You can tell by looking at 115 to 130. The first, 115, is one second higher than 114. This pattern continues down until 130. They’re one second apart because that’s how fast ping was firing them. Nothing got out while the monitor was plugged in, but they immediately got out and were all responded back to at the same time as soon as I unplugged the monitor.

This is a very interesting phenomenon and one I’ve never encountered before.

Weird.

Update June 8, 2008: Wireless-N to the rescue. I bought a LinkSys WRT160N to replace the wireless of my LinkSys WRT54G and my problems are solved. My wifi access is considerably faster to boot. Cheers.

I am now almost finished with my first week of orientation at my new job with Pittsburgh-based Grant Street Group. I’m going to be doing some software development on one of their product lines and have spent much of the week in training to use the product. I’ve also spent a lot of time reading policy, learning development practices, and digging through the code. I’m hoping that I can start digging in and actually working in the near future. I’ve gotten to meet several members of the staff as well and am very excited to be working here.

Now that I’m no longer looking for work, however, I’m hoping that I can start catching up on my back log of hobby projects that need to get done. This includes some items on the honey-do list (like cleaning the garage which never seems to happen), but I’d really like to get the second half of my latest O’Reilly article published sometime this century. I’ve had at least one person ask where the other half of it is and I really need to get the rest of the text fleshed out and off to the editor (all done, but finishing up the research).

I’m also trying to figure out what other hobby projects are going to be kept and which ones need to be canned now that I’ve switched jobs. I have some loose ends to tie up. One that is likely to be canned is my relationship with Drupal. I think I’m going to have to pass the modules off to other developers (possibly to my former employer) or drop maintenance altogether. I don’t have any need for Drupal at this time with my blog now on Movable Type, no longer working with a company using it, and I’m no longer maintaining the site for New Hope.

I’m still evaluating my relationship with the Jifty project. I really like it, but I’m not sure where it fits in my current work-life balance. I have a couple home toys I’ve written to use it, but nothing serious. I really did like the prospect of helping on the documentation side of things there more, but I’m having trouble really finding time for that. Having a son really sucks time away from being able to do much in the evenings just for kicks and giggles. (That’s not a complaint. I could very easily choose the projects over Gabriel, but who’d want to do that? Gabe is way too much fun to avoid spending time with!)

I guess I’ll see how things shake out over the next few months.

Cheers.

Phew! This has been a whirlwind week and I can now break the silence and talk about it openly. Today, I gave Boomer Consulting two weeks notice and signed my shiny new contract to start work with Grant Street Group. This has been simmering for a bit, so I’ll tell the story of how we got here.

A little more than two years ago I left Kansas State University to give up systems administration to seek a job in software development. I had some pretty strict limits on what I was looking for location-wise (Manhattan, Kansas only) and a friend of mine let me know that his company, Boomer Consulting, was looking for a software developer. The job was to be primarily customizing a Java-based CMS called Magnolia. I interviewed, was offered a position, and took the job. From there we added PHP-based Drupal and some other Perl-based apps to the list of things to do, which was quite a lot for a lone dev. Things at Boomer have been rocky from time to time, but overall it’s been a good experience. In the end, I’m leaving because Boomer is refocusing how it improves it’s software and I no longer feel like I’m a good fit for this direction.

When I determined that things at Boomer were drawing to a close, I began searching for positions. This time around I was a little freer to decide on location, but I still had a very strong desire to stay in Kansas or at least a neighboring state. My two main goals in this job search were to find a job primarily working in Perl, since I’m having fun with it and it’s a focus for me right now, and either a job in Kansas or nearby or would allow me to work from home. I even considered a couple positions out of the area to make sure I wasn’t missing out on anything important, but only if it looked like a really good opportunity.

I talked with a recruiter for a hospital informatics company in Nashville. I also talked with a certain movie database company in Seattle. However, neither opportunity was exciting enough to justify uprooting our family. As much as I love the Seattle area and as much as the housing market in Nashville is appealing, extended family and our friends in town are too important to leave for a so-so job opportunity.

Other than those and the company I’m moving toward, I also spoke with the folks at a certain wiki company in California and a company that develops software for building surveys for Fortune 500 companies. All of these were telecommute positions that would allow me to work almost entirely from home with the possibility of travel to home offices a few times every year.

This past week I actually interviewed in both Seattle and in Pittsburgh (that’s PA not KS, which is Pittsburg without the “H”). This was a lot of flying and odd sleep patterns, which unfortunately ended in a migraine on Saturday. In Pittsburgh, I had a chance to meet with the folks at Grant Street Group and I got a really good feeling from everyone I spoke with. The offer they gave me is exciting and, frankly, better than I really expected. Therefore, I will be learning the ropes for Grant Street Group in the next few weeks.

Anyway, I wanted to let all my friends and fans (all 4 of them, cough) as soon as possible because I’m kind of tired of being discrete about the fact that I was searching for a job.

Cheers.

I read an article at Ars Technica quoting a comment Bill Gates gave to a question during a speech he gave on pharmaceutical research. The answer shows that he is both badly informed and he’s got a very elitist point of view regarding Free Software and Open Source.

His first mistake was making the statement that there’s a difference between “free software” and “open source.” For those of us that know the history of what is most commonly called “Open Source” today was originally called “Free Software” and this “Free” was advertised using the motto: “Free as in speech. Not free as in beer.” Gates mistakenly uses the term “Free Software” to refer to software given away gratis, specifically dropping that they give away their software in developing countries. Of course, he wasn’t referring to “Free Software” as Richard Stallman coined the phrase, but since he mentions Stallman’s license, the GPL, shortly afterwards, it seems awkward that he would do this. This was a mistake and shows that he’s somewhat misinformed on the topic.

Second, he states that the GPL prevents anyone from improving the software. This could not be further from the truth. In fact, Microsoft’s own proprietary EULAs are the licenses that prevent improvement. The whole reason Stallman began the Free Software crusade is because he was tired of finding bugs in proprietary software that he wasn’t permitted to fix because the license forbade it. Any software written in the GPL can be fixed by anyone, anytime. Software under a Microsoft-style EULA may only be fixed by Microsoft or a licensed partner.

Now, what Bill Gates really objects to is the fact that software written under the GPL doesn’t grant him the kind of flexibility he wants to exploit the profits of his software. By holding a lock grip on who may and may not modify the software people depend upon, he’s guaranteeing that everyone has to pay him money to do anything: monopoly.

The GPL allows businesses to compete not only to produce the same piece of software, but to improve it and support it. You can’t get that from Microsoft’s proprietary software. If there’s a bug in the software itself, you have to pay Microsoft to fix the problem. If you don’t want to pay Microsoft, you have to find another solution or workaround the issue, which is what is usually done instead.

Bill Gates might argue, then, that he still isn’t reaping the full benefits of the innovation his company produced. However, I would argue back that if his company still manages to provide the very best support for your software, then that’s who the folks that want the best support will talk to. Plus, his company has the opportunity to benefit from the work of others at very low (i.e., free as in beer) rates. Opening up forces his company to be good at what it does, not just be the sole gatekeeper that we all have to bow to in order to get our work done whether they’re any good at it or not. That’s reality, Mr. Gates.

Cheers.

Sorry for the Sir Mixalot reference there. Ahem. Anyway, I just had a coworker walk by my desk and say something like, “Don’t you just wish we could get rid of all these glitches and be done with them?” She left before I could answer and I think it was rhetorical (it’s hard for me to switch gears to interpret social signals while in the midst of concentrating on code). My answer was going to, “Uh, no. I like finding bugs.” In this sense, I’d agree with what Nat Torkington twittered recently (edited for language, I try to run a PG rated blog):

acid test of whether I’m still a hacker: do I think “oh goody!” or “oh [skittles]!” when I find a bug?

Not only this, but a few years back I worked for a company, NRG, that shared office space with another company owned by John Devore. John and I were chatting one afternoon and we were talking about debugging things. He said that when he finds a bug, he doesn’t just want to fix it. He wants to know why he didn’t find it before: why does the bug work so well except here? That’s certainly my passion.

Debugging is an interesting opportunity to learn. Not only can you learn from the mistake, but you can then use that mistake to actually become an enhancement in the future if it happens to be interesting in some way. Sometimes, you can even end up turning a bug into a feature if it happens to do something really cool with a slight change.

Anyway, I just wanted to post that I enjoy debugging and when I get to the point where I say, “Ah crap” when I need to fix something, it’s time to switch careers.

Cheers.

I’ve been taking DDJ for a couple years now. It’s cheap and occasionally has something interesting in it, but it’s been less interesting than I remember it being when I read it in college. I’ve been much more enamored with the Communciations of the ACM. Today, I received my issue and there’s an interview with Paul Jansen of TIOBE Software. In the article, he’s quoted saying:

Another language that has had its day is Perl. It was once the standard language for every system administrator and build manager, but now everyone has been waiting on a new major release for more than seven years. That is considered far too long.

While I am biased, I have to admit that I disagree pretty strongly with Jansen’s assessment. First, let me go into the problems with how he came to this conclusion and then explain why I think I’m justified trusting that Perl is in it for the long haul despite my bias that would have me think so anyway.

I want to first evaluate the way Jansen has collected the data he’s used to make this statement. TIOBE puts together what they call the TIOBE Index. This is a rating of the popularity of various programming languages. The TIOBE web site claims, “The ratings are based on the number of skilled engineers world-wide, courses and third party vendors.” How do they measure this? By performing a search for:

+"<language> programming"

on 5 popular search engines, including: Google, Google Blogs, MSN, Yahoo!, and YouTube. That’s it.

What they are measuring is not actual popularity, but the amount of hype surrounding each one. Not only are they measuring hype, but only hype that discusses “programming”. What if everyone prefers to say “programming Perl is fun!” That wouldn’t get picked up by the search they use. What about “Perl scripting”? Nope. Missed. (Here I should point out that Andy Lester appears to have been on to something when he gave his lightning talk about Perl programs versus scripts at OSCON last year.) In essence, this is, if they’re disclosing the complete metric, incomplete. It’s a shortcut that might be 90% right or 50% right. This is just poor statistics.

The second aspect of Jansen’s comments I take issue with is the statement that there has not been a major release in seven years. That’s not strictly true. Perl 5.10 has just been released and it includes new features like the new smart match operator. Beyond that, there has been some very active development on a closely related project, Parrot, and language development toward a huge milestone, Perl 6. Furthermore, where Perl truly shines is in all the development on CPAN. CPAN is getting large and complex enough now that we’re having to rethink how it works just so we can find anything on it. This is a good problem to have.

This comment by Jansen does, however, serve to indicate a certain perception gap caused by the long wait for Perl 6. It’s even been considered that the name of Perl 6 is harmful to Perl 5. This has been discussed out by others for some time.

In my opinion, Jansen is on shaky ground with his claims and probably only because he’s not well informed by anything but his own metrics. I should think that he’d at least research the trends and issues facing the top 10 languages listed by his survey as to provide some better justification for it’s accuracy.

As for the reasons I still have warm and fuzzy feelings toward Perl’s future, I can list them off rather easily.

  1. I am participating in a number of growing projects that depend on Perl’s future. Jifty and rethinking-cpan are just a couple I’m involved in. I can point you to several other vital projects that I use or am familiar with.
  2. I know of several companies actively pursuing Perl to develop core projects and continuing to train developers. This includes imdb.com, Socialtext, Best Practical, Six Apart, and several others.
  3. Recently, Google launched Google App Engine. This tool provides services to Python developers as part of the initial release. The top most voted for issues are first to add support for Ruby and second to add support for Perl, as of this writing.
  4. There’s an average of 50 new and updated modules being posted to CPAN every day. That’s not a small number.

I can probably come up with more, but now it’s getting late, so I’d better end this thing. If Perl is going to die, it’s got some years left before it happens. I think there will be enough activity to keep it going and increasing during those years rather than dying.

Cheers.

Google StreetView is cool, but mildly disturbing. Since Manhattan hasn't been cataloged yet and I can't show you my actual house, here's a view of the house I grew up in in Lawrence. If you follow around Lance Court you can see one of the owners since we lived there added a big fence and a pool around back.


View Larger Map

Anyway, I thought it was interesting enough to share with y'all.

Cheers.