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

stream: simpler stream constructon #697

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 131 additions & 2 deletions doc/api/stream.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,7 @@ of stream class you are writing:
<p>[Writable](#stream_class_stream_writable_1)</p>
</td>
<td>
<p><code>[_write][]</code></p>
<p><code>[_write][]</code>, <code>_writev</code></p>
</td>
</tr>
<tr>
Expand All @@ -729,7 +729,7 @@ of stream class you are writing:
<p>[Duplex](#stream_class_stream_duplex_1)</p>
</td>
<td>
<p><code>[_read][]</code>, <code>[_write][]</code></p>
<p><code>[_read][]</code>, <code>[_write][]</code>, <code>_writev</code></p>
</td>
</tr>
<tr>
Expand Down Expand Up @@ -1315,6 +1315,135 @@ for examples and testing, but there are occasionally use cases where
it can come in handy as a building block for novel sorts of streams.


## Simplified API Via Revealing Constructor Pattern
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically I wouldn't call this a revealing constructor pattern, since no functionality is "revealed"; you instead just use stream.push and stream.emit("error", ...) and similar.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get your point, I will amend the description as well.


<!--type=misc-->

To implement any sort of stream you can now pass that streams specific methods as parameters to the constructors options:

<table>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure this table needs to be duplicated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I will remove 😄

<thead>
<tr>
<th>
<p>Use-case</p>
</th>
<th>
<p>Class</p>
</th>
<th>
<p>Method(s) to implement</p>
</th>
</tr>
</thead>
<tr>
<td>
<p>Reading only</p>
</td>
<td>
<p>[Readable](#stream_class_stream_readable_1)</p>
</td>
<td>
<p><code>[read][_read]</code></p>
</td>
</tr>
<tr>
<td>
<p>Writing only</p>
</td>
<td>
<p>[Writable](#stream_class_stream_writable_1)</p>
</td>
<td>
<p><code>[write][_write]</code></p>
</td>
</tr>
<tr>
<td>
<p>Reading and writing</p>
</td>
<td>
<p>[Duplex](#stream_class_stream_duplex_1)</p>
</td>
<td>
<p><code>[read][_read]</code>, <code>[write][_write]</code>, <code>writev</code></p>
</td>
</tr>
<tr>
<td>
<p>Operate on written data, then read the result</p>
</td>
<td>
<p>[Transform](#stream_class_stream_transform_1)</p>
</td>
<td>
<p><code>transform</code>, <code>flush</code></p>
</td>
</tr>
</table>

Examples:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are lovely illustrations of how you no longer need to subclass for simple cases. I might even call that out as an explicit benefit.


### Readable
```javascript
var readable = new stream.Readable({
read: function(n) {
// sets this._read under the hood
}
});
```

### Writable
```javascript
var writable = new stream.Writable({
write: function(chunk, encoding, next) {
// sets this._write under the hood
}
});

// or

var writable = new stream.Writable({
writev: function(chunks, next) {
// sets this._writev under the hood
}
});
```

### Duplex
```javascript
var duplex = new stream.Duplex({
read: function(n) {
// sets this._read under the hood
},
write: function(chunk, encoding, next) {
// sets this._write under the hood
}
});

// or

var duplex = new stream.Duplex({
read: function(n) {
// sets this._read under the hood
},
writev: function(chunks, next) {
// sets this._writev under the hood
}
});
```

### Transform
```javascript
var transform = new stream.Transform({
transform: function(chunk, encoding, next) {
// sets this._transform under the hood
},
flush: function(done) {
// sets this._flush under the hood
}
});
```

## Streams: Under the Hood

<!--type=misc-->
Expand Down
3 changes: 3 additions & 0 deletions lib/_stream_readable.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ function Readable(options) {
// legacy
this.readable = true;

if (options && typeof options.read === 'function')
this._read = options.read;

Stream.call(this);
}

Expand Down
8 changes: 8 additions & 0 deletions lib/_stream_transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,14 @@ function Transform(options) {
// sync guard flag.
this._readableState.sync = false;

if (options) {
if (typeof options.transform === 'function')
this._transform = options.transform;

if (typeof options.flush === 'function')
this._flush = options.flush;
}

this.once('prefinish', function() {
if (typeof this._flush === 'function')
this._flush(function(er) {
Expand Down
8 changes: 8 additions & 0 deletions lib/_stream_writable.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@ function Writable(options) {
// legacy.
this.writable = true;

if (options) {
if (typeof options.write === 'function')
this._write = options.write;

if (typeof options.writev === 'function')
this._writev = options.writev;
}

Stream.call(this);
}

Expand Down
19 changes: 19 additions & 0 deletions test/parallel/test-stream-readable-revealing-constructor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
var common = require('../common');
var assert = require('assert');

var Readable = require('stream').Readable;

var _readCalled = false;
function _read(n) {
_readCalled = true;
this.push(null);
}

var r = new Readable({ read: _read });
r.resume();

process.on('exit', function () {
assert.equal(r._read, _read);
assert(_readCalled);
console.log('ok');
});
32 changes: 32 additions & 0 deletions test/parallel/test-stream-transform-revealing-constructor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
var common = require('../common');
var assert = require('assert');

var Transform = require('stream').Transform;

var _transformCalled = false;
function _transform(d, e, n) {
_transformCalled = true;
n();
}

var _flushCalled = false;
function _flush(n) {
_flushCalled = true;
n();
}

var t = new Transform({
transform: _transform,
flush: _flush
});

t.end(new Buffer('blerg'));
t.resume();

process.on('exit', function () {
assert.equal(t._transform, _transform);
assert.equal(t._flush, _flush);
assert(_transformCalled);
assert(_flushCalled);
console.log('ok');
});
44 changes: 44 additions & 0 deletions test/parallel/test-stream-writable-revealing-constructor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
var common = require('../common');
var assert = require('assert');

var Writable = require('stream').Writable;

(function one() {
var _writeCalled = false;
function _write(d, e, n) {
_writeCalled = true;
}

var w = new Writable({ write: _write });
w.end(new Buffer('blerg'));

process.on('exit', function () {
assert.equal(w._write, _write);
assert(_writeCalled);
console.log('ok 1');
});
}());

(function two() {
var _writevCalled = false;
var dLength = 0;

function _writev(d, n) {
dLength = d.length;
_writevCalled = true;
}

var w = new Writable({ writev: _writev });
w.cork();

w.write(new Buffer('blerg'));
w.write(new Buffer('blerg'));
w.end();

process.on('exit', function () {
assert.equal(w._writev, _writev);
assert.equal(dLength, 2);
assert(_writevCalled);
console.log('ok 2');
});
}());