index.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. 'use strict';
  2. function Kareem() {
  3. this._pres = {};
  4. this._posts = {};
  5. }
  6. Kareem.prototype.execPre = function(name, context, callback) {
  7. var pres = this._pres[name] || [];
  8. var numPres = pres.length;
  9. var numAsyncPres = pres.numAsync || 0;
  10. var currentPre = 0;
  11. var asyncPresLeft = numAsyncPres;
  12. var done = false;
  13. if (!numPres) {
  14. return process.nextTick(function() {
  15. callback(null);
  16. });
  17. }
  18. var next = function() {
  19. if (currentPre >= numPres) {
  20. return;
  21. }
  22. var pre = pres[currentPre];
  23. if (pre.isAsync) {
  24. pre.fn.call(
  25. context,
  26. function(error) {
  27. if (error) {
  28. if (done) {
  29. return;
  30. }
  31. done = true;
  32. return callback(error);
  33. }
  34. ++currentPre;
  35. next.apply(context, arguments);
  36. },
  37. function(error) {
  38. if (error) {
  39. if (done) {
  40. return;
  41. }
  42. done = true;
  43. return callback(error);
  44. }
  45. if (--numAsyncPres === 0) {
  46. return callback(null);
  47. }
  48. });
  49. } else if (pre.fn.length > 0) {
  50. var args = [function(error) {
  51. if (error) {
  52. if (done) {
  53. return;
  54. }
  55. done = true;
  56. return callback(error);
  57. }
  58. if (++currentPre >= numPres) {
  59. if (asyncPresLeft > 0) {
  60. // Leave parallel hooks to run
  61. return;
  62. } else {
  63. return callback(null);
  64. }
  65. }
  66. next.apply(context, arguments);
  67. }];
  68. if (arguments.length >= 2) {
  69. for (var i = 1; i < arguments.length; ++i) {
  70. args.push(arguments[i]);
  71. }
  72. }
  73. pre.fn.apply(context, args);
  74. } else {
  75. pre.fn.call(context);
  76. if (++currentPre >= numPres) {
  77. if (asyncPresLeft > 0) {
  78. // Leave parallel hooks to run
  79. return;
  80. } else {
  81. return process.nextTick(function() {
  82. callback(null);
  83. });
  84. }
  85. }
  86. next();
  87. }
  88. };
  89. next();
  90. };
  91. Kareem.prototype.execPost = function(name, context, args, callback) {
  92. var posts = this._posts[name] || [];
  93. var numPosts = posts.length;
  94. var currentPost = 0;
  95. if (!numPosts) {
  96. return process.nextTick(function() {
  97. callback.apply(null, [null].concat(args));
  98. });
  99. }
  100. var next = function() {
  101. var post = posts[currentPost];
  102. if (post.length > args.length) {
  103. post.apply(context, args.concat(function(error) {
  104. if (error) {
  105. return callback(error);
  106. }
  107. if (++currentPost >= numPosts) {
  108. return callback.apply(null, [null].concat(args));
  109. }
  110. next();
  111. }));
  112. } else {
  113. post.apply(context, args);
  114. if (++currentPost >= numPosts) {
  115. return callback.apply(null, [null].concat(args));
  116. }
  117. next();
  118. }
  119. };
  120. next();
  121. };
  122. Kareem.prototype.wrap = function(name, fn, context, args, useLegacyPost) {
  123. var lastArg = (args.length > 0 ? args[args.length - 1] : null);
  124. var _this = this;
  125. this.execPre(name, context, function(error) {
  126. if (error) {
  127. if (typeof lastArg === 'function') {
  128. return lastArg(error);
  129. }
  130. return;
  131. }
  132. var end = (typeof lastArg === 'function' ? args.length - 1 : args.length);
  133. fn.apply(context, args.slice(0, end).concat(function() {
  134. if (arguments[0]) {
  135. // Assume error
  136. return typeof lastArg === 'function' ?
  137. lastArg(arguments[0]) :
  138. undefined;
  139. }
  140. if (useLegacyPost && typeof lastArg === 'function') {
  141. lastArg.apply(context, arguments);
  142. }
  143. var argsWithoutError = Array.prototype.slice.call(arguments, 1);
  144. _this.execPost(name, context, argsWithoutError, function() {
  145. if (arguments[0]) {
  146. return typeof lastArg === 'function' ?
  147. lastArg(arguments[0]) :
  148. undefined;
  149. }
  150. return typeof lastArg === 'function' && !useLegacyPost ?
  151. lastArg.apply(context, arguments) :
  152. undefined;
  153. });
  154. }));
  155. });
  156. };
  157. Kareem.prototype.createWrapper = function(name, fn, context) {
  158. var _this = this;
  159. return function() {
  160. var args = Array.prototype.slice.call(arguments);
  161. _this.wrap(name, fn, context, args);
  162. };
  163. };
  164. Kareem.prototype.pre = function(name, isAsync, fn, error) {
  165. if (typeof arguments[1] !== 'boolean') {
  166. error = fn;
  167. fn = isAsync;
  168. isAsync = false;
  169. }
  170. this._pres[name] = this._pres[name] || [];
  171. var pres = this._pres[name];
  172. if (isAsync) {
  173. pres.numAsync = pres.numAsync || 0;
  174. ++pres.numAsync;
  175. }
  176. pres.push({ fn: fn, isAsync: isAsync });
  177. return this;
  178. };
  179. Kareem.prototype.post = function(name, fn) {
  180. (this._posts[name] = this._posts[name] || []).push(fn);
  181. return this;
  182. };
  183. Kareem.prototype.clone = function() {
  184. var n = new Kareem();
  185. for (var key in this._pres) {
  186. n._pres[key] = this._pres[key].slice();
  187. }
  188. for (var key in this._posts) {
  189. n._posts[key] = this._posts[key].slice();
  190. }
  191. return n;
  192. };
  193. module.exports = Kareem;