Skip to content
This repository has been archived by the owner on Apr 20, 2018. It is now read-only.

Refactoring hooks to run before event dispatching #57

Merged
merged 1 commit into from
Mar 6, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 0 additions & 55 deletions src/after.js

This file was deleted.

62 changes: 0 additions & 62 deletions src/before.js

This file was deleted.

93 changes: 89 additions & 4 deletions src/hooks.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,96 @@
import before from './before';
import after from './after';
import { hooks as utils } from 'feathers-commons';

import * as hooks from './bundled';
import { addHookMethod, processHooks } from './commons';

function isPromise(result) {
return typeof result !== 'undefined' &&
typeof result.then === 'function';
}

function hookMixin(service){
if(typeof service.mixin !== 'function') {
return;
}

const app = this;
const methods = app.methods;
const oldBefore = service.before;
const oldAfter = service.after;
const mixin = {};

addHookMethod(service, 'before', methods);
addHookMethod(service, 'after', methods);

methods.forEach(method => {
if(typeof service[method] !== 'function') {
return;
}

mixin[method] = function() {
// A reference to the original method
const _super = this._super.bind(this);
// Create the hook object that gets passed through
const hookObject = utils.hookObject(method, 'before', arguments);

hookObject.app = app;

// Process all before hooks
return processHooks.call(this, this.__beforeHooks[method], hookObject)
// Use the hook object to call the original method
.then(hookObject => {
if(typeof hookObject.result !== 'undefined') {
return Promise.resolve(hookObject);
}

return new Promise((resolve, reject) => {
const args = utils.makeArguments(hookObject);
// The method may not be normalized yet so we have to handle both
// ways, either by callback or by Promise
const callback = function(error, result) {
if(error) {
reject(error);
} else {
hookObject.result = result;
resolve(hookObject);
}
};

// We replace the callback with resolving the promise
args.splice(args.length - 1, 1, callback);

const result = _super(... args);

if(isPromise(result)) {
result.then(data => callback(null, data), callback);
}
});
})
// Make a copy of hookObject from `before` hooks and update type
.then(hookObject => Object.assign({}, hookObject, { type: 'after' }))
// Run through all `after` hooks
.then(processHooks.bind(this, this.__afterHooks[method]))
// Finally, return the result
.then(hookObject => hookObject.result);
};
});

service.mixin(mixin);

// Before hooks that were registered in the service
if(oldBefore) {
service.before(oldBefore);
}

// After hooks that were registered in the service
if(oldAfter) {
service.after(oldAfter);
}
}

function configure() {
return function() {
this.mixins.push(before(this));
this.mixins.push(after);
this.mixins.unshift(hookMixin);
};
}

Expand Down
35 changes: 35 additions & 0 deletions test/hooks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,39 @@ describe('feathers-hooks', () => {
done();
});
});

it('dispatches events with data modified by hook', done => {
const app = feathers().configure(hooks()).use('/dummy', {
create(data) {
return Promise.resolve(data);
}
});

const service = app.service('dummy');

service.before({
create(hook) {
hook.data.user = 'David';
}
}).after({
create(hook) {
hook.result.after = true;
}
});

service.once('created', function(data) {
try {
assert.deepEqual(data, {
test: true,
user: 'David',
after: true
});
done();
} catch(e) {
done(e);
}
});

service.create({ test: true });
});
});