Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release v0.4.0 #1

Merged
merged 10 commits into from Nov 6, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 13 additions & 5 deletions README.md
@@ -1,4 +1,4 @@
# IteratorStream [![Build Status](https://secure.travis-ci.org/brianloveswords/iterator-stream.png)](http://travis-ci.org/brianloveswords/iterator-stream)
# IteratorStream v0.4.0 [![Build Status](https://secure.travis-ci.org/brianloveswords/iterator-stream.png)](http://travis-ci.org/brianloveswords/iterator-stream)

An adapter for turning an iterator into a streaming iterator.

Expand Down Expand Up @@ -31,7 +31,7 @@ Note that it doesn't have to call `next()`! See `method` below.

### Options
- `separator`: A string added to the end of each computation before
emitting. Defaults to `\n'`. Pass `null` to disable.
emitting.
- `format`: Can be a method or a format string that will be passed to
`util.format`. Defaults to `'%s'`. If given a function, it will be
called like `formatFn(value)` where value is the raw output of the
Expand All @@ -40,15 +40,23 @@ Note that it doesn't have to call `next()`! See `method` below.
computed. If the resulting computations are small, this could be
inefficient. If you pass a `bufferSize`, it will buffer **at least**
that many bytes before emitting a data event.
- `condition`: An optional condition function to run against the output
- `takeWhile`: An optional condition function to run against the output
of every computation. If this check fails, no more data will be send
and an `end` event will be emitted. Useful for infinite iterators.
- `iterations`: Maximum number of iterations to go through before
`end`ing. Defaults to Infinity.
`end`ing.
- `take`: How many items to take before calling it quits. This is
different from `iterations` only when a `filter` is passed – `take` is
counted post-filter, iterations is pre-filter.
- `method`: Name of the method to call over and over again. Defaults to
`"next"`
- `transform`: A method to run on every (non-null) value coming from the
iterator. Defaults to `function (x) { return x; }`
iterator. Defaults to `function (x) { return x }`

#### Application Order
```
transform → takeWhile → filter → format
```

### Example

Expand Down
55 changes: 44 additions & 11 deletions index.js
Expand Up @@ -2,18 +2,28 @@ var util = require('util');
var Stream = require('stream');

function identity(x) { return x };

function alwaysTrue() { return true };

function isDefined(s) {
return (typeof s !== undefined && s !== null && s !== '');
}

function IterStream(iter, options) {
this.buffer = '';
this.iterations = options.iterations || Infinity;
this.bufferSize = options.bufferSize || 0;
this.condition = options.condition || alwaysTrue;
this.format = options.format || '%s';
this.take = options.take || Infinity;

this.method = options.method || 'next';
this.bufferSize = options.bufferSize || 0;

this.takeWhile = options.takeWhile || options.condition || alwaysTrue;
this.transform = options.transform || identity;
this.separator = typeof options.separator === 'undefined'
? '\n'
: (options.separator || '');
this.filter = options.filter || alwaysTrue;

this.format = options.format || '%s';
this.separator = options.separator || '';

if (typeof this.format === 'function')
this.formatOutput = this.format;
this.iter = iter;
Expand All @@ -31,24 +41,43 @@ IterStream.prototype.pause = function pause() {

IterStream.prototype.resume = function resume() {
this.paused = false;
var formatted;
var data = this.next();
var first = true;
var separator = this.separator;
var formatted;
while (!this.paused && this.continuable(data)) {
this.iterations--;

if (!this.filter(data)) {
data = this.next();
continue;
}

formatted = this.formatOutput(data);
formatted += this.separator;
if (!first && isDefined(separator))
this.emitDataEvent(this.separator);
this.emitDataEvent(formatted);

data = this.next();
first = false;
this.take--;
}

if (data === null || !this.continuable(data))
this.emitEndEvent();
};

IterStream.prototype.continuable = function continuable(data) {
return data !== null && this.condition(data) && --this.iterations >= 0;
return (
data !== null &&
this.takeWhile(data) &&
this.iterations > 0 &&
this.take > 0
);
};

IterStream.prototype.next = function next() {
var value;
var value, transformed;
try {
value = this.iter[this.method]();
} catch(err) {
Expand All @@ -59,7 +88,11 @@ IterStream.prototype.next = function next() {
this.emit('error', err);
}
}
return (value !== null ? this.transform(value) : null);
if (value === null)
return null;

transformed = this.transform(value);
return transformed;
};

IterStream.prototype.formatOutput = function formatOutput(data) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "iterator-stream",
"version": "0.3.1",
"version": "0.4.0",
"description": "An adapter for making any iterator streaming",
"main": "index.js",
"directories": {
Expand Down
42 changes: 30 additions & 12 deletions test/iterstream.test.js
Expand Up @@ -11,7 +11,7 @@ var random = iterators.random;

test('testing regular ol ABCs', function (t) {
var str = StreamString();
iterstream(letter(), { separator: null }).pipe(str).once('end', function () {
iterstream(letter()).pipe(str).once('end', function () {
t.same(str.value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
t.end();
});
Expand All @@ -21,7 +21,6 @@ test('formatting with a string', function (t) {
var str = StreamString();
iterstream(letter(), {
format: '%s|',
separator: null
}).pipe(str).once('end', function () {
t.same(str.value, 'A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|');
t.end();
Expand All @@ -32,7 +31,6 @@ test('formatting with a method', function (t) {
var str = StreamString();
iterstream(letter(), {
format: function (l) { return l.toLowerCase(); },
separator: null,
}).pipe(str).once('end', function () {
t.same(str.value, 'abcdefghijklmnopqrstuvwxyz');
t.end();
Expand All @@ -45,17 +43,17 @@ test('record separator', function (t) {
format: '%s',
separator: '.'
}).pipe(str).once('end', function () {
t.same(str.value, 'A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.');
t.same(str.value, 'A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z');
t.end();
});
});

test('additional condition', function (t) {
test('takeWhile', function (t) {
var str = StreamString();
iterstream(letter(), {
condition: function (value) { return value < 'D'; }
takeWhile: function (value) { return value < 'D'; }
}).pipe(str).once('end', function () {
t.same(str.value, 'A\nB\nC\n');
t.same(str.value, 'ABC');
t.end();
});
});
Expand All @@ -66,7 +64,7 @@ test('buffering stuff', function (t) {
bufferSize: 8192
}).pipe(str).once('end', function () {
t.ok(str.events[0].size >= 8192, 'should have buffered the send');
t.same(str.value.indexOf('1.3069892237633987e+308\n'), 32907, 'should have the last event');
t.ok(str.value.indexOf('1.3069892237633987e+308') > -1, 'should have the last event');
t.end();
});
});
Expand Down Expand Up @@ -94,13 +92,33 @@ test('method option', function (t) {

test('transform option', function (t) {
var str = StreamString();
iterstream(random(), {
transform: function(v) { return 'hi' },
iterstream(natural(), {
transform: function (v) { return 'hi' },
iterations: 3,
method: 'random',
separator: '',
}).pipe(str).once('end', function () {
t.same(str.value, 'hihihi');
t.end();
});
});


test('filter option', function (t) {
var str = StreamString();
iterstream(natural(), {
filter: function (v) { return v % 2 == 0 },
takeWhile: function (v) { return v <= 10 },
}).pipe(str).once('end', function () {
t.same(str.value, '0246810');
t.end();
});
});

test('take option', function (t) {
var str = StreamString();
iterstream(natural(), {
take: 5,
}).pipe(str).once('end', function () {
t.same(str.value, '01234');
t.end();
});
});