From 5edbe1a543d617b25239c9486ea2af95a2c5561c Mon Sep 17 00:00:00 2001 From: Sam Newman Date: Tue, 3 Feb 2015 01:12:41 +0000 Subject: [PATCH] stream: simpler stream constructon Via revealing constructor pattern. Referenced to discussion in issue https://github.com/iojs/readable-stream/issues/102 of iojs/readable-stream --- doc/api/stream.markdown | 133 +++++++++++++++++- lib/_stream_readable.js | 3 + lib/_stream_transform.js | 8 ++ lib/_stream_writable.js | 8 ++ ...t-stream-readable-revealing-constructor.js | 19 +++ ...-stream-transform-revealing-constructor.js | 32 +++++ ...t-stream-writable-revealing-constructor.js | 44 ++++++ 7 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-stream-readable-revealing-constructor.js create mode 100644 test/parallel/test-stream-transform-revealing-constructor.js create mode 100644 test/parallel/test-stream-writable-revealing-constructor.js diff --git a/doc/api/stream.markdown b/doc/api/stream.markdown index 943718660cbcb8..4cb7b1b4f9599c 100644 --- a/doc/api/stream.markdown +++ b/doc/api/stream.markdown @@ -718,7 +718,7 @@ of stream class you are writing:

[Writable](#stream_class_stream_writable_1)

-

[_write][]

+

[_write][], _writev

@@ -729,7 +729,7 @@ of stream class you are writing:

[Duplex](#stream_class_stream_duplex_1)

-

[_read][], [_write][]

+

[_read][], [_write][], _writev

@@ -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 + + + +To implement any sort of stream you can now pass that streams specific methods as parameters to the constructors options: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Use-case

+
+

Class

+
+

Method(s) to implement

+
+

Reading only

+
+

[Readable](#stream_class_stream_readable_1)

+
+

[read][_read]

+
+

Writing only

+
+

[Writable](#stream_class_stream_writable_1)

+
+

[write][_write]

+
+

Reading and writing

+
+

[Duplex](#stream_class_stream_duplex_1)

+
+

[read][_read], [write][_write], writev

+
+

Operate on written data, then read the result

+
+

[Transform](#stream_class_stream_transform_1)

+
+

transform, flush

+
+ +Examples: + +### 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 diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js index 2418648b69caa0..bf736477e80da6 100644 --- a/lib/_stream_readable.js +++ b/lib/_stream_readable.js @@ -85,6 +85,9 @@ function Readable(options) { // legacy this.readable = true; + if (options && typeof options.read === 'function') + this._read = options.read; + Stream.call(this); } diff --git a/lib/_stream_transform.js b/lib/_stream_transform.js index d3e7a1348634ce..8ff428e11ffed0 100644 --- a/lib/_stream_transform.js +++ b/lib/_stream_transform.js @@ -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) { diff --git a/lib/_stream_writable.js b/lib/_stream_writable.js index 0176f4095fc76e..6a008fe76d2dbb 100644 --- a/lib/_stream_writable.js +++ b/lib/_stream_writable.js @@ -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); } diff --git a/test/parallel/test-stream-readable-revealing-constructor.js b/test/parallel/test-stream-readable-revealing-constructor.js new file mode 100644 index 00000000000000..ea1703fa46ef9b --- /dev/null +++ b/test/parallel/test-stream-readable-revealing-constructor.js @@ -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'); +}); diff --git a/test/parallel/test-stream-transform-revealing-constructor.js b/test/parallel/test-stream-transform-revealing-constructor.js new file mode 100644 index 00000000000000..e354612798ecb7 --- /dev/null +++ b/test/parallel/test-stream-transform-revealing-constructor.js @@ -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'); +}); diff --git a/test/parallel/test-stream-writable-revealing-constructor.js b/test/parallel/test-stream-writable-revealing-constructor.js new file mode 100644 index 00000000000000..42ecf419e4420a --- /dev/null +++ b/test/parallel/test-stream-writable-revealing-constructor.js @@ -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'); + }); +}());