Skip to content

Commit

Permalink
make it so
Browse files Browse the repository at this point in the history
  • Loading branch information
Gabriel Llamas committed Jan 31, 2015
0 parents commit c023093
Show file tree
Hide file tree
Showing 14 changed files with 360 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/node_modules
5 changes: 5 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CHANGES
examples
test
.npmignore
.travis.yml
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
language: node_js
node_js:
- "0.10"
- "0.11"
after_script: "npm install coveralls && npm run-script test-lcov | ./node_modules/.bin/coveralls"
17 changes: 17 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
v0.1.5 (25 Jan 2015)
Bump version.

v0.1.4 (19 Jan 2015)
Added a "baton" parameter.

v0.1.3 (12 Jan 2015)
Bump version.

v0.1.2 (10 Jan 2015)
Bump version.

v0.1.1 (10 Jan 2015)
Bump version.

v0.1.0 (09 Jan 2015)
First release.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2015 Gabriel Llamas

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
66 changes: 66 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
cargo-ship
==========

#### Parallel execution of tasks with a shared namespace ####

[![npm version][npm-version-image]][npm-url]
[![Travis][travis-image]][travis-url]
[![Coveralls][coveralls-image]][coveralls-url]

The extremly well-known parallel execution of tasks, but with a cargo, a shared object where tasks can store data. It's like a cargo ship, cranes (tasks) storing data (cargo). Each task writing to the shared object.

It's very useful when you need to call a series of functions in parallel and store the data in a common place.

```javascript
var cranes = [
function (cargo, done) {
cargo.a = 1;
done();
},
function (cargo, done) {
cargo.b = 2;
done();
},
function (cargo, done) {
cargo.c = 3;
done();
}
];

ship.load(cranes, function (err, cargo) {
// cargo { a: 1, b: 2, c: 3 }
});
```

It's basically the same behaviour as the `async.parallel()` with with a slightly and sightly! interface.

___module_.load(cranes[, cargo], callback) : undefined__
Executes all tasks in parallel.

`cranes` is an array of functions to run in parallel. Each function has the signature `function(cargo, done)`, where `cargo` is the shared object and `done` the function to call when the task finishes. As usual, pass an error to `done()` to abort the execution of the tasks. This is the error returned by the `load()` function. Because aborting asynchronous parallel tasks is not possible once they begin, the callback is guaranteed to be called only once with the first error occurred.

A `cargo` can be passed from outside. Use the second parameter to initialize the cargo with data.

```javascript
var cranes = [
function (cargo, done) {
cargo.b = 2;
done();
},
function (cargo, done) {
cargo.c = 3;
done();
}
];

ship.load(cranes, { a: 1 }, function (err, cargo) {
// cargo { a: 1, b: 2, c: 3 }
});
```

[npm-version-image]: https://img.shields.io/npm/v/cargo-ship.svg?style=flat
[npm-url]: https://npmjs.org/package/cargo-ship
[travis-image]: https://img.shields.io/travis/gagle/node-cargo-ship.svg?style=flat
[travis-url]: https://travis-ci.org/gagle/node-cargo-ship
[coveralls-image]: https://img.shields.io/coveralls/gagle/node-cargo-ship.svg?style=flat
[coveralls-url]: https://coveralls.io/r/gagle/node-cargo-ship
11 changes: 11 additions & 0 deletions examples/boot/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';

var race = require('../../lib');

race.start(require('./modules').map(function (name) {
return require('./modules/' + name);
}), function (err, baton) {
if (err) return console.error(err);
console.log(baton);
// { a: 1, b: 2, c: 3 }
});
6 changes: 6 additions & 0 deletions examples/boot/modules/a.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
'use strict';

module.exports = function (baton, next) {
baton.a = 1;
next();
};
6 changes: 6 additions & 0 deletions examples/boot/modules/b.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
'use strict';

module.exports = function (baton, next) {
baton.b = 2;
next();
};
6 changes: 6 additions & 0 deletions examples/boot/modules/c.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
'use strict';

module.exports = function (baton, next) {
baton.c = 3;
next();
};
7 changes: 7 additions & 0 deletions examples/boot/modules/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

module.exports = [
'a',
'b',
'c'
];
29 changes: 29 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict';

exports.load = function (cranes, cargo, cb) {
if (arguments.length === 2) {
cb = cargo;
cargo = {};
}

var len = cranes.length;
var remaining = len;
if (!len) return cb(null, cargo);

var done = function (err) {
if (errored) return;

if (err) {
errored = true;
return cb(err);
}

if (!--remaining) return cb(null, cargo);
};

var errored = false;

for (var i = 0; !errored && i < len; i++) {
cranes[i](cargo, done);
}
};
26 changes: 26 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "cargo-ship",
"version": "0.0.0",
"description": "Parallel execution of tasks with a shared namespace",
"keywords": [
"parallel",
"tasks",
"shared"
],
"author": "Gabriel Llamas <gagle@outlook.com>",
"repository": "git://github.com/gagle/node-cargo-ship.git",
"engines": {
"node": ">=0.10"
},
"devDependencies": {
"code": "^1.3.0",
"lab": "^5.2.1",
"sinon": "^1.12.2"
},
"scripts": {
"test": "node node_modules/lab/bin/lab -t 100",
"test-lcov": "node node_modules/lab/bin/lab -t 100 -r lcov"
},
"license": "MIT",
"main": "lib"
}
154 changes: 154 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
'use strict';

var sinon = require('sinon');
var code = require('code');
var lab = module.exports.lab = require('lab').script();

var expect = code.expect;
var describe = lab.describe;
var it = lab.it;

var ship = require('../lib');

describe('cargo-ship', function () {
it('calls tasks in parallel (sync)', function (done) {
var cranes = [
function (cargo, done) {
expect(cargo).to.deep.equal({});
cargo.a = 1;
done();
},
function (cargo, done) {
expect(cargo).to.deep.equal({
a: 1
});
cargo.b = 2;
done();
},
function (cargo, done) {
expect(cargo).to.deep.equal({
a: 1,
b: 2
});
cargo.a = 3;
done();
}
];

ship.load(cranes, function (err, cargo) {
expect(err).to.not.exist();
expect(cargo).to.deep.equal({
a: 3,
b: 2
});
done();
});
});

it('calls tasks in parallel (async)', function (done) {
var cranes = [
function (cargo, done) {
cargo.a = 1;
process.nextTick(done);
},
function (cargo, done) {
cargo.b = 2;
process.nextTick(done);
},
function (cargo, done) {
cargo.c = 3;
process.nextTick(done);
}
];

ship.load(cranes, function (err, cargo) {
expect(err).to.not.exist();
expect(cargo).to.deep.equal({
a: 1,
b: 2,
c: 3
});
done();
});
});

it('finishes with no tasks', function (done) {
ship.load([], function (err, cargo) {
expect(err).to.not.exist();
expect(cargo).to.deep.equal({});
done();
});
});

it('can receive a cargo from the outside', function (done) {
var cranes = [
function (cargo, done) {
expect(cargo).to.deep.equal({
c: 3
});
cargo.a = 1;
done();
},
function (cargo, done) {
cargo.b = 2;
done();
}
];

ship.load(cranes, { c: 3 }, function (err, cargo) {
expect(err).to.not.exist();
expect(cargo).to.deep.equal({
a: 1,
b: 2,
c: 3
});
done();
});
});

it('aborts with error (sync)', function (done) {
var errInstance = new Error();
var spy = sinon.spy(function (baton, next) {
next();
});

var cranes = [
function (cargo, done) {
done(errInstance);
},
spy
];

ship.load(cranes, function (err, cargo) {
expect(err).to.equal(errInstance);
expect(cargo).to.be.undefined();
expect(spy.callCount).to.equal(0);
done();
});
});

it('aborts with error (async)', function (done) {
var errInstance = new Error();
var errInstance2 = new Error();

var spyCrane = sinon.spy(function (baton, next) {
next(errInstance);
});

var cranes = [
function (cargo, done) {
process.nextTick(function () {
done(errInstance2);
});
},
spyCrane
];

ship.load(cranes, function (err, cargo) {
expect(err).to.equal(errInstance);
expect(cargo).to.be.undefined();
expect(spyCrane.callCount).to.equal(1);
done();
});
});
});

0 comments on commit c023093

Please sign in to comment.