Skip to content

Commit

Permalink
add init lifecycle method to custom registries that receives the un…
Browse files Browse the repository at this point in the history
…dertaker instance - closes #25
  • Loading branch information
phated committed Aug 21, 2015
1 parent 4237140 commit e7bf5b3
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 31 deletions.
28 changes: 19 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ or add custom functionality to your registries.

A registry's prototype should define:

- `init(taker)`: receives the undertaker instance to set pre-defined tasks using the `task(taskName, fn)` method.
- `get(taskName)`: returns the task with that name
or `undefined` if no task is registered with that name.
- `set(taskName, fn)`: add task to the registry. If `set` modifies a task, it should return the new task.
Expand All @@ -171,8 +172,11 @@ module.exports = MyRegistry;

### Sharing tasks

To share common tasks with all your projects, you can set a registry with those
tasks defined inside the constructor. For example you might want to share a `clean` task:
To share common tasks with all your projects, you can expose an `init` method on the registry
prototype and it will receive the Undertaker instance as the only argument. You can then use
`undertaker.task(name, fn)` to register pre-defined tasks.

For example you might want to share a `clean` task:

```javascript
var fs = require('fs');
Expand All @@ -181,23 +185,29 @@ var util = require('util');
var DefaultRegistry = require('undertaker-registry');
var del = require('del');

function CommonRegistry(){
function CommonRegistry(opts){
DefaultRegistry.call(this);

var buildDir = './build';
opts = opts || {};

this.buildDir = opts.buildDir || './build';
}

util.inherits(CommonRegistry, DefaultRegistry);

CommonRegistry.prototype.init = function(takerInst){
var buildDir = this.buildDir;
var exists = fs.existsSync(buildDir);

if(exists){
throw new Error('Cannot initialize common tasks. `build/` directory exists.');
throw new Error('Cannot initialize common tasks. ' + buildDir + ' directory exists.');
}

this.set('clean', function(cb){
takerInst.task('clean', function(cb){
del([buildDir], cb);
});
}

util.inherits(CommonRegistry, DefaultRegistry);

module.exports = CommonRegistry;
```

Expand All @@ -206,7 +216,7 @@ Then to use it in a project:
var Undertaker = require('undertaker');
var CommonRegistry = require('myorg-common-tasks');

var taker = new Undertaker(CommonRegistry);
var taker = new Undertaker(CommonRegistry({ buildDir: '/dist' }));

taker.task('build', taker.series('clean', function build(cb) {
// do things
Expand Down
12 changes: 5 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,16 @@ var parallel = require('./lib/parallel');
var registry = require('./lib/registry');
var _getTask = require('./lib/get-task');
var _setTask = require('./lib/set-task');
var validateRegistry = require('./lib/helpers/validateRegistry');

function Undertaker(Registry){
function Undertaker(customRegistry){
EventEmitter.call(this);

Registry = Registry || DefaultRegistry;

this._registry = new Registry();
this._registry = new DefaultRegistry();
if(customRegistry){
this.registry(customRegistry);
}

this._settle = (process.env.UNDERTAKER_SETTLE === 'true');

validateRegistry(this._registry);
}

inherits(Undertaker, EventEmitter);
Expand Down
32 changes: 29 additions & 3 deletions lib/helpers/validateRegistry.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,36 @@ var assert = require('assert');

var _ = require('lodash');

function isConstructor(registry){
if(!(registry && registry.prototype)){
return false;
}

var hasProtoGet = _.isFunction(registry.prototype.get);
var hasProtoSet = _.isFunction(registry.prototype.set);
var hasProtoInit = _.isFunction(registry.prototype.init);
var hasProtoTasks = _.isFunction(registry.prototype.tasks);

if(hasProtoGet || hasProtoSet || hasProtoInit || hasProtoTasks){
return true;
}

return false;
}

function validateRegistry(registry){
assert(_.isFunction(registry.get), 'Custom registry must have `get` function');
assert(_.isFunction(registry.set), 'Custom registry must have `set` function');
assert(_.isFunction(registry.tasks), 'Custom registry must have `tasks` function');
try {
assert(_.isFunction(registry.get), 'Custom registry must have `get` function');
assert(_.isFunction(registry.set), 'Custom registry must have `set` function');
assert(_.isFunction(registry.init), 'Custom registry must have `init` function');
assert(_.isFunction(registry.tasks), 'Custom registry must have `tasks` function');
} catch (err) {
if(isConstructor(registry)){
assert(false, 'Custom registries must be instantiated, but it looks like you passed a constructor');
} else {
throw err;
}
}
}

module.exports = validateRegistry;
4 changes: 3 additions & 1 deletion lib/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ var validateRegistry = require('./helpers/validateRegistry');

function setTasks(inst, task, name){
inst.set(name, task);
return inst;
}

function registry(newRegistry){
Expand All @@ -18,7 +19,8 @@ function registry(newRegistry){

var tasks = this._registry.tasks();

this._registry = _.transform(tasks, setTasks, newRegistry);
this._registry = _.reduce(tasks, setTasks, newRegistry);
this._registry.init(this);
}

module.exports = registry;
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"es6-weak-map": "^1.0.1",
"last-run": "^1.1.0",
"lodash": "^3.5.0",
"undertaker-registry": "0.0.2"
"undertaker-registry": "0.0.3"
},
"devDependencies": {
"async-once": "^1.0.0",
Expand All @@ -36,8 +36,8 @@
"once": "^1.3.1",
"promised-del": "^1.0.2",
"through2": "^0.6.3",
"undertaker-common-tasks": "git://github.com/phated/undertaker-common-tasks#3d3a89ce66cf7397bb00ea9924e12e07db3bf987",
"undertaker-task-metadata": "git://github.com/undertakerjs/undertaker-task-metadata#eb02af6139fcf51a8abb5b7b76a993744512d69d",
"undertaker-common-tasks": "git://github.com/phated/undertaker-common-tasks",
"undertaker-task-metadata": "git://github.com/undertakerjs/undertaker-task-metadata",
"vinyl-fs": "^1.0.0"
},
"keywords": [
Expand Down
52 changes: 44 additions & 8 deletions test/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ var MetadataRegistry = require('undertaker-task-metadata');
function noop(){}

function CustomRegistry(){}
CustomRegistry.prototype.init = noop;
CustomRegistry.prototype.get = noop;
CustomRegistry.prototype.set = noop;
CustomRegistry.prototype.tasks = noop;
Expand Down Expand Up @@ -52,7 +53,7 @@ describe('registry', function(){
});

it('should transfer all tasks from old registry to new', function(done){
var taker = new Undertaker(CommonRegistry);
var taker = new Undertaker(new CommonRegistry());
var customRegistry = new DefaultRegistry();
taker.registry(customRegistry);

Expand Down Expand Up @@ -83,12 +84,40 @@ describe('registry', function(){

taker.series('context')();
});

it('throws with a descriptive method when constructor is passed', function(done){
var taker = new Undertaker();

function ctor(){
taker.registry(CommonRegistry);
}

expect(ctor).to.throw(Error, 'Custom registries must be instantiated, but it looks like you passed a constructor');
done();
});

it('calls into the init function after tasks are transferred', function(done){
var taker = new Undertaker(new CommonRegistry());

var ogInit = DefaultRegistry.prototype.init;

DefaultRegistry.prototype.init = function(inst){
expect(inst).to.equal(taker);
expect(inst.task('clean')).to.be.a.function();
expect(inst.task('serve')).to.be.a.function();
};

taker.registry(new DefaultRegistry());

DefaultRegistry.prototype.init = ogInit;
done();
});
});

describe('constructor', function(){

it('should take a custom registry constructor on instantiation', function(done){
var taker = new Undertaker(CustomRegistry);
it('should take a custom registry on instantiation', function(done){
var taker = new Undertaker(new CustomRegistry());
expect(taker.registry()).to.be.an.instanceof(CustomRegistry);
expect(taker.registry()).to.not.be.an.instanceof(DefaultRegistry);
done();
Expand All @@ -102,7 +131,7 @@ describe('registry', function(){
});

it('should take a registry that pre-defines tasks', function(done){
var taker = new Undertaker(CommonRegistry);
var taker = new Undertaker(new CommonRegistry());
expect(taker.registry()).to.be.an.instanceof(CommonRegistry);
expect(taker.registry()).to.be.an.instanceof(DefaultRegistry);
expect(taker.task('clean')).to.be.a.function();
Expand All @@ -115,27 +144,34 @@ describe('registry', function(){
var taker;

function noGet(){
taker = new Undertaker(InvalidRegistry);
taker = new Undertaker(new InvalidRegistry());
}

expect(noGet).to.throw(Error, 'Custom registry must have `get` function');
InvalidRegistry.prototype.get = noop;

function noSet(){
taker = new Undertaker(InvalidRegistry);
taker = new Undertaker(new InvalidRegistry());
}

expect(noSet).to.throw(Error, 'Custom registry must have `set` function');
InvalidRegistry.prototype.set = noop;

function noInit(){
taker = new Undertaker(new InvalidRegistry());
}

expect(noInit).to.throw(Error, 'Custom registry must have `init` function');
InvalidRegistry.prototype.init = noop;

function noTasks(){
taker = new Undertaker(InvalidRegistry);
taker = new Undertaker(new InvalidRegistry());
}

expect(noTasks).to.throw(Error, 'Custom registry must have `tasks` function');
InvalidRegistry.prototype.tasks = noop;

taker = new Undertaker(InvalidRegistry);
taker = new Undertaker(new InvalidRegistry());
done();
});
});
Expand Down

0 comments on commit e7bf5b3

Please sign in to comment.