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

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

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

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

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

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

#> 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!