Using Contractor With Child Processes

Contractor is a factory of contracts, that helps with documenting our APIs.
The child_process facility built into node is using EventEmitter (v1), and does not support callbacks, so it is a great fit for Contractor!
Since I needed it for a distributed worker framework I am working on, I added a helper facility called “Lawyer”, it basically reads the contracts and routes them to the provided object.

Let us take the parent and child example code from Node’s official documentation (http://nodejs.org/api/child_process.html) and see how simple it would be to add some documentation.

Parent

1
2
3
4
5
6
7
8
9
var cp = require('child_process');

var n = cp.fork(__dirname + '/sub.js');

n.on('message', function(m) {
console.log('PARENT got message:', m);
});

n.send({ hello: 'world' });

Child

1
2
3
4
5
process.on('message', function(m) {
console.log('CHILD got message:', m);
});

process.send({ foo: 'bar' });

Node’s child_process facility provied us with a very basic means of communication, we simply call n.send({any: "thing"}) and it automagically recieved by the forked child. Let’s spice it up with a more RPC message passing style, with contracts and lawyers

contracts.js

1
2
3
4
5
6
7
8
9
10
var Contractor = require('contractor').Contractor;
exports.ChildPublish = {
"GreetingResponse" : Contractor.Create("GreetingResponse", Contractor.Required("childs greeting response")).
"StatusResponse" : Contractor.Create("StatusResponse", Contractor.Required("errors count"), Contractor.Required("number of completed jobs"))
}

exports.ChildSubscribe = {
"Greeting": Contractor.Create("Greeting", Contractor.Required("greeting word")),
"StatusQuery" : Contractor.Create("StatusQuery")
}

parent.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var contracts = require('./contracts');
var Contractor = require('contractor').Contractor;
var Lawyer = require('contractor').Lawyer;
var cp = require('child_process');

var n = cp.fork(__dirname + '/sub.js');

n.on('message', function(m) {
Lawyer.Read(m, {
"GreetingResponse" : function(childResponse){ console.log("child responded:" + childResponse) },
"StatusResponse" : function(errorCount, numCompletedJobs){ console.log("childs status, errors:"+errorCount+" jobs done:" + numCompletedJobs) }
});
});
n.send(contracts.ChildSubscribe.Greeting("this is your father!"));
n.send(contracts.ChildSubscribe.StatusQuery());

sub.js

1
2
3
4
5
6
7
8
9
var contracts = require('./contracts');
var Contractor = require('contractor').Contractor;
var Lawyer = require('contractor').Lawyer;
process.on('message', function(m) {
Lawyer.Read(m, {
"Greeting" : function(parentGreeting){ process.send( contracts.ChildPublish.GreetingResponse("Hi "+parentGreeting+" can I have 10$?")) },
"StatusQuery" : function(){ process.send( contracts.ChildPublish.StatusResponse(0, 42) ) }
});
});

If we were to run these

1
2
3
#> node parent.js
child responded:Hi this is your father! can I have 10$?
childs status, errors:0 jobs done:42

If you found Contractor / Lawyer interesting and/or useful, I would love to hear about it!