Skip to content

Commit

Permalink
Merge branch 'release/v1.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
sebelga committed Nov 13, 2016
2 parents eb3a1ee + f1841a0 commit ad308b1
Show file tree
Hide file tree
Showing 7 changed files with 384 additions and 185 deletions.
25 changes: 25 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "script"
},
"root": true,
"env": {
"node": true
},
"extends": "airbnb-base",
"plugins": [
"mocha"
],
"rules": {
// enable additional rules
"indent": ["error", 4, {"SwitchCase": 1}],
"no-use-before-define": ["error", {"functions": false}],
"prefer-rest-params": "off",
"prefer-spread": "off",
"no-underscore-dangle": "off",
"no-param-reassign": "off",
"max-len": ["error", 120],
"mocha/no-exclusive-tests": "off"
}
}
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
language: node_js
node_js:
- "6"
- "5"

branches:
only:
- master
- /^release\/.*$/
- develop
- /^release\/.*$/
272 changes: 164 additions & 108 deletions hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,172 +2,228 @@
'use strict';

const is = require('is');
const arrify = require('arrify');

class HooksPromise {
hook(name, originalMethod) {
let proto = this.__proto__;
if (!proto.hasOwnProperty(name)) {
proto = this;
}
static wrap(scope) {
scope.pre = this.pre;
scope.post = this.post;
scope.hook = this.hook;
scope.__setupHooks = this.__setupHooks;
}

const pres = proto.__pres = proto.__pres || {};
const posts = proto.__posts = proto.__posts || {};
static hook(name, originalMethod) {
const proto = this;

pres[name] = pres[name] || [];
posts[name] = posts[name] || [];
const presHooks = proto.__pres = proto.__pres || {};
const postsHooks = proto.__posts = proto.__posts || {};

// We wrapp the original method with he hooked logig
proto[name] = function(...passedArgs) {
const self = this;
presHooks[name] = presHooks[name] || [];
postsHooks[name] = postsHooks[name] || [];

const lastArg = arguments[arguments.length-1];
// We wrapp the original method with he hooked logig
proto[name] = function wrapper() {
const passedArgs = Array.prototype.slice.apply(arguments);
const self = this;

/**
* Array or pre hooks
*/
const pres = this.__pres[name];
/**
* Array or pre hooks
*/
const pres = this.__pres[name];

/**
* Array of post hooks
*/
const posts = this.__posts[name];
/**
* Array of post hooks
*/
const posts = this.__posts[name];

/**
* Total pre hooks
*/
const totalPres = pres.length;
/**
* Total pre hooks
*/
const totalPres = pres.length;

/**
* Current hook being processed
*/
let currentPre = -1;
/**
* Total posts hooks
*/
const totalPost = posts.length;

/**
* Scope for the hooks
*/
let scope;

/**
* Current hook being processed
*/
let currentPre = -1;

/**
* arguments eventually passed to the hook - are mutable
*/
let hookArgs;

let resolveFn;
let rejectFn;

if (self.preHooksEnabled === false) {
return done.apply(self, passedArgs);
}

return new Promise((resolve, reject) => {
resolveFn = resolve;
rejectFn = reject;

/**
* arguments eventually passed to the hook - are mutable
* Error Handler
*/
let hookArgs;
const handleError = function handleError(err) {
reject(err);
};

/**
* Total async method still to execute
* Middleware (hook) wrapper
*/
let asyncsLeft = proto[name].__numAsyncPres;
const next = function next() {
let args = Array.prototype.slice.apply(arguments);

return new Promise(function(resolve, reject) {
if (is.object(args[0]) && {}.hasOwnProperty.call(args[0], '__override')) {
args = arrify(args[0].__override);
}

/**
* Error Handler
*/
const handleError = function(err) {
reject(err);
};
// If there is a __scopeHook function on the object
// we call it to get the scope wanted for the hook
scope = getScope(self, name, args);

/**
* Middleware (hook) wrapper
* Reference to current pre hook
*/
const next = function (...args) {
/**
* Reference to current pre hook
*/
let currentHook;
let preArgs;

if (args && args.length && typeof args[0] !== 'undefined') {
hookArgs = args;
};
let currentHook;

if (++currentPre < totalPres) {
currentHook = pres[currentPre]
if (args && args.length && typeof args[0] !== 'undefined') {
hookArgs = args;
}

return currentHook.apply(self, hookArgs).then(next, handleError);
}
if (currentPre + 1 < totalPres) {
currentPre += 1;
currentHook = pres[currentPre];

return done.apply(self, hookArgs);
return currentHook.apply(scope, hookArgs).then(next, handleError);
}

const done = function (...args) {
if (is.array(args[0]) && args.length === 1) {
args = args[0];
}
// hookArgs.push(resolve);
return done.apply(scope, hookArgs);
};

let response;
let totalPost = posts.length;
let currentPost = -1;
let postArgs;
return next.apply(this, passedArgs);
});

function done() {
const args = Array.prototype.slice.apply(arguments);

let next = function (response) {
/**
* Reference to current post hook
*/
let currPost;
let postArgs;
resolveFn = resolveFn || function resolve(data) { return Promise.resolve(data); };

if (++currentPost < totalPost) {
currPost = posts[currentPost];
let currentPost = -1;

// Call next "post" hook
return currPost.call(self, response).then(next, handleError);
} else {
// Resolve... we're done! :)
return resolve(response);
const next = function next(data) {
if (currentPost + 1 < totalPost && self.__hooksEnabled !== false) {
currentPost += 1;
/**
* Reference to current post hook
*/
const currPost = posts[currentPost];

// Call next "post" hook
return currPost.call(scope, data).then(next, (err) => {
// we convert response to object
if (!is.object(data)) {
data = {
result: data,
};
}
};

// We execute the actual (original) method
response = originalMethod.apply(self, args);
// create errors Array
data.errorsPostHook = data.errorsPostHook || [];
data.errorsPostHook.push(err);

return next(data);
});
}

// Resolve... we're done! :)
return resolveFn(data);
};

// We execute the actual (original) method
let response = originalMethod.apply(self, args);

// We either return a 'post' hook
if (totalPost > 0) {
return response.then(next, handleError);
}
if (response.constructor.name !== 'Promise') {
response = Promise.resolve();
}

// no "post" hook, we're done!
return response.then(resolve, handleError);
};
// If there are post hooks, we chain the response with the hook
if (totalPost > 0) {
return response.then(next, rejectFn);
}

return next.apply(this, passedArgs);
});
};
// no "post" hook, we're done!
return response.then(resolveFn, rejectFn);
}
};

proto[name].__numAsyncPres = 0;
proto[name].__hooked = true;
proto[name].__numAsyncPres = 0;
proto[name].__hooked = true;

return this;
return this;
}

pre(name, fn) {
let proto = this.__proto__;
if (!proto.hasOwnProperty(name)) {
proto = this;
static pre(name, fn) {
const proto = this;

if (is.array(fn)) {
return fn.forEach((middleware) => {
proto.pre.call(proto, name, middleware);
});
}

const pres = proto.__pres = proto.__pres || {};

this._lazySetupHooks(proto, name);
proto.__setupHooks(proto, name);

pres[name].push(fn);

return this;
return proto;
}

post(name, fn) {
let proto = this.__proto__;
if (!proto.hasOwnProperty(name)) {
proto = this;
static post(name, fn) {
const proto = this;

if (is.array(fn)) {
return fn.forEach((middleware) => {
proto.post.call(proto, name, middleware);
});
}

const posts = proto.__posts = proto.__posts || {};

this._lazySetupHooks(proto, name);
proto.__setupHooks(proto, name);

posts[name].push(fn);

return this;
return proto;
}

_lazySetupHooks(proto, methodName) {
if (proto[methodName].__hooked !== true) {
static __setupHooks(proto, methodName) {
if (proto[methodName] && proto[methodName].__hooked !== true) {
this.hook(methodName, proto[methodName]);
}
}
}

module.exports = new HooksPromise();
// Helpers
function getScope(self, hookName, args) {
return self.__scopeHook &&
typeof self.__scopeHook === 'function' &&
typeof self.__scopeHook(hookName, args) !== 'undefined' ?
self.__scopeHook(hookName, args) : self;
}

module.exports = HooksPromise;
14 changes: 13 additions & 1 deletion mocks/model.mock.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use strict';

class MyClass{
save() {
Expand All @@ -7,4 +8,15 @@ class MyClass{
}
};

module.exports = MyClass;
class MyOtherClass{
save() {
return new Promise((resolve, reject) => {
resolve({ data: [1, 2, 3] });
});
}
};

module.exports = {
MyClass,
MyOtherClass
};
Loading

0 comments on commit ad308b1

Please sign in to comment.