From 5edbe1a543d617b25239c9486ea2af95a2c5561c Mon Sep 17 00:00:00 2001 From: Sam Newman Date: Tue, 3 Feb 2015 01:12:41 +0000 Subject: [PATCH 1/5] 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'); + }); +}()); From 0b6fcb0374344dcb776b69ea7b251ac510c11d9d Mon Sep 17 00:00:00 2001 From: Sam Newman Date: Tue, 3 Feb 2015 02:18:25 +0000 Subject: [PATCH 2/5] simplified documentation --- doc/api/stream.markdown | 64 ++--------------------------------------- 1 file changed, 3 insertions(+), 61 deletions(-) diff --git a/doc/api/stream.markdown b/doc/api/stream.markdown index 4cb7b1b4f9599c..93cd30145e420d 100644 --- a/doc/api/stream.markdown +++ b/doc/api/stream.markdown @@ -1315,71 +1315,13 @@ 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 +## Simplified Constructor API -To implement any sort of stream you can now pass that streams specific methods as parameters to the constructors options: +In simple cases there is now the added benefit of being able to construct a stream without the need of inheritance. - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

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

-
+This can be done simply by passing the necessary streams specific methods as parameters to the constructors options: Examples: From 8d728975366a6df21afaf5bd6fec4bb2424ed004 Mon Sep 17 00:00:00 2001 From: Sam Newman Date: Tue, 3 Feb 2015 02:38:18 +0000 Subject: [PATCH 3/5] minor copy updates --- doc/api/stream.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/stream.markdown b/doc/api/stream.markdown index 93cd30145e420d..bbd2af0d7a3f95 100644 --- a/doc/api/stream.markdown +++ b/doc/api/stream.markdown @@ -1319,9 +1319,9 @@ it can come in handy as a building block for novel sorts of streams. -In simple cases there is now the added benefit of being able to construct a stream without the need of inheritance. +In simple cases there is now the added benefit of being able to construct a stream without inheritance. -This can be done simply by passing the necessary streams specific methods as parameters to the constructors options: +This can be done by passing the appropriate methods as constructor options: Examples: From 8bc8805594357433927c8e87859d6b27598e51be Mon Sep 17 00:00:00 2001 From: Sam Newman Date: Tue, 3 Feb 2015 02:58:44 +0000 Subject: [PATCH 4/5] renamed test files --- ...tructor.js => test-stream-readable-constructor-set-methods.js} | 0 ...ructor.js => test-stream-transform-constructor-set-methods.js} | 0 ...tructor.js => test-stream-writable-constructor-set-methods.js} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename test/parallel/{test-stream-readable-revealing-constructor.js => test-stream-readable-constructor-set-methods.js} (100%) rename test/parallel/{test-stream-transform-revealing-constructor.js => test-stream-transform-constructor-set-methods.js} (100%) rename test/parallel/{test-stream-writable-revealing-constructor.js => test-stream-writable-constructor-set-methods.js} (100%) diff --git a/test/parallel/test-stream-readable-revealing-constructor.js b/test/parallel/test-stream-readable-constructor-set-methods.js similarity index 100% rename from test/parallel/test-stream-readable-revealing-constructor.js rename to test/parallel/test-stream-readable-constructor-set-methods.js diff --git a/test/parallel/test-stream-transform-revealing-constructor.js b/test/parallel/test-stream-transform-constructor-set-methods.js similarity index 100% rename from test/parallel/test-stream-transform-revealing-constructor.js rename to test/parallel/test-stream-transform-constructor-set-methods.js diff --git a/test/parallel/test-stream-writable-revealing-constructor.js b/test/parallel/test-stream-writable-constructor-set-methods.js similarity index 100% rename from test/parallel/test-stream-writable-revealing-constructor.js rename to test/parallel/test-stream-writable-constructor-set-methods.js From fab2f662ebe6a4a495952231dac53107037af80f Mon Sep 17 00:00:00 2001 From: Sam Newman Date: Tue, 3 Feb 2015 03:29:57 +0000 Subject: [PATCH 5/5] minor refactor of tests --- ...stream-readable-constructor-set-methods.js | 1 - ...tream-transform-constructor-set-methods.js | 1 - ...stream-writable-constructor-set-methods.js | 68 ++++++++----------- 3 files changed, 29 insertions(+), 41 deletions(-) diff --git a/test/parallel/test-stream-readable-constructor-set-methods.js b/test/parallel/test-stream-readable-constructor-set-methods.js index ea1703fa46ef9b..a88ffcd67a1446 100644 --- a/test/parallel/test-stream-readable-constructor-set-methods.js +++ b/test/parallel/test-stream-readable-constructor-set-methods.js @@ -15,5 +15,4 @@ r.resume(); process.on('exit', function () { assert.equal(r._read, _read); assert(_readCalled); - console.log('ok'); }); diff --git a/test/parallel/test-stream-transform-constructor-set-methods.js b/test/parallel/test-stream-transform-constructor-set-methods.js index e354612798ecb7..55e64ed9b2e733 100644 --- a/test/parallel/test-stream-transform-constructor-set-methods.js +++ b/test/parallel/test-stream-transform-constructor-set-methods.js @@ -28,5 +28,4 @@ process.on('exit', function () { assert.equal(t._flush, _flush); assert(_transformCalled); assert(_flushCalled); - console.log('ok'); }); diff --git a/test/parallel/test-stream-writable-constructor-set-methods.js b/test/parallel/test-stream-writable-constructor-set-methods.js index 42ecf419e4420a..7373568b38ebd2 100644 --- a/test/parallel/test-stream-writable-constructor-set-methods.js +++ b/test/parallel/test-stream-writable-constructor-set-methods.js @@ -3,42 +3,32 @@ 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'); - }); -}()); +var _writeCalled = false; +function _write(d, e, n) { + _writeCalled = true; +} + +var w = new Writable({ write: _write }); +w.end(new Buffer('blerg')); + +var _writevCalled = false; +var dLength = 0; +function _writev(d, n) { + dLength = d.length; + _writevCalled = true; +} + +var w2 = new Writable({ writev: _writev }); +w2.cork(); + +w2.write(new Buffer('blerg')); +w2.write(new Buffer('blerg')); +w2.end(); + +process.on('exit', function () { + assert.equal(w._write, _write); + assert(_writeCalled); + assert.equal(w._writev, _writev); + assert.equal(dLength, 2); + assert(_writevCalled); +});