Add Change.apply method for object modification

This commit is contained in:
Patrick Mooney 2014-07-16 14:53:13 -05:00
parent 26e427f4a0
commit a80cedd870
3 changed files with 190 additions and 1 deletions

View File

@ -2,6 +2,7 @@
## CURRENT
- Add Change.apply method for modifying objects
- Update bunyan to 0.23.1
- #143 Preserve raw Buffer value in Control objects
- Test code coverage with node-istanbul

View File

@ -1,6 +1,6 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert');
var assert = require('assert-plus');
var Attribute = require('./attribute');
var Protocol = require('./protocol');
@ -111,6 +111,68 @@ Change.compare = function (a, b) {
return Attribute.compare(a.modification, b.modification);
};
/**
* Apply a Change to properties of an object.
*
* @param {Object} change the change to apply.
* @param {Object} obj the object to apply it to.
* @param {Boolean} scalar convert single-item arrays to scalars. Default: false
*/
Change.apply = function apply(change, obj, scalar) {
assert.string(change.operation);
assert.string(change.modification.type);
assert.ok(Array.isArray(change.modification.vals));
assert.object(obj);
var type = change.modification.type;
var vals = change.modification.vals;
var data = obj[type];
if (data !== undefined) {
if (!Array.isArray(data)) {
data = [data];
}
} else {
data = [];
}
switch (change.operation) {
case 'replace':
if (vals.length === 0) {
// replace empty is a delete
delete obj[type];
return obj;
} else {
data = vals;
}
break;
case 'add':
// add only new unique entries
var newValues = vals.filter(function (entry) {
return (data.indexOf(entry) === -1);
});
data = data.concat(newValues);
break;
case 'delete':
data = data.filter(function (entry) {
return (vals.indexOf(entry) === -1);
});
if (data.length === 0) {
// Erase the attribute if empty
delete obj[type];
return obj;
}
break;
default:
break;
}
if (scalar && data.length === 1) {
// store single-value outputs as scalars, if requested
obj[type] = data[0];
} else {
obj[type] = data;
}
return obj;
};
Change.prototype.parse = function (ber) {
assert.ok(ber);

View File

@ -114,3 +114,129 @@ test('parse', function (t) {
t.end();
});
test('apply - replace', function (t) {
var res;
var single = new Change({
operation: 'replace',
modification: {
type: 'cn',
vals: ['new']
}
});
var twin = new Change({
operation: 'replace',
modification: {
type: 'cn',
vals: ['new', 'two']
}
});
// plain
res = Change.apply(single, { cn: ['old'] });
t.ok(res.cn);
t.equal(res.cn.length, 1);
t.equal(res.cn[0], 'new');
// multiple
res = Change.apply(single, { cn: ['old', 'also'] });
t.ok(res.cn);
t.equal(res.cn.length, 1);
t.equal(res.cn[0], 'new');
//absent
res = Change.apply(single, { dn: ['otherjunk'] });
t.ok(res.cn);
t.equal(res.cn.length, 1);
t.equal(res.cn[0], 'new');
// scalar formatting "success"
res = Change.apply(single, { cn: 'old' }, true);
t.ok(res.cn);
t.equal(res.cn, 'new');
// scalar formatting "failure"
res = Change.apply(twin, { cn: 'old' }, true);
t.ok(res.cn);
t.equal(res.cn.length, 2);
t.equal(res.cn[0], 'new');
t.equal(res.cn[1], 'two');
t.end();
});
test('apply - add', function (t) {
var res;
var single = new Change({
operation: 'add',
modification: {
type: 'cn',
vals: ['new']
}
});
// plain
res = Change.apply(single, { cn: ['old'] });
t.deepEqual(res.cn, ['old', 'new']);
// multiple
res = Change.apply(single, { cn: ['old', 'also'] });
t.deepEqual(res.cn, ['old', 'also', 'new']);
//absent
res = Change.apply(single, { dn: ['otherjunk'] });
t.deepEqual(res.cn, ['new']);
// scalar formatting "success"
res = Change.apply(single, { }, true);
t.equal(res.cn, 'new');
// scalar formatting "failure"
res = Change.apply(single, { cn: 'old' }, true);
t.deepEqual(res.cn, ['old', 'new']);
// duplicate add
res = Change.apply(single, { cn: 'new' });
t.deepEqual(res.cn, ['new']);
t.end();
});
test('apply - delete', function (t) {
var res;
var single = new Change({
operation: 'delete',
modification: {
type: 'cn',
vals: ['old']
}
});
// plain
res = Change.apply(single, { cn: ['old', 'new'] });
t.deepEqual(res.cn, ['new']);
// empty
res = Change.apply(single, { cn: ['old'] });
t.equal(res.cn, undefined);
t.ok(Object.keys(res).indexOf('cn') === -1);
// scalar formatting "success"
res = Change.apply(single, { cn: ['old', 'one'] }, true);
t.equal(res.cn, 'one');
// scalar formatting "failure"
res = Change.apply(single, { cn: ['old', 'several', 'items'] }, true);
t.deepEqual(res.cn, ['several', 'items']);
//absent
res = Change.apply(single, { dn: ['otherjunk'] });
t.ok(res);
t.equal(res.cn, undefined);
t.end();
});