Quick Tasks in Nag, Horde's task manager

Horde recently added a new library: Horde_Date_Parser. It started off as a port of the ruby library Chronic, but was completely revamped to support multiple locales. I also added even more tests and several that don't pass in Chronic pass in Horde_Date_Parser.

To show off the new library, I've added a "quick add" feature to Nag, the Horde task manager. This adds a tasks/quickAdd registry method, that takes a single string. Dates in the string are parsed out as due dates, and multiple tasks can be added by separating them with newlines. Child tasks can also be created with indentation (including * or - lists).

For example:

laundry tomorrow
* buy detergent tonight 

Will create two tasks: a parent task named "laundry" with a due date of tomorrow, and a child task named "buy detergent" with a due date of tonight.

In addition to the API call there's a pretty UI for this:

Gives:

Others have these features of course. However our version handles some cases that some of them don't:

MySQL Conference Wrapup

Absolute highlight of the MySQL conference: being congratulated by Monty Widenius for creative use of merge tables, and receiving a batch of good feedback on our keynote (video).

Speaking at the MySQL Conference

I'm really excited to be participating in the closing keynote of this year's MySQL conference. We'll be talking about our experiences behind the scenes of the Obama compaign. For me and my coworker Leigh Heyman, that means the software and hardware systems behind my.barackobama.com, which took in over $500 million in contributions and sent over 1.3 billion emails.

I also recently spoke with another coworker, Josh King, at the BostonPHP user's group about the various communications systems we ran - Josh about the Neighbor-to-Neighbor tool for canvassing, and myself about email. There are some comments and photos on the meetup page, and a podcast recording on the BostonPHP website.

Using Horde/Controller from the command line

Horde/Controller is a new Horde 4/PHP 5 component that builds on Horde/Routes to provide the "C" in an MVC architecture. It's heavily based on the Maintainable framework, which is in turn heavily based on Rails, so it will look similar if you're familiar with either of those. Both Mad and Rails have the concept of tasks that can be run from the command line; I thought it would be nice to use the same routing and controller architecture to allow command line scripts.

After a little hacking, the Horde_Controller package in git now has Cli request and response objects and this script is all you need to give command-line access to a set of controllers:

#!/usr/bin/env php
<?php

require 'Horde/Autoloader.php';

// Set up our request and routing objects
$request = new Horde_Controller_Request_Cli();
$response = new Horde_Controller_Response_Cli();
$mapper = new Horde_Routes_Mapper();
$mapper->connect(':controller/:action');

// Create our controller context.
$context = array(
    'mapper' => $mapper,
    'controllerDir' => dirname(__FILE__) . '/../lib/controllers',
);

// Dispatch.
try {
    $dispatcher = Horde_Controller_Dispatcher::singleton($context);
    $dispatcher->dispatch($request, $response);
} catch (Exception $e) {
    var_dump($e->getMessage());
}

Error handling can obviously be improved, and your controllers need to produce plaintext output for this to be friendly, but it gives you the flexibility to add new commands to your script just by dropping in new controller classes to the controllerDir.

Arguments are parsed with the Horde/Argv package, using a new allowUnknownArgs parameter that auto-creates simple text values for options unknown to the parser. This lets you take arbitrary command line arguments in your controller without having to tell the dispatcher script what arguments you expect ahead of time (of course the trade off is that the dispatcher can't validate the arguments, so the controllers are responsible for that, just like they are with web requests).

Here's an example of invoking the index() method of a MigrateController class:

$ ./dispatch.php migrate

To invoke the currentVersion() method of the same controller:

$ ./dispatch.php migrate/current_version

To pass an argument named target_version with the value "11" to the controller's index() method:

$ ./dispatch.php migrate --target-version=11

It's still a work in progress, but I'm happy with the possibilities.