包详细信息

kareem

mongoosejs12mApache-2.02.6.3

Next-generation take on pre/post function hooks

自述文件

kareem

Build Status

Re-imagined take on the hooks module, meant to offer additional flexibility in allowing you to execute hooks whenever necessary, as opposed to simply wrapping a single function.

Named for the NBA's all-time leading scorer Kareem Abdul-Jabbar, known for his mastery of the hook shot

API

pre hooks

Much like hooks, kareem lets you define pre and post hooks: pre hooks are called before a given function executes. Unlike hooks, kareem stores hooks and other internal state in a separate object, rather than relying on inheritance. Furthermore, kareem exposes an execPre() function that allows you to execute your pre hooks when appropriate, giving you more fine-grained control over your function hooks.

It runs without any hooks specified

hooks.execPre('cook', null, function() {
  // ...
});

It runs basic serial pre hooks

pre hook functions take one parameter, a "done" function that you execute when your pre hook is finished.

var count = 0;

hooks.pre('cook', function(done) {
  ++count;
  done();
});

hooks.execPre('cook', null, function() {
  assert.equal(1, count);
});

It can run multipe pre hooks

var count1 = 0;
var count2 = 0;

hooks.pre('cook', function(done) {
  ++count1;
  done();
});

hooks.pre('cook', function(done) {
  ++count2;
  done();
});

hooks.execPre('cook', null, function() {
  assert.equal(1, count1);
  assert.equal(1, count2);
});

It can run fully synchronous pre hooks

If your pre hook function takes no parameters, its assumed to be fully synchronous.

var count1 = 0;
var count2 = 0;

hooks.pre('cook', function() {
  ++count1;
});

hooks.pre('cook', function() {
  ++count2;
});

hooks.execPre('cook', null, function(error) {
  assert.equal(null, error);
  assert.equal(1, count1);
  assert.equal(1, count2);
});

It properly attaches context to pre hooks

Pre save hook functions are bound to the second parameter to execPre()

hooks.pre('cook', function(done) {
  this.bacon = 3;
  done();
});

hooks.pre('cook', function(done) {
  this.eggs = 4;
  done();
});

var obj = { bacon: 0, eggs: 0 };

// In the pre hooks, `this` will refer to `obj`
hooks.execPre('cook', obj, function(error) {
  assert.equal(null, error);
  assert.equal(3, obj.bacon);
  assert.equal(4, obj.eggs);
});

It can execute parallel (async) pre hooks

Like the hooks module, you can declare "async" pre hooks - these take two parameters, the functions next() and done(). next() passes control to the next pre hook, but the underlying function won't be called until all async pre hooks have called done().

hooks.pre('cook', true, function(next, done) {
  this.bacon = 3;
  next();
  setTimeout(function() {
    done();
  }, 5);
});

hooks.pre('cook', true, function(next, done) {
  next();
  var _this = this;
  setTimeout(function() {
    _this.eggs = 4;
    done();
  }, 10);
});

hooks.pre('cook', function(next) {
  this.waffles = false;
  next();
});

var obj = { bacon: 0, eggs: 0 };

hooks.execPre('cook', obj, function() {
  assert.equal(3, obj.bacon);
  assert.equal(4, obj.eggs);
  assert.equal(false, obj.waffles);
});

It supports returning a promise

You can also return a promise from your pre hooks instead of calling next(). When the returned promise resolves, kareem will kick off the next middleware.

hooks.pre('cook', function() {
  return new Promise(resolve => {
    setTimeout(() => {
      this.bacon = 3;
      resolve();
    }, 100);
  });
});

var obj = { bacon: 0 };

hooks.execPre('cook', obj, function() {
  assert.equal(3, obj.bacon);
});

post hooks

acquit:ignore:end

It runs without any hooks specified

hooks.execPost('cook', null, [1], function(error, eggs) {
  assert.ifError(error);
  assert.equal(1, eggs);
  done();
});

It executes with parameters passed in

hooks.post('cook', function(eggs, bacon, callback) {
  assert.equal(1, eggs);
  assert.equal(2, bacon);
  callback();
});

hooks.execPost('cook', null, [1, 2], function(error, eggs, bacon) {
  assert.ifError(error);
  assert.equal(1, eggs);
  assert.equal(2, bacon);
});

It can use synchronous post hooks

var execed = {};

hooks.post('cook', function(eggs, bacon) {
  execed.first = true;
  assert.equal(1, eggs);
  assert.equal(2, bacon);
});

hooks.post('cook', function(eggs, bacon, callback) {
  execed.second = true;
  assert.equal(1, eggs);
  assert.equal(2, bacon);
  callback();
});

hooks.execPost('cook', null, [1, 2], function(error, eggs, bacon) {
  assert.ifError(error);
  assert.equal(2, Object.keys(execed).length);
  assert.ok(execed.first);
  assert.ok(execed.second);
  assert.equal(1, eggs);
  assert.equal(2, bacon);
});

It supports returning a promise

You can also return a promise from your post hooks instead of calling next(). When the returned promise resolves, kareem will kick off the next middleware.

hooks.post('cook', function(bacon) {
  return new Promise(resolve => {
    setTimeout(() => {
      this.bacon = 3;
      resolve();
    }, 100);
  });
});

var obj = { bacon: 0 };

hooks.execPost('cook', obj, obj, function() {
  assert.equal(obj.bacon, 3);
});

wrap()

acquit:ignore:end

It wraps pre and post calls into one call

hooks.pre('cook', true, function(next, done) {
  this.bacon = 3;
  next();
  setTimeout(function() {
    done();
  }, 5);
});

hooks.pre('cook', true, function(next, done) {
  next();
  var _this = this;
  setTimeout(function() {
    _this.eggs = 4;
    done();
  }, 10);
});

hooks.pre('cook', function(next) {
  this.waffles = false;
  next();
});

hooks.post('cook', function(obj) {
  obj.tofu = 'no';
});

var obj = { bacon: 0, eggs: 0 };

var args = [obj];
args.push(function(error, result) {
  assert.ifError(error);
  assert.equal(null, error);
  assert.equal(3, obj.bacon);
  assert.equal(4, obj.eggs);
  assert.equal(false, obj.waffles);
  assert.equal('no', obj.tofu);

  assert.equal(obj, result);
});

hooks.wrap(
  'cook',
  function(o, callback) {
    assert.equal(3, obj.bacon);
    assert.equal(4, obj.eggs);
    assert.equal(false, obj.waffles);
    assert.equal(undefined, obj.tofu);
    callback(null, o);
  },
  obj,
  args);

createWrapper()

It wraps wrap() into a callable function

hooks.pre('cook', true, function(next, done) {
  this.bacon = 3;
  next();
  setTimeout(function() {
    done();
  }, 5);
});

hooks.pre('cook', true, function(next, done) {
  next();
  var _this = this;
  setTimeout(function() {
    _this.eggs = 4;
    done();
  }, 10);
});

hooks.pre('cook', function(next) {
  this.waffles = false;
  next();
});

hooks.post('cook', function(obj) {
  obj.tofu = 'no';
});

var obj = { bacon: 0, eggs: 0 };

var cook = hooks.createWrapper(
  'cook',
  function(o, callback) {
    assert.equal(3, obj.bacon);
    assert.equal(4, obj.eggs);
    assert.equal(false, obj.waffles);
    assert.equal(undefined, obj.tofu);
    callback(null, o);
  },
  obj);

cook(obj, function(error, result) {
  assert.ifError(error);
  assert.equal(3, obj.bacon);
  assert.equal(4, obj.eggs);
  assert.equal(false, obj.waffles);
  assert.equal('no', obj.tofu);

  assert.equal(obj, result);
});

clone()

acquit:ignore:end

It clones a Kareem object

var k1 = new Kareem();
k1.pre('cook', function() {});
k1.post('cook', function() {});

var k2 = k1.clone();
assert.deepEqual(Array.from(k2._pres.keys()), ['cook']);
assert.deepEqual(Array.from(k2._posts.keys()), ['cook']);

merge()

It pulls hooks from another Kareem object

var k1 = new Kareem();
var test1 = function() {};
k1.pre('cook', test1);
k1.post('cook', function() {});

var k2 = new Kareem();
var test2 = function() {};
k2.pre('cook', test2);
var k3 = k2.merge(k1);
assert.equal(k3._pres.get('cook').length, 2);
assert.equal(k3._pres.get('cook')[0].fn, test2);
assert.equal(k3._pres.get('cook')[1].fn, test1);
assert.equal(k3._posts.get('cook').length, 1);

更新日志

Changelog

2.6.0 (2024-03-04)

  • feat: add TypeScript types

2.5.1 (2023-01-06)

  • fix: avoid passing final callback to pre hook, because calling the callback can mess up hook execution #36 Automattic/mongoose#12836

2.5.0 (2022-12-01)

  • feat: add errorHandler option to post() #34

2.4.0 (2022-06-13)

  • feat: add overwriteResult() and skipWrappedFunction() for more advanced control flow

2.3.4 (2022-02-10)

  • perf: various performance improvements #27 #24 #23 #22 #21 #20

2.3.3 (2021-12-26)

  • fix: handle sync errors in wrap()

2.3.2 (2020-12-08)

  • fix: handle sync errors in pre hooks if there are multiple hooks

2.3.0 (2018-09-24)

2.2.3 (2018-09-10)

2.2.2 (2018-09-10)

  • chore: release 2.2.2 (3f0144d)
  • fix: allow merge() to not clone (e628d65)

2.2.1 (2018-06-05)

  • chore: release 2.2.1 (4625a64)
  • chore: remove lockfile from git (7f3e4e6)
  • fix: handle numAsync correctly when merging (fef8e7e)
  • test: repro issue with not copying numAsync (952d9db)

2.2.0 (2018-06-05)

2.1.0 (2018-05-16)

  • chore: release 2.1.0 (ba5f1bc)
  • feat: add option to check wrapped function return value for promises (c9d7dd1)
  • refactor: use const in wrap() (0fc21f9)

2.0.7 (2018-04-28)

2.0.6 (2018-03-22)

2.0.5 (2018-02-22)

2.0.4 (2018-02-08)

2.0.3 (2018-02-01)

2.0.2 (2018-01-24)

2.0.1 (2018-01-09)

  • chore: release 2.0.1 with lockfile bump (09c44fb)

2.0.0 (2018-01-09)

  • chore: bump marked re: security (cc564a9)
  • chore: release 2.0.0 (f511d1c)

2.0.0-rc5 (2017-12-23)

  • chore: fix build on node 4+5 (6dac5a4)
  • chore: fix built on node 4 + 5 again (434ef0a)
  • chore: release 2.0.0-rc5 (25a32ee)

2.0.0-rc4 (2017-12-22)

2.0.0-rc3 (2017-12-22)

2.0.0-rc2 (2017-12-21)

  • chore: release 2.0.0-rc2 (76325fa)
  • fix: ensure next() and done() run in next tick (6c20684)

2.0.0-rc1 (2017-12-21)

2.0.0-rc0 (2017-12-17)

1.5.0 (2017-07-20)

1.4.2 (2017-07-06)

1.4.1 (2017-04-25)

  • chore: release 1.4.1 (5ecf0c2)
  • fix: handle numAsyncPres with clone() (c72e857), closes #8
  • test: repro #8 (9b4d6b2), closes #8

1.4.0 (2017-04-19)

1.3.0 (2017-03-26)

  • chore: release 1.3.0 (f3a9e50)
  • feat: pass function args to execPre (4dd466d)

1.2.1 (2017-02-03)

1.2.0 (2017-01-02)

1.1.5 (2016-12-13)

1.1.4 (2016-12-09)

  • chore: release 1.1.4 (ece401c)
  • chore: run tests on node 6 (e0cb1cb)
  • fix: only copy own properties in clone() (dfe28ce), closes #7

1.1.3 (2016-06-27)

  • chore: release 1.1.3 (87171c8)
  • fix: couple more issues with arg processing (c65f523)

1.1.2 (2016-06-27)

1.1.1 (2016-06-27)

  • chore: release 1.1.1 (8bb3050)
  • fix: skip error handlers if no error (0eb3a44)

1.1.0 (2016-05-11)

  • chore: release 1.1.0 (85332d9)
  • chore: test on node 4 and node 5 (1faefa1)
  • 100% coverage again (c9aee4e)
  • add support for error post hooks (d378113)
  • basic setup for sync hooks #4 (55aa081), closes #4
  • proof of concept for error handlers (e4a07d9)
  • refactor out handleWrapError helper (b19af38)

1.0.1 (2015-05-10)

1.0.0 (2015-01-28)

0.0.8 (2015-01-27)

0.0.7 (2015-01-04)

0.0.6 (2015-01-01)

  • Update docs and bump 0.0.6 (92c12a7)

0.0.5 (2015-01-01)

0.0.4 (2014-12-13)

  • Bump 0.0.4, run docs generation (51a15fe)
  • Use correct post parameters in wrap() (9bb5da3)

0.0.3 (2014-12-12)

  • Add npm test script, fix small bug with args not getting passed through post (49e3e68)
  • Bump 0.0.3 (65621d8)
  • Update readme (901388b)

0.0.2 (2014-12-12)

  • Add github repo and bump 0.0.2 (59db8be)

0.0.1 (2014-12-12)

2.2.5 (2018-09-24)

2.2.4 (2018-09-24)

2.2.3 (2018-09-24)

2.2.3 (2018-09-10)

2.2.2 (2018-09-10)

  • chore: release 2.2.2 (3f0144d)
  • fix: allow merge() to not clone (e628d65)

2.2.1 (2018-06-05)

  • chore: release 2.2.1 (4625a64)
  • chore: remove lockfile from git (7f3e4e6)
  • fix: handle numAsync correctly when merging (fef8e7e)
  • test: repro issue with not copying numAsync (952d9db)

2.2.0 (2018-06-05)

2.1.0 (2018-05-16)

  • chore: release 2.1.0 (ba5f1bc)
  • feat: add option to check wrapped function return value for promises (c9d7dd1)
  • refactor: use const in wrap() (0fc21f9)

2.0.7 (2018-04-28)

2.0.6 (2018-03-22)

2.0.5 (2018-02-22)

2.0.4 (2018-02-08)

2.0.3 (2018-02-01)

2.0.2 (2018-01-24)

2.0.1 (2018-01-09)

  • chore: release 2.0.1 with lockfile bump (09c44fb)

2.0.0 (2018-01-09)

  • chore: bump marked re: security (cc564a9)
  • chore: release 2.0.0 (f511d1c)

2.0.0-rc5 (2017-12-23)

  • chore: fix build on node 4+5 (6dac5a4)
  • chore: fix built on node 4 + 5 again (434ef0a)
  • chore: release 2.0.0-rc5 (25a32ee)

2.0.0-rc4 (2017-12-22)

2.0.0-rc3 (2017-12-22)

2.0.0-rc2 (2017-12-21)

  • chore: release 2.0.0-rc2 (76325fa)
  • fix: ensure next() and done() run in next tick (6c20684)

2.0.0-rc1 (2017-12-21)

2.0.0-rc0 (2017-12-17)

1.5.0 (2017-07-20)

1.4.2 (2017-07-06)

1.4.1 (2017-04-25)

  • chore: release 1.4.1 (5ecf0c2)
  • fix: handle numAsyncPres with clone() (c72e857), closes #8
  • test: repro #8 (9b4d6b2), closes #8

1.4.0 (2017-04-19)

1.3.0 (2017-03-26)

  • chore: release 1.3.0 (f3a9e50)
  • feat: pass function args to execPre (4dd466d)

1.2.1 (2017-02-03)

1.2.0 (2017-01-02)

1.1.5 (2016-12-13)

1.1.4 (2016-12-09)

  • chore: release 1.1.4 (ece401c)
  • chore: run tests on node 6 (e0cb1cb)
  • fix: only copy own properties in clone() (dfe28ce), closes #7

1.1.3 (2016-06-27)

  • chore: release 1.1.3 (87171c8)
  • fix: couple more issues with arg processing (c65f523)

1.1.2 (2016-06-27)

1.1.1 (2016-06-27)

  • chore: release 1.1.1 (8bb3050)
  • fix: skip error handlers if no error (0eb3a44)

1.1.0 (2016-05-11)

  • chore: release 1.1.0 (85332d9)
  • chore: test on node 4 and node 5 (1faefa1)
  • 100% coverage again (c9aee4e)
  • add support for error post hooks (d378113)
  • basic setup for sync hooks #4 (55aa081), closes #4
  • proof of concept for error handlers (e4a07d9)
  • refactor out handleWrapError helper (b19af38)

1.0.1 (2015-05-10)

1.0.0 (2015-01-28)

0.0.8 (2015-01-27)

0.0.7 (2015-01-04)

0.0.6 (2015-01-01)

  • Update docs and bump 0.0.6 (92c12a7)

0.0.5 (2015-01-01)

0.0.4 (2014-12-13)

  • Bump 0.0.4, run docs generation (51a15fe)
  • Use correct post parameters in wrap() (9bb5da3)

0.0.3 (2014-12-12)

  • Add npm test script, fix small bug with args not getting passed through post (49e3e68)
  • Bump 0.0.3 (65621d8)
  • Update readme (901388b)

0.0.2 (2014-12-12)

  • Add github repo and bump 0.0.2 (59db8be)

0.0.1 (2014-12-12)