diff --git a/lib/dn.js b/lib/dn.js index d41df44..b03ce15 100644 --- a/lib/dn.js +++ b/lib/dn.js @@ -1,6 +1,10 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. +var assert = require('assert-plus'); + + +///--- Helpers function invalidDN(name) { var e = new Error(); @@ -9,18 +13,26 @@ function invalidDN(name) { return e; } - function isAlphaNumeric(c) { var re = /[A-Za-z0-9]/; return re.test(c); } - function isWhitespace(c) { var re = /\s/; return re.test(c); } +function repeatChar(c, n) { + var out = ''; + var max = n ? n : 0; + for (var i = 0; i < max; i++) + out += c; + return out; +} + +///--- API + function RDN(obj) { var self = this; this.attrs = {}; @@ -32,12 +44,10 @@ function RDN(obj) { } } +RDN.prototype.set = function rdnSet(name, value, opts) { + assert.string(name, 'name (string) required'); + assert.string(value, 'value (string) required'); -RDN.prototype.set = function set(name, value, opts) { - if (typeof (name) !== 'string') - throw new TypeError('name (string) required'); - if (typeof (value) !== 'string') - throw new TypeError('value (string) required'); var self = this; var lname = name.toLowerCase(); this.attrs[lname] = { @@ -52,8 +62,7 @@ RDN.prototype.set = function set(name, value, opts) { } }; - -RDN.prototype.equals = function equals(rdn) { +RDN.prototype.equals = function rdnEquals(rdn) { if (typeof (rdn) !== 'object') return false; @@ -79,13 +88,10 @@ RDN.prototype.equals = function equals(rdn) { * Convert RDN to string according to specified formatting options. * (see: DN.format for option details) */ -RDN.prototype.format = function format(options) { - if (options) { - if (typeof (options) !== 'object') - throw new TypeError('options must be an object'); - } else { - options = {}; - } +RDN.prototype.format = function rdnFormat(options) { + assert.optionalObject(options, 'options must be an object'); + options = options || {}; + var self = this; var str = ''; @@ -154,8 +160,7 @@ RDN.prototype.format = function format(options) { return str; }; - -RDN.prototype.toString = function toString() { +RDN.prototype.toString = function rdnToString() { return this.format(); }; @@ -314,26 +319,18 @@ function parse(name) { } - -///--- API - - function DN(rdns) { - if (!Array.isArray(rdns)) - throw new TypeError('rdns ([object]) required'); - rdns.forEach(function (rdn) { - if (typeof (rdn) !== 'object') - throw new TypeError('rdns ([object]) required'); - }); + assert.optionalArrayOfObject(rdns, '[object] required'); - this.rdns = rdns.slice(); + this.rdns = rdns ? rdns.slice() : []; this._format = {}; - - this.__defineGetter__('length', function () { - return this.rdns.length; - }); } - +Object.defineProperties(DN.prototype, { + length: { + get: function getLength() { return this.rdns.length; }, + configurable: false + } +}); /** * Convert DN to string according to specified formatting options. @@ -358,21 +355,11 @@ function DN(rdns) { * - upperName: RDN names will be uppercased instead of lowercased. * - skipSpace: Disable trailing space after RDN separators */ -DN.prototype.format = function (options) { - if (options) { - if (typeof (options) !== 'object') - throw new TypeError('options must be an object'); - } else { - options = this._format; - } +DN.prototype.format = function dnFormat(options) { + assert.optionalObject(options, 'options must be an object'); + options = options || this._format; + var str = ''; - function repeatChar(c, n) { - var out = ''; - var max = n ? n : 0; - for (var i = 0; i < max; i++) - out += c; - return out; - } this.rdns.forEach(function (rdn) { var rdnString = rdn.format(options); if (str.length !== 0) { @@ -390,22 +377,19 @@ DN.prototype.format = function (options) { return str; }; - /** * Set default string formatting options. */ DN.prototype.setFormat = function setFormat(options) { - if (typeof (options) !== 'object') - throw new TypeError('options must be an object'); + assert.object(options, 'options must be an object'); + this._format = options; }; - -DN.prototype.toString = function () { +DN.prototype.toString = function dnToString() { return this.format(); }; - DN.prototype.parentOf = function parentOf(dn) { if (typeof (dn) !== 'object') dn = parse(dn); @@ -425,20 +409,17 @@ DN.prototype.parentOf = function parentOf(dn) { return true; }; - DN.prototype.childOf = function childOf(dn) { if (typeof (dn) !== 'object') dn = parse(dn); return dn.parentOf(this); }; - DN.prototype.isEmpty = function isEmpty() { return (this.rdns.length === 0); }; - -DN.prototype.equals = function (dn) { +DN.prototype.equals = function dnEquals(dn) { if (typeof (dn) !== 'object') dn = parse(dn); @@ -453,8 +434,7 @@ DN.prototype.equals = function (dn) { return true; }; - -DN.prototype.parent = function () { +DN.prototype.parent = function dnParent() { if (this.rdns.length !== 0) { var save = this.rdns.shift(); var dn = new DN(this.rdns); @@ -465,47 +445,38 @@ DN.prototype.parent = function () { return null; }; - -DN.prototype.clone = function () { +DN.prototype.clone = function dnClone() { var dn = new DN(this.rdns); dn._format = this._format; return dn; }; - -DN.prototype.reverse = function () { +DN.prototype.reverse = function dnReverse() { this.rdns.reverse(); return this; }; - -DN.prototype.pop = function () { +DN.prototype.pop = function dnPop() { return this.rdns.pop(); }; - -DN.prototype.push = function (rdn) { - if (typeof (rdn) !== 'object') - throw new TypeError('rdn (RDN) required'); +DN.prototype.push = function dnPush(rdn) { + assert.object(rdn, 'rdn (RDN) required'); return this.rdns.push(rdn); }; - -DN.prototype.shift = function () { +DN.prototype.shift = function dnShift() { return this.rdns.shift(); }; - -DN.prototype.unshift = function (rdn) { - if (typeof (rdn) !== 'object') - throw new TypeError('rdn (RDN) required'); +DN.prototype.unshift = function dnUnshift(rdn) { + assert.object(rdn, 'rdn (RDN) required'); return this.rdns.unshift(rdn); }; - -DN.isDN = function (dn) { +DN.isDN = function isDN(dn) { if (!dn || typeof (dn) !== 'object') { return false; } @@ -523,11 +494,7 @@ DN.isDN = function (dn) { ///--- Exports module.exports = { - parse: parse, - DN: DN, - RDN: RDN - }; diff --git a/test/dn.test.js b/test/dn.test.js index 796d212..d86ddcf 100644 --- a/test/dn.test.js +++ b/test/dn.test.js @@ -3,13 +3,11 @@ var test = require('tape').test; - ///--- Globals var dn; - ///--- Tests test('load library', function (t) { @@ -205,6 +203,51 @@ test('format persists across clone', function (t) { }); +test('initialization', function (t) { + var dn1 = new dn.DN(); + t.ok(dn1); + t.equals(dn1.toString(), ''); + t.ok(dn1.isEmpty(), 'DN with no initializer defaults to null DN'); + + var data = [ + new dn.RDN({ foo: 'bar' }), + new dn.RDN({ o: 'base' }) + ]; + var dn2 = new dn.DN(data); + t.ok(dn2); + t.equals(dn2.toString(), 'foo=bar, o=base'); + t.ok(!dn2.isEmpty()); + + t.end(); +}); + + +test('array functions', function (t) { + var dn1 = dn.parse('a=foo, b=bar, c=baz'); + t.ok(dn1); + t.equal(dn1.toString(), 'a=foo, b=bar, c=baz'); + + t.ok(dn1.reverse()); + t.equal(dn1.toString(), 'c=baz, b=bar, a=foo'); + + var rdn = dn1.pop(); + t.ok(rdn); + t.equal(dn1.toString(), 'c=baz, b=bar'); + + t.ok(dn1.push(rdn)); + t.equal(dn1.toString(), 'c=baz, b=bar, a=foo'); + + rdn = dn1.shift(); + t.ok(rdn); + t.equal(dn1.toString(), 'b=bar, a=foo'); + + t.ok(dn1.unshift(rdn)); + t.equal(dn1.toString(), 'c=baz, b=bar, a=foo'); + + t.end(); +}); + + test('isDN duck-testing', function (t) { var valid = dn.parse('cn=foo'); var isDN = dn.DN.isDN;