Comparing version 1.1.0 to 1.2.0
# History | ||
## 1.2.0 | ||
* Sanely handle circular references in the data when serialising. | ||
## 1.1.0 | ||
@@ -4,0 +8,0 @@ |
{ | ||
"name": "bfj", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "Big-friendly JSON. Asynchronous streaming functions for large JSON data sets.", | ||
@@ -5,0 +5,0 @@ "homepage": "https://github.com/philbooth/bfj", |
@@ -441,2 +441,13 @@ # BFJ | ||
* `options.circular`: | ||
By default, | ||
circular references | ||
will emit an error. | ||
Set this property | ||
to `'ignore'` | ||
if you'd prefer | ||
to silently skip past | ||
circular references | ||
in the data. | ||
The emitted events | ||
@@ -608,2 +619,15 @@ are defined | ||
* `options.circular`: | ||
By default, | ||
circular references | ||
will emit | ||
a 'dataError' event | ||
on the returned stream. | ||
Set this property | ||
to `'ignore'` | ||
if you'd prefer | ||
to silently skip past | ||
circular references | ||
in the data. | ||
#### Example | ||
@@ -616,2 +640,3 @@ | ||
stream.on('end', end); | ||
stream.on('dataError', error); | ||
``` | ||
@@ -695,2 +720,14 @@ | ||
* `options.circular`: | ||
By default, | ||
circular references | ||
will reject | ||
the returned promise. | ||
Set this property | ||
to `'ignore'` | ||
if you'd prefer | ||
to silently skip past | ||
circular references | ||
in the data. | ||
#### Example | ||
@@ -702,2 +739,5 @@ | ||
// :) | ||
}) | ||
.catch(function (error) { | ||
// :( | ||
}); | ||
@@ -784,2 +824,14 @@ ``` | ||
* `options.circular`: | ||
By default, | ||
circular references | ||
will cause the write | ||
to fail. | ||
Set this property | ||
to `'ignore'` | ||
if you'd prefer | ||
to silently skip past | ||
circular references | ||
in the data. | ||
#### Example | ||
@@ -786,0 +838,0 @@ |
@@ -5,7 +5,6 @@ /*globals require, module, setImmediate, Promise, Buffer, Map */ | ||
var check, EventEmitter, error, events; | ||
var check, EventEmitter, events; | ||
check = require('check-types'); | ||
EventEmitter = require('events').EventEmitter; | ||
error = require('./error'); | ||
events = require('./events'); | ||
@@ -30,9 +29,12 @@ | ||
* | ||
* @option maps: 'object', or 'ignore', default is 'object'. | ||
* @option maps: 'object' or 'ignore', default is 'object'. | ||
* | ||
* @option iterables: 'array', or 'ignore', default is 'array'. | ||
* @option iterables: 'array' or 'ignore', default is 'array'. | ||
* | ||
* @option circular: 'error' or 'ignore', default is 'error'. | ||
**/ | ||
function eventify (data, options) { | ||
var coercions, emitter; | ||
var references, coercions, emitter, ignoreCircularReferences, ignoreItems; | ||
references = []; | ||
coercions = {}; | ||
@@ -54,2 +56,6 @@ emitter = new EventEmitter(); | ||
normaliseOption('iterables'); | ||
if (options.circular === 'ignore') { | ||
ignoreCircularReferences = true; | ||
} | ||
} | ||
@@ -180,8 +186,19 @@ | ||
function array (datum) { | ||
return collection(datum, 'array', proceed); | ||
// For an array, collection:object and collection:array are the same. | ||
return collection(datum, datum, 'array', proceed); | ||
} | ||
function collection (c, type, action) { | ||
var resolve; | ||
function collection (object, array, type, action) { | ||
var ignoreThisItem, resolve; | ||
if (references.indexOf(object) >= 0) { | ||
ignoreThisItem = ignoreItems = true; | ||
if (! ignoreCircularReferences) { | ||
emitter.emit(events.error, new Error('Circular reference.')); | ||
} | ||
} else { | ||
references.push(object); | ||
} | ||
emitter.emit(events[type]); | ||
@@ -196,3 +213,11 @@ | ||
function item (index) { | ||
if (index >= c.length) { | ||
if (index >= array.length) { | ||
if (ignoreThisItem) { | ||
ignoreItems = false; | ||
} | ||
if (ignoreItems) { | ||
return; | ||
} | ||
emitter.emit(events.endPrefix + events[type]); | ||
@@ -203,3 +228,7 @@ | ||
action(c[index]).then(item.bind(null, index + 1)); | ||
if (ignoreItems) { | ||
return item(index + 1); | ||
} | ||
action(array[index]).then(item.bind(null, index + 1)); | ||
} | ||
@@ -209,3 +238,4 @@ } | ||
function object (datum) { | ||
return collection(Object.keys(datum), 'object', function (key) { | ||
// For an object, collection:object and collection:array are different. | ||
return collection(datum, Object.keys(datum), 'object', function (key) { | ||
emitter.emit(events.property, key); | ||
@@ -212,0 +242,0 @@ |
@@ -32,5 +32,7 @@ /*globals require, module */ | ||
* | ||
* @option maps: 'object', or 'ignore', default is 'object'. | ||
* @option maps: 'object' or 'ignore', default is 'object'. | ||
* | ||
* @option iterables: 'array', or 'ignore', default is 'array'. | ||
* @option iterables: 'array' or 'ignore', default is 'array'. | ||
* | ||
* @option circular: 'error' or 'ignore', default is 'error'. | ||
**/ | ||
@@ -61,2 +63,3 @@ function streamify (data, options) { | ||
emitter.on(events.end, end); | ||
emitter.on(events.error, error); | ||
@@ -219,3 +222,7 @@ return stream; | ||
} | ||
function error (error) { | ||
stream.emit('dataError', error); | ||
} | ||
} | ||
@@ -27,8 +27,10 @@ /*globals require, module, Promise */ | ||
* | ||
* @option maps: 'object', or 'ignore', default is 'object'. | ||
* @option maps: 'object' or 'ignore', default is 'object'. | ||
* | ||
* @option iterables: 'array', or 'ignore', default is 'array'. | ||
* @option iterables: 'array' or 'ignore', default is 'array'. | ||
* | ||
* @option circular: 'error' or 'ignore', default is 'error'. | ||
**/ | ||
function stringify (data, options) { | ||
var stream, json, resolve; | ||
var stream, json, resolve, reject; | ||
@@ -40,5 +42,7 @@ stream = streamify(data, options); | ||
stream.on('end', end); | ||
stream.on('dataError', error); | ||
return new Promise(function (res) { | ||
return new Promise(function (res, rej) { | ||
resolve = res; | ||
reject = rej; | ||
}); | ||
@@ -53,3 +57,7 @@ | ||
} | ||
function error (e) { | ||
reject(e); | ||
} | ||
} | ||
@@ -32,5 +32,7 @@ /*globals require, module, Promise */ | ||
* | ||
* @option maps: 'object', or 'ignore', default is 'object'. | ||
* @option maps: 'object' or 'ignore', default is 'object'. | ||
* | ||
* @option iterables: 'array', or 'ignore', default is 'array'. | ||
* @option iterables: 'array' or 'ignore', default is 'array'. | ||
* | ||
* @option circular: 'error' or 'ignore', default is 'error'. | ||
**/ | ||
@@ -43,7 +45,7 @@ function write (path, data, options) { | ||
resolve(); | ||
}).on('error', function (error) { | ||
reject(error); | ||
}); | ||
}) | ||
.on('error', reject) | ||
.on('dataError', reject); | ||
}); | ||
} | ||
@@ -1469,4 +1469,182 @@ 'use strict'; | ||
}); | ||
suite('array circular reference:', function () { | ||
setup(function (done) { | ||
var array, emitter; | ||
array = [ 'foo' ]; | ||
array[1] = array; | ||
emitter = eventify(array); | ||
Object.keys(events).forEach(function (key) { | ||
emitter.on(events[key], spooks.fn({ | ||
name: key, | ||
log: log | ||
})); | ||
}); | ||
emitter.on(events.end, done); | ||
}); | ||
test('array event occurred twice', function () { | ||
assert.strictEqual(log.counts.array, 2); | ||
}); | ||
test('string event occurred once', function () { | ||
assert.strictEqual(log.counts.string, 1); | ||
}); | ||
test('string event was dispatched correctly', function () { | ||
assert.strictEqual(log.args.string[0][0], 'foo'); | ||
}); | ||
test('endArray event occurred twice', function () { | ||
assert.strictEqual(log.counts.endArray, 2); | ||
}); | ||
test('end event occurred once', function () { | ||
assert.strictEqual(log.counts.end, 1); | ||
}); | ||
test('error event occurred once', function () { | ||
assert.strictEqual(log.counts.error, 1); | ||
}); | ||
test('error event was dispatched correctly', function () { | ||
assert.lengthOf(log.args.error[0], 1); | ||
assert.instanceOf(log.args.error[0][0], Error); | ||
assert.strictEqual(log.args.error[0][0].message, 'Circular reference.'); | ||
}); | ||
}); | ||
suite('object circular reference:', function () { | ||
setup(function (done) { | ||
var object, emitter; | ||
object = { foo: 'bar' }; | ||
object.self = object; | ||
emitter = eventify(object); | ||
Object.keys(events).forEach(function (key) { | ||
emitter.on(events[key], spooks.fn({ | ||
name: key, | ||
log: log | ||
})); | ||
}); | ||
emitter.on(events.end, done); | ||
}); | ||
test('object event occurred twice', function () { | ||
assert.strictEqual(log.counts.object, 2); | ||
}); | ||
test('property event occurred twice', function () { | ||
assert.strictEqual(log.counts.property, 2); | ||
}); | ||
test('property event was dispatched correctly first time', function () { | ||
assert.strictEqual(log.args.property[0][0], 'foo'); | ||
}); | ||
test('property event was dispatched correctly second time', function () { | ||
assert.strictEqual(log.args.property[1][0], 'self'); | ||
}); | ||
test('endObject event occurred twice', function () { | ||
assert.strictEqual(log.counts.endObject, 2); | ||
}); | ||
test('end event occurred once', function () { | ||
assert.strictEqual(log.counts.end, 1); | ||
}); | ||
test('error event occurred once', function () { | ||
assert.strictEqual(log.counts.error, 1); | ||
}); | ||
test('error event was dispatched correctly', function () { | ||
assert.strictEqual(log.args.error[0][0].message, 'Circular reference.'); | ||
}); | ||
}); | ||
suite('array circular reference with ignore set:', function () { | ||
setup(function (done) { | ||
var array, emitter; | ||
array = [ 'foo' ]; | ||
array[1] = array; | ||
emitter = eventify(array, { circular: 'ignore' }); | ||
Object.keys(events).forEach(function (key) { | ||
emitter.on(events[key], spooks.fn({ | ||
name: key, | ||
log: log | ||
})); | ||
}); | ||
emitter.on(events.end, done); | ||
}); | ||
test('array event occurred twice', function () { | ||
assert.strictEqual(log.counts.array, 2); | ||
}); | ||
test('string event occurred once', function () { | ||
assert.strictEqual(log.counts.string, 1); | ||
}); | ||
test('endArray event occurred twice', function () { | ||
assert.strictEqual(log.counts.endArray, 2); | ||
}); | ||
test('end event occurred once', function () { | ||
assert.strictEqual(log.counts.end, 1); | ||
}); | ||
test('error event did not occur', function () { | ||
assert.strictEqual(log.counts.error, 0); | ||
}); | ||
}); | ||
suite('object circular reference with ignore set:', function () { | ||
setup(function (done) { | ||
var object, emitter; | ||
object = { foo: 'bar' }; | ||
object.self = object; | ||
emitter = eventify(object, { circular: 'ignore' }); | ||
Object.keys(events).forEach(function (key) { | ||
emitter.on(events[key], spooks.fn({ | ||
name: key, | ||
log: log | ||
})); | ||
}); | ||
emitter.on(events.end, done); | ||
}); | ||
test('object event occurred twice', function () { | ||
assert.strictEqual(log.counts.object, 2); | ||
}); | ||
test('property event occurred twice', function () { | ||
assert.strictEqual(log.counts.property, 2); | ||
}); | ||
test('endObject event occurred twice', function () { | ||
assert.strictEqual(log.counts.endObject, 2); | ||
}); | ||
test('end event occurred once', function () { | ||
assert.strictEqual(log.counts.end, 1); | ||
}); | ||
test('error event did not occur', function () { | ||
assert.strictEqual(log.counts.error, 0); | ||
}); | ||
}); | ||
}); | ||
}); | ||
@@ -36,3 +36,3 @@ 'use strict'; | ||
log: log, | ||
archetype: { instance: { push: function () {} } }, | ||
archetype: { instance: { push: function () {}, emit: function () {} } }, | ||
results: results | ||
@@ -133,4 +133,4 @@ })); | ||
test('EventEmitter.on was called nine times', function () { | ||
assert.strictEqual(log.counts.on, 9); | ||
test('EventEmitter.on was called ten times', function () { | ||
assert.strictEqual(log.counts.on, 10); | ||
assert.strictEqual(log.these.on[0], results.eventify[0]); | ||
@@ -145,2 +145,3 @@ assert.strictEqual(log.these.on[1], results.eventify[0]); | ||
assert.strictEqual(log.these.on[8], results.eventify[0]); | ||
assert.strictEqual(log.these.on[9], results.eventify[0]); | ||
}); | ||
@@ -231,2 +232,16 @@ | ||
test('EventEmitter.on was called correctly tenth time', function () { | ||
assert.lengthOf(log.args.on[9], 2); | ||
assert.strictEqual(log.args.on[9][0], 'err'); | ||
assert.isFunction(log.args.on[9][1]); | ||
assert.notStrictEqual(log.args.on[9][1], log.args.on[0][1]); | ||
assert.notStrictEqual(log.args.on[9][1], log.args.on[1][1]); | ||
assert.notStrictEqual(log.args.on[9][1], log.args.on[2][1]); | ||
assert.notStrictEqual(log.args.on[9][1], log.args.on[3][1]); | ||
assert.notStrictEqual(log.args.on[9][1], log.args.on[4][1]); | ||
assert.notStrictEqual(log.args.on[9][1], log.args.on[6][1]); | ||
assert.notStrictEqual(log.args.on[9][1], log.args.on[7][1]); | ||
assert.notStrictEqual(log.args.on[9][1], log.args.on[8][1]); | ||
}); | ||
suite('array event:', function () { | ||
@@ -271,2 +286,6 @@ setup(function () { | ||
}); | ||
test('stream.emit was not called', function () { | ||
assert.strictEqual(log.counts.emit, 0); | ||
}); | ||
}); | ||
@@ -300,2 +319,6 @@ }); | ||
}); | ||
test('stream.emit was not called', function () { | ||
assert.strictEqual(log.counts.emit, 0); | ||
}); | ||
}); | ||
@@ -421,2 +444,6 @@ | ||
}); | ||
test('stream.emit was not called', function () { | ||
assert.strictEqual(log.counts.emit, 0); | ||
}); | ||
}); | ||
@@ -560,2 +587,6 @@ }); | ||
}); | ||
test('stream.emit was not called', function () { | ||
assert.strictEqual(log.counts.emit, 0); | ||
}); | ||
}); | ||
@@ -611,2 +642,6 @@ }); | ||
}); | ||
test('stream.emit was not called', function () { | ||
assert.strictEqual(log.counts.emit, 0); | ||
}); | ||
}); | ||
@@ -648,2 +683,6 @@ | ||
}); | ||
test('stream.emit was not called', function () { | ||
assert.strictEqual(log.counts.emit, 0); | ||
}); | ||
}) | ||
@@ -669,2 +708,6 @@ }); | ||
}); | ||
test('stream.emit was not called', function () { | ||
assert.strictEqual(log.counts.emit, 0); | ||
}); | ||
}); | ||
@@ -689,2 +732,6 @@ }); | ||
}); | ||
test('stream.emit was not called', function () { | ||
assert.strictEqual(log.counts.emit, 0); | ||
}); | ||
}); | ||
@@ -715,4 +762,4 @@ }); | ||
test('EventEmitter.on was called nine times', function () { | ||
assert.strictEqual(log.counts.on, 9); | ||
test('EventEmitter.on was called ten times', function () { | ||
assert.strictEqual(log.counts.on, 10); | ||
}); | ||
@@ -893,2 +940,6 @@ | ||
}); | ||
test('stream.emit was not called', function () { | ||
assert.strictEqual(log.counts.emit, 0); | ||
}); | ||
}); | ||
@@ -925,3 +976,23 @@ }); | ||
}); | ||
test('stream.emit was not called', function () { | ||
assert.strictEqual(log.counts.emit, 0); | ||
}); | ||
}); | ||
suite('error event:', function () { | ||
setup(function () { | ||
log.args.on[9][1]('foo'); | ||
}); | ||
test('stream.emit was called once', function () { | ||
assert.strictEqual(log.counts.emit, 1); | ||
}); | ||
test('stream.emit was called correctly', function () { | ||
assert.lengthOf(log.args.emit[0], 2); | ||
assert.strictEqual(log.args.emit[0][0], 'dataError'); | ||
assert.strictEqual(log.args.emit[0][1], 'foo'); | ||
}); | ||
}); | ||
}); | ||
@@ -928,0 +999,0 @@ }); |
@@ -105,6 +105,7 @@ 'use strict'; | ||
test('stream.on was called twice', function () { | ||
assert.strictEqual(log.counts.on, 2); | ||
test('stream.on was called three times', function () { | ||
assert.strictEqual(log.counts.on, 3); | ||
assert.strictEqual(log.these.on[0], require('./streamify')()); | ||
assert.strictEqual(log.these.on[1], require('./streamify')()); | ||
assert.strictEqual(log.these.on[2], require('./streamify')()); | ||
}); | ||
@@ -124,2 +125,9 @@ | ||
test('stream.on was called correctly third time', function () { | ||
assert.strictEqual(log.args.on[2][0], 'dataError'); | ||
assert.isFunction(log.args.on[2][1]); | ||
assert.notStrictEqual(log.args.on[2][1], log.args.on[0][1]); | ||
assert.notStrictEqual(log.args.on[2][1], log.args.on[1][1]); | ||
}); | ||
test('promise is unfulfilled', function () { | ||
@@ -175,2 +183,13 @@ assert.isUndefined(resolved); | ||
}); | ||
suite('dataError event:', function () { | ||
setup(function (d) { | ||
done = d; | ||
log.args.on[2][1]('wibble'); | ||
}); | ||
test('promise is rejected', function () { | ||
assert.strictEqual(rejected, 'wibble'); | ||
}); | ||
}); | ||
}); | ||
@@ -177,0 +196,0 @@ }); |
@@ -148,5 +148,7 @@ 'use strict'; | ||
test('stream.on was called twice', function () { | ||
assert.strictEqual(log.counts.on, 2); | ||
test('stream.on was called three times', function () { | ||
assert.strictEqual(log.counts.on, 3); | ||
assert.strictEqual(log.these.on[0], require('./streamify')()); | ||
assert.strictEqual(log.these.on[1], require('./streamify')()); | ||
assert.strictEqual(log.these.on[2], require('./streamify')()); | ||
}); | ||
@@ -167,2 +169,10 @@ | ||
test('stream.on was called correctly third time', function () { | ||
assert.lengthOf(log.args.on[2], 2); | ||
assert.strictEqual(log.args.on[2][0], 'dataError'); | ||
assert.isFunction(log.args.on[2][1]); | ||
assert.notStrictEqual(log.args.on[2][1], log.args.on[0][1]); | ||
assert.strictEqual(log.args.on[2][1], log.args.on[1][1]); | ||
}); | ||
test('promise was returned', function () { | ||
@@ -229,2 +239,31 @@ assert.instanceOf(result, Promise); | ||
}); | ||
suite('dispatch dataError event:', function () { | ||
var resolved, error, passed, failed; | ||
setup(function (done) { | ||
passed = failed = false; | ||
result.then(function (r) { | ||
resolved = r; | ||
passed = true; | ||
done(); | ||
}).catch(function (e) { | ||
error = e; | ||
failed = true; | ||
done(); | ||
}); | ||
log.args.on[2][1]('wibble'); | ||
}); | ||
teardown(function () { | ||
resolved = error = passed = failed = undefined; | ||
}); | ||
test('promise was rejected', function () { | ||
assert.isTrue(failed); | ||
assert.isFalse(passed); | ||
assert.strictEqual(error, 'wibble'); | ||
}); | ||
}); | ||
}); | ||
@@ -231,0 +270,0 @@ }); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
321667
6897
901