Have

Coding a "defensive" or a "contractual" API is a design decision, if you opt for the defensive way, here is a neat module called have that will help you to have your arguments, and validate it too!

Here is a simple example of two functions straight from the source, that does the same argument validations, the first one is without using have and the second one is with have:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function withoutHave(id, arr, opts, callback) {
  assert(typeof id === 'string' || typeof id === 'number',
    'id argument not string or number');

  if (!(arr instanceof Array)) { arr = [arr]; }
  for (var i = 0; i < arr.length; i++) {
    assert(typeof arr[i] === 'string', 'arr member not a string');
  }

  if (typeof opts === 'function') {
    callback = opts;
    opts = { x: 'some default value' };
  }

  assert(!opts || typeof opts === 'object', 'options object not a hash');
  assert(typeof callback === 'function', 'callback missing or not a function');

  // logic...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function withHave(id, arr, opts, callback) {
have(arguments,
    { id: 'str or num'
    , arr: 'str or str array'
    , opts: 'optional obj'
    , callback: 'func' });

  if (!(arr instanceof Array)) { arr = [arr]; }

  if (typeof opts === 'function') {
    callback = opts;
    opts = { x: 'some default value' };
  }

  // logic...
}

How's have better than any other argument validator?

Have provides:

  • mini-DSL

  • Soft assertion.

  • Shorter notations.

You can do a simple assertion with have like:

1
2
3
4
5
have.assert(function(cond, message) {
  if (!cond) {
    console.log('WARN: assertion failed: ' + message);
  }
});

You can also wrap the exported have function, in case you want to log the function name as well:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var assert = require('assert')
  , have = require('have')
  , funcName = "";

have.assert(function(cond, msg) {
  assert(cond, 'inside function: ' + funcName + ', ' + msg);
});

have = (function(have_) {
  return function(args, schema) {
    funcName = args.callee.name;
    have_(args, schema);
    funcName = "";
  };
})(have);


function test() {
  have(arguments, { one: 'string' });
}

test(123);

That would result in a output like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
assert.js:92
  throw new assert.AssertionError({
        ^
AssertionError: inside function: test, one argument is not string
    at funcName (/private/tmp/k.js:6:3)
    at ensure (/private/tmp/node_modules/have/have.js:109:5)
    at have (/private/tmp/node_modules/have/have.js:124:11)
    at have.one (/private/tmp/k.js:12:5)
    at test (/private/tmp/k.js:19:3)
    at Object.<anonymous> (/private/tmp/k.js:22:1)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)

GIF FTW!

Thanks to Stdarg for suggesting this module and also a special thanks to chakrit the author of 'have' module

Hope you liked have have fun! :)

Suggest a module

Comments