2010-12-15

Git, TargetProcess and Hashtags

At $job, we're using TargetProcess for our user story and task management. One of the neat things you can do in TP is tag commits to a code repository in such a way that they will link to the task associated with that commit:
#123 This commit is linked to task number 123
In order to get this to work, the hashtag has to be the first thing on the first line of the commit message. Anyone using `git` as their source control should immediately see the problem with this...

...In git commit messages, any line starting with # is considered a comment and is stripped out of the commit message. Quite annoyingly, there's no way to change git's default comment delimiter. Since $job is using Subversion, this isn't a problem except for myself, the lowly git user hobbling along with git-svn.

The solution I've come up with is a two-pronged approach involving changing a default and adding a git-hook.

Firstly, the default --cleanup option on a `git-commit` command strips trailing and leading whitespace lines and all commentary (lines starting with #). So I aliased `git commit` to only worry about the whitespace lines, by adding this to my .gitconfig:
[alias]
    cmt = "commit --cleanup=whitespace""
So now git will leave lines starting with "#" alone. This means I have strip them out myself. This is accomplished with a git commit-msg hook that looks like this:
# Use TargetProcess hashtags to link commits to tasks
# use this in conjunction with --cleanup=whitespace
sed -e 's/^#.*//' -e 's/^@\(#.*\)/\1/' -i $1
The first sed expression strips out comment lines. The second one translates any line that starts with "@#" into one starting with "#". The trick is that it does it after the comment line stripping. So now I can do commits that look like this:
git cmt

# The editor opens and I can type:

@#123 This commit is linked to task number 123
# This is a comment line that will get stripped out
#  So it this

When the commit goes through, voila! A commit message starting with a hashtag that can be picked up by TargetProcess.

Edit 2010-12-16: After experimentation and verification, it turns out the TP can pick up the task id hashtag anywhere in the commit message. So all the above is not really necessary. It's interesting from an educational perspective, though.

2010-12-05

Private Git repository with Dropbox

I recently began working on a project that needs a remotely-accessible private git repository. The project needs to be done on the cheap, and we don't have the budget to pay for a private github or assembla repository. We're already using Dropbox to handle project documentation and such, so it became a natural fit for sharing code as well.

I already have the Dropbox client installed and set up on my development machine. Here is how to set up the repo:
cd /path/to/Dropbox
mkdir myproject
cd myproject
git --bare init
This initializes a "bare" repo; a repo without a working directory. In other words, you can't edit and commit to it directly. The repo is now your "remote", hosted on Dropbox, and accessible through the local filesystem.

Create a working clone from the remote repo:
cd /path/to/myproject-working
git clone /path/to/Dropbox/myproject .
This will complain about cloning an empty repo, which is fine. Now follow the normal git development cycle:
echo "Read this!" > README
git add README
git commit -m "Initial commit"
When you're done making changes and committing, it's time to push to the remote repo in Dropbox:
git push /path/to/Dropbox/myproject --all
The `--all` flag is necessary on the first commit to the remote. It can be left off of all subsequent commits.

That's it! Now the repo is available on any computer linked to the Dropbox account, and can be checked out using the normal `git clone`, work, commit, `git push` cycle.

If you want other developers to have access, simply share the repo directory with their Dropbox accounts as you would any other directory. They can clone it and push to it just like any other git repository.

Here's another trick if you don't have the Dropbox daemon (dropboxd) running all the time on your development machine. Put the following script in "/path/to/Dropbox/myproject/hooks". Name the script "dropbox-push"
#!/bin/sh

dropbox running
if [ $? -eq 1 ]; then
 echo "Dropbox started by another process."
 exit 0
fi

dropbox start
until [ "`dropbox status`" = "Idle" ]; do
 sleep 1
done
dropbox stop
Now make the post-receive hook runnable:
mv post-receive.sample post-receive
Edit the "post-receive" file, and add the following lines:
cd `dirname $0`
./dropbox-push
Save the file. Now whenever you (or another developer) pushes to the repo, it will check to see if the Dropbox daemon is running, and if not, will start it and let it run until the code is synced, then stop the daemon. Since the hook itself is in under Dropbox, it will be synced and will be run for all other developers pushing to the Dropbox repo as well.

So there it is, a free, private git repository; at least, until the repository gets larger than the Dropbox free 2GB limit.

2010-11-21

JadedPHP controllers

In my last post about JadedPHP, I talked about the model layer. Today, it's all about the controller layer.

Jaded controllers are built around the Intercepting Filter Pattern. In brief, when the Dispatcher sends a request to a controller for processing, the request is actually dispatched to a chain of filters which can modify it or check it for necessary attributes (has it been authenticated, etc.) before passing it on to the actual request handler.

A controller class extends the Jaded_Controller class, and needs to define a process() method.
class MyController extends Jaded_Controller
{
    protected function process(Jaded_Request $oRequest, Jaded_Response $oResponse)
    {
        $oResponse->assign('myVar', 'this is the assigned value');
        $oResponse->assign('requestVar', $oRequest->getParam('reqParam'));
    }
}
Here is a simple controller that assigns some values to a response variable. One of the assignments, myVar, is given by the controller. The other is taken from the passed in request object. If the name parameter was never set in the request, the getParam() call will return null.

$oController = new MyController();
$oRequest = new Jaded_Request();
$oResponse = new Jaded_Response();

// The way we get to the process() method is through the dispatch() method
$oController->dispatch($oRequest, $oResponse);

print_r($oResponse->getAssigns());
/*
Prints:
Array
(
    [myVar] => 'this is the assigned value'
    [requestVar] => 
)

*/

The requestVar is null because we never populated it. Here is a controller filter that will populate it for us:
class RequestFillerFilter extends Jaded_Controller_Filter_PreProcessor
{
    protected function preProcess(Jaded_Request $oRequest, Jaded_Response $oResponse)
    {
        $oRequest->setParam('requestVar', 123);
    }
}

//Now we modify the dispatching code from above
$oController = new MyController();
$oRequest = new Jaded_Request();
$oResponse = new Jaded_Response();

// Create the filter, wrapping the controller that will process the request
$oFilter = new RequestFillerFilter($oController);

// Filters are actually controllers themselves, and are dispatched the same way
$oFilter->dispatch($oRequest, $oResponse);

print_r($oResponse->getAssigns());
/*
Prints:
Array
(
    [myVar] => 'this is the assigned value'
    [requestVar] => 123
)

*/
requestVar was set in the request object before it was passed to the controller, so it was there for the controller to access.

Here's an example of a post-processing filter, that will handle our output for us:
class Print_RFilter extends Jaded_Controller_Filter_PostProcessor
{
    protected function postProcess(Jaded_Request $oRequest, Jaded_Response $oResponse)
    {
        print_r($oResponse->getAssigns());
    }
}

//And here is the usage
$oController = new MyController();
$oRequest = new Jaded_Request();
$oResponse = new Jaded_Response();

// Filters expect a controller as a construction param,
// but since filters are controllers, we can chain them like this:
$oFilter = new Print_RFilter( new RequestFillerFilter( $oController));
$oFilter->dispatch($oRequest, $oResponse);

/*
Prints:
Array
(
    [myVar] => 'this is the assigned value'
    [requestVar] => 123
)

*/
Notice that the dispatcher no longer needs to call print_r() itself, because the post filter will do it instead.

It gets unwieldy to have to keep wrapping controllers in filters, especially if a set of controllers always gets wrapped in the same filters. Jaded provides a Chain filter that let's you string together commonly used filters to simplify wrapping controllers in filters.
class CommonFilterChain extends Jaded_Controller_Filter_Chain
{
    protected $aFilters = array(
        'Print_RFilter',
        'RequestFillerFilter',
    );
}

$oController = new MyController();
$oRequest = new Jaded_Request();
$oResponse = new Jaded_Response();

$oFilter = new CommonFilterChain($oController);
$oFilter->dispatch($oRequest, $oResponse);

/*
Prints:
Array
(
    [myVar] => 'this is the assigned value'
    [requestVar] => 123
)

*/
It doesn't matter in this example, but filters listed in a chain are wrapped with the first listed filter as the outermost, and the last filter as the innermost. This is relevant when using a Jaded_Controller_Filter which defines both preProcess() and postProcess() methods.

The other interesting thing to note about chains is that they are filters themselves, so it is possible to have a chain filter listed inside another chain filter.

My goal in JadedPHP is to provide filters that perform tasks common to a web application and wrap them in easy to use chains. This way, the dispatcher can detect that a request is an HTTP request, and automatically wrap it in filters that will initialize the session, put the request method and parameters into a request object, set up an HTML or AJAX renderer, check for authentication and other necessary tasks.

The example source code is available here.

2010-11-14

New domains

I just registered joshadell.com and everymansoftware.com. Right now, they both redirect to this blog. everymansoftware.com will probably continue to point here for a while. I intend to get some stuff up on joshadell.com about projects, maybe some live demos, who knows?

After attending indieconf yesterday, I was bitten by the bug to start making my name my "brand" and doing a little more self-promotion, blogging, and contributing to the web community.

I'm using DreamHost in case anyone is interested.

2010-11-03

Models can be so Jaded

I am currently working on a PHP framework which I've been calling JadedPHP. It's purpose it to provide a lightweight MVC framework for some of my personal projects. Basically, I'm after a learning experience that provides useful output for me. If someone else gets use out of it as well, that's just icing on the cake.

My feelings on PHP frameworks in general are that they abstract way too much of the application's structure from the developer. I like to dig in and really know what's going on with the code that's running my code. Jaded is an attempt to build as little "automagic" into the framework as possible, and thus maximize the flexibility given to the developer to borrow bits and pieces as needed, and to override the rest if necessary.

The model layer is pretty solid so far. Models in Jaded are broken into 3 parts: a definition, a store, and a container.

The definition, oddly enough, defines the model. This means it lists the available fields of the model, which of those fields are key fields that uniquely identify the record held by the model, and any default values for those fields. Basically, the definition gives the model's structure, what it looks like.

A model store is the actual storage mechanism for the model. It implements basic CRUD operations, and is a place to define additional data manipulation tasks. The base class for model stores does not specify what the storage mechanism is. It could be a database, a CSV or XML file, or some volatile cache. It is up to concrete stores to actually implement the CRUD operations for a given model. (In reality, Jaded comes with basic database store that performs one model -> one row mapping using Jaded's PDO wrapper class.)

Finally, there is the model container itself. The container defines which definition and store the model will use. It is also responsible for holding the individual field values for a given record. It provides basic getter and setter functions, as well as the ability to fill a model from an array of values or spit out an array containing the values.

So how does this look? Let's pretend we have a database table that looks like this:
CREATE TABLE ducks (
    duckid int NOT NULL AUTO_INCREMENT,
    type int NOT NULL,
    name varchar(20) NULL,
    sound varchar(10) NULL,
    PRIMARY KEY duckid (duckid)
);

The definition for a Duck model would look something like this:
class DuckDefinition extends Jaded_Model_Definition
{
    const TypeMallard = 0;
    const TypeWood = 1;

    /**
     * Maps a name that calling code can use to an internal field name
     * Note that they do not have to match, and there can be multiple
     * aliases to a single internal name.
     */
    protected $aFieldMap = array(
        "duckid" => "duckid",
        "type"   => "type",
        "name"   => "name",
        "noise"  => "sound",
        "sound"  => "sound"
    );

    /**
     * The key fields for this model
     * Fields that uniquely identify it
     * A key of "auto" means the key is automatically set by the store,
     * else use "key"
     */
    protected $aKeyFields = array(
        "duckid" => "auto"
    );

    /**
     * Defaults for any fields
     */
    protected $aDefaultValues = array(
        "type"  => self::TypeMallard,
        "sound" => "quack"
    );
}

And now the store. In this case, I'm cheating and using the built-in basic database store:
class DuckStore extends Jaded_Model_Store_Database
{
    protected $sTable = "ducks";

    /**
     * This bit is simply the connection name used by Jaded's database wrapper
     */
    protected $sDbId = "duck_database";
}

And finally, a model container that wraps it all up:
class Duck extends Jaded_Model
{
    protected $sDefaultDefinition = "DuckDefinition";
    protected $sDefaultStore      = "DuckStore";
}

And now a bit of usage:

$oDuck = new Duck();
echo $oDuck->getType();    // prints "0"
echo $oDuck->getSound();   // prints "quack"

$oDuck->setName("Donald");
$oDuck->create();
$iDuckId = $oDuck->getDuckId();

$oDuck2 = new Duck();
$oDuck2->setDuckId($iDuckId);
$oDuck2->load();
echo $oDuck2->getName();   // prints "Donald"

// Now let's pretend we need to migrate all ducks to a CSV file, and we have a store for that
class DuckCSV extends Duck
{
    protected $sDefaultStore      = "DuckCSVStore";
}

$oDuck3 = new DuckCSV($oDuck2);
$oDuck->update();
Note how when we need to store the model in a different storage medium, we can just change the store type, and keep the definition and any methods that might have been built into the Duck class.

If the model's are stored in a database one row per model object, Jaded has a lot of functionality built in. But it also provides the flexibility to build model's that have the same definition, but have a store that pushes to/pulls from an RSS feed, or Twitter, or a stock ticker, or any other data source. For only a little extra setup, you get a lot of options.

There will probably be a GitHub repository soon, and another post or two as I start to use Jaded in more projects.

2010-10-13

Box2dnode Available via npm

box2dnode has been packaged up and released as an npm module. To get it, use the following command:
npm install box2d

Creating the package was relatively easy. It only involved creating a package.json file in the root of the package:

{
 "name" : "box2d",
 "version" : "1.0.0",
 "description" : "2D physics engine",
 "homepage" : "http://github.com/jadell/box2dnode",
 "author": {
  "name" : "Josh Adell",
  "email" : "josh.adell@gmail.com",
  "url" : "http://everymansoftware.blogspot.com/"
 },
 "main" : "./box2dnode",
 "engines" : ["node"]
}
Of course, box2d is a simple package. I'll be interested to see how packaging works with a more complex project.

2010-10-04

Global Pub-Sub

I've started working on a universal messaging bus that would allow any number of components to broadcast "events" to any other number of listeners (the publication-subscription, or observer, pattern.) Since I'm a sucker for playing with new tools, I've decided to build the bus in node.js. The basic idea is to allow a client in one process to subscribe to events broadcast by other clients in other processes by subscribing to a "central" bus.

In practice, each broadcaster will have a local bus object to which they publish. That bus serializes the message and sends it to a central server. The central server checks the message for validity, then pushes the message to any connected listeners. There will also be an HTTP connector that will store events for a period of time to allow AJAX listeners to periodically connect and pull down waiting events.

The listeners will have a local bus object to which they can subscribe. When an event is pushed to the listener object by the server (or pulled down in a long poll by an AJAX client), the bus un-serializes the message and pushes it to the registered listeners for that event type.

To the publishers and subscribers, everything appears to be happening local to the process.

What does this look like codewise?
// Publisher client
var bus = new Bus(server, port);
bus.broadcast('someEvent', arg0, arg1);

// Listener client
var bus = new Bus(server, port);
bus.addListener('someEvent', function (arg0, arg1) {
    // do something with the information
});


Ideally, I'd like to include publisher-subscriber clients in several languages. A PHP subscriber would use a callback:
function handleSomeEvent($arg0, $arg1) {
    // do something with the information
}

$oBus = new Bus($server, $port);
$oBus->addListener('someEvent', 'handleSomeEvent');

The publisher-subscriber client could use a TCP, WebSocket, or HTTP long-poll to communicate with the remote bus. A future iteration might allow for multiple remote bus nodes to push events to each other to then be pushed down to individual client listeners.

More information as it develops.