├── .jshintignore
├── .travis.yml
├── .gitignore
├── .npmignore
├── .jshintrc
├── package.json
├── LICENSE
├── test
├── condition.js
├── ternary.js
└── index.js
├── index.js
├── README.md
└── img
├── condition.svg
└── ternary.svg
/.jshintignore:
--------------------------------------------------------------------------------
1 | node_modules/**
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "8"
4 | - "10"
5 | - "12"
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.log
3 | node_modules
4 | build
5 | *.node
6 | components
7 | *.orig
8 | .idea
9 | temp.txt*
10 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.log
3 | node_modules
4 | build
5 | *.node
6 | components
7 | *.orig
8 | .idea
9 | temp.txt*
10 | test
11 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "bitwise": true,
3 | "camelcase": true,
4 | "curly": true,
5 | "eqeqeq": true,
6 | "forin": true,
7 | "immed": true,
8 | "latedef": true,
9 | "newcap": true,
10 | "noarg": true,
11 | "noempty": true,
12 | "nonew": true,
13 | "regexp": true,
14 | "strict": true,
15 | "trailing": true,
16 | "undef": true,
17 | "unused": true,
18 | "node": true
19 | }
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ternary-stream",
3 | "description": "Fork stream based on passed condition, and collect down-stream",
4 | "version": "3.0.0",
5 | "homepage": "https://github.com/robrich/ternary-stream",
6 | "repository": "git://github.com/robrich/ternary-stream.git",
7 | "author": "Rob Richardson (http://robrich.org/)",
8 | "main": "./index.js",
9 | "keywords": [
10 | "conditional",
11 | "if",
12 | "ternary",
13 | "stream"
14 | ],
15 | "dependencies": {
16 | "duplexify": "^4.1.1",
17 | "fork-stream": "^0.0.4",
18 | "merge-stream": "^2.0.0",
19 | "through2": "^3.0.1"
20 | },
21 | "devDependencies": {
22 | "jshint": "^2.9.4",
23 | "mocha": "^6.1.4",
24 | "should": "^13.2.3"
25 | },
26 | "scripts": {
27 | "test": "mocha && jshint ."
28 | },
29 | "license": "MIT"
30 | }
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 [Richardson & Sons, LLC](http://richardsonandsons.com/)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/test/condition.js:
--------------------------------------------------------------------------------
1 | /*global describe:false, it:false */
2 |
3 | 'use strict';
4 |
5 | var ternaryStream = require('../');
6 |
7 | var through = require('through2');
8 | require('should');
9 |
10 | describe('ternary-stream', function() {
11 | describe('condition', function() {
12 |
13 | function runTest(answer, expected, done) {
14 | // arrange
15 | var called = 0;
16 | var theData = {the:'data'};
17 |
18 | var condition = function (data) {
19 | data.should.equal(theData);
20 | called++;
21 | return answer;
22 | };
23 |
24 | var childStream = through.obj(function (data, enc, cb) {
25 | data.should.equal(theData);
26 | called+=10;
27 | this.push(data);
28 | cb();
29 | });
30 |
31 | // act
32 | var s = ternaryStream(condition, childStream);
33 |
34 | s.once('data', function (data) {
35 | data.should.equal(theData);
36 | called+=100;
37 | });
38 |
39 | // assert
40 | s.once('end', function(){
41 |
42 | // Test that command executed
43 | called.should.equal(expected);
44 | done();
45 | });
46 |
47 | // act
48 | s.write(theData);
49 | s.end();
50 | }
51 |
52 | it('should call the function when passed truthy', function(done) {
53 | // arrange
54 | var answer = true;
55 |
56 | // act, assert
57 | runTest(answer, 111, done);
58 | });
59 |
60 | it('should not call the function when passed falsey', function(done) {
61 | // arrange
62 | var answer = false;
63 |
64 | // act, assert
65 | runTest(answer, 101, done);
66 | });
67 | });
68 | });
69 |
--------------------------------------------------------------------------------
/test/ternary.js:
--------------------------------------------------------------------------------
1 | /*global describe:false, it:false */
2 |
3 | 'use strict';
4 |
5 | var ternaryStream = require('../');
6 |
7 | var through = require('through2');
8 | require('should');
9 |
10 | describe('ternary-stream', function() {
11 | describe('ternary,', function() {
12 |
13 | function runTest(answer, expected, done) {
14 | // arrange
15 | var called = 0;
16 | var theData = {the:'data'};
17 |
18 | var condition = function (data) {
19 | data.should.equal(theData);
20 | called++;
21 | return answer;
22 | };
23 |
24 | var trueStream = through.obj(function (data, enc, cb) {
25 | data.should.equal(theData);
26 | called+=10;
27 | this.push(data);
28 | cb();
29 | });
30 | var falseStream = through.obj(function (data, enc, cb) {
31 | data.should.equal(theData);
32 | called+=20;
33 | this.push(data);
34 | cb();
35 | });
36 |
37 | // act
38 | var s = ternaryStream(condition, trueStream, falseStream);
39 |
40 | s.once('data', function (data) {
41 | data.should.equal(theData);
42 | called+=100;
43 | });
44 |
45 | // assert
46 | s.once('end', function(){
47 |
48 | // Test that command executed
49 | called.should.equal(expected);
50 | done();
51 | });
52 |
53 | // act
54 | s.write(theData);
55 | s.end();
56 | }
57 |
58 | it('should call the function when passed truthy', function(done) {
59 | // arrange
60 | var answer = true;
61 |
62 | // act, assert
63 | runTest(answer, 111, done);
64 | });
65 |
66 | it('should not call the function when passed falsey', function(done) {
67 | // arrange
68 | var answer = false;
69 |
70 | // act, assert
71 | runTest(answer, 121, done);
72 | });
73 | });
74 | });
75 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var through2 = require('through2');
4 | var ForkStream = require('fork-stream');
5 | var mergeStream = require('merge-stream');
6 | var duplexify = require('duplexify');
7 |
8 | module.exports = function (condition, trueStream, falseStream) {
9 | if (!trueStream) {
10 | throw new Error('fork-stream: child action is required');
11 | }
12 |
13 | // output stream
14 | var outStream = through2.obj();
15 |
16 | // create fork-stream
17 | var forkStream = new ForkStream({
18 | classifier: function (e, cb) {
19 | var ans = !!condition(e);
20 | return cb(null, ans);
21 | }
22 | });
23 |
24 | // if condition is true, pipe input to trueStream
25 | forkStream.a.pipe(trueStream);
26 |
27 | var mergedStream;
28 |
29 | if (falseStream) {
30 | // if there's an 'else' condition
31 | // if condition is false
32 | // pipe input to falseStream
33 | forkStream.b.pipe(falseStream);
34 | // merge output with trueStream's output
35 | mergedStream = mergeStream(falseStream, trueStream);
36 | // redirect falseStream errors to mergedStream
37 | falseStream.on('error', function(err) { mergedStream.emit('error', err); });
38 | } else {
39 | // if there's no 'else' condition
40 | // if condition is false
41 | // merge output with trueStream's output
42 | mergedStream = mergeStream(forkStream.b, trueStream);
43 | }
44 |
45 | // redirect trueStream errors to mergedStream
46 | trueStream.on('error', function(err) { mergedStream.emit('error', err); });
47 |
48 | // send everything down-stream
49 | mergedStream.pipe(outStream);
50 | // redirect mergedStream errors to outStream
51 | mergedStream.on('error', function(err) { outStream.emit('error', err); });
52 |
53 | // consumers write in to forkStream, we write out to outStream
54 | return duplexify.obj(forkStream, outStream);
55 | };
56 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | /*global describe:false, it:false */
2 |
3 | 'use strict';
4 |
5 | var ternaryStream = require('../');
6 |
7 | var through = require('through2');
8 | var should = require('should');
9 |
10 | describe('ternary-stream', function() {
11 | describe('smoke test', function() {
12 |
13 | it('should call the function when passed truthy', function(done) {
14 | // arrange
15 | var called = 0;
16 |
17 | var condition = function (data) {
18 | return data.answer;
19 | };
20 |
21 | var childStream = through.obj(function (data, enc, cb) {
22 | called++;
23 | this.push(data);
24 | cb();
25 | });
26 |
27 | // act
28 | var s = ternaryStream(condition, childStream);
29 |
30 | s.on('data', function (/*data*/) {
31 | called += 10;
32 | });
33 |
34 | // assert
35 | s.once('finish', function () {
36 |
37 | // Test that command executed
38 | called.should.equal(21);
39 | done();
40 | });
41 |
42 | // act
43 | s.write({answer:true});
44 | s.write({answer:false});
45 | s.end();
46 | });
47 |
48 | it('should properly redirect trueStream errors', function(done) {
49 | // arrange
50 | var called = 0;
51 |
52 | var condition = function (data) {
53 | return data.answer;
54 | };
55 |
56 | var trueStream = through.obj(function (data, enc, cb) {
57 | called++;
58 | this.emit('error', new Error('foo'));
59 | cb();
60 | });
61 |
62 | // act
63 | var s = ternaryStream(condition, trueStream);
64 |
65 | s.on('error', function (/*data*/) {
66 | done();
67 | });
68 |
69 | // act
70 | s.write({answer:true});
71 | s.write({answer:false});
72 | s.end();
73 | });
74 |
75 | it('should properly redirect falseStream errors', function(done) {
76 | // arrange
77 | var called = 0;
78 |
79 | var condition = function (data) {
80 | return data.answer;
81 | };
82 |
83 | var falseStream = through.obj(function (data, enc, cb) {
84 | called++;
85 | this.emit('error', new Error('foo'));
86 | cb();
87 | });
88 |
89 | // act
90 | var s = ternaryStream(condition, through.obj(), falseStream);
91 |
92 | s.on('error', function (/*data*/) {
93 | done();
94 | });
95 |
96 | // act
97 | s.write({answer:true});
98 | s.write({answer:false});
99 | s.end();
100 | });
101 |
102 | it('should error if no parameters passed', function(done) {
103 | // arrange
104 | var caughtErr;
105 |
106 | // act
107 | try {
108 | ternaryStream();
109 | } catch (err) {
110 | caughtErr = err;
111 | }
112 |
113 | // assert
114 | should.exist(caughtErr);
115 | caughtErr.message.indexOf('required').should.be.above(-1);
116 | done();
117 | });
118 |
119 | });
120 | });
121 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ternary-stream 
2 | =======
3 |
4 | A ternary stream: conditionally control the flow of stream data
5 |
6 | ## Usage
7 |
8 | 1: Conditionally filter content
9 |
10 | **Condition**
11 |
12 | ![][condition]
13 |
14 | if the condition returns truthy, data is piped to the child stream
15 |
16 | ```js
17 | var ternaryStream = require('ternary-stream');
18 |
19 | var condition = function (data) {
20 | return true;
21 | };
22 |
23 | process.stdin
24 | .pipe(ternaryStream(condition, process.stdout))
25 | .pipe(fs.createWriteStream('./out.txt'));
26 | ```
27 |
28 | Data will conditionally go to stdout, and always go to the file
29 |
30 | 2: Ternary stream
31 |
32 | **Ternary**
33 |
34 | ![][ternary]
35 |
36 |
37 | ```javascript
38 | var ternaryStream = require('ternary-stream');
39 | var through2 = require('through2');
40 |
41 | var count = 0;
42 | var condition = function (data) {
43 | count++;
44 | return count % 2;
45 | };
46 |
47 | process.stdin
48 | .pipe(ternaryStream(condition, fs.createWriteStream('./truthy.txt'), fs.createWriteStream('./falsey.txt')))
49 | .pipe(process.stdout);
50 | ```
51 |
52 | Data will either go to truthy.txt (if condition is true) or falsey.txt (if condition is false) and will always go to stdout
53 |
54 | ## API
55 |
56 | ### ternaryStream(condition, stream [, elseStream])
57 |
58 | ternary-stream will pipe data to `stream` whenever `condition` is truthy.
59 |
60 | If `condition` is falsey and `elseStream` is passed, data will pipe to `elseStream`.
61 |
62 | After data is piped to `stream` or `elseStream` or neither, data is piped down-stream.
63 |
64 | #### Parameters
65 |
66 | ##### condition
67 |
68 | Type: `function`: takes in stream data and returns `boolean`
69 |
70 | ```js
71 | function (data) {
72 | return true; // or false
73 | }
74 | ```
75 |
76 | ##### stream
77 |
78 | Stream for ternary-stream to pipe data into when condition is truthy.
79 |
80 | ##### elseStream
81 |
82 | Optional, Stream for ternary-stream to pipe data into when condition is falsey.
83 |
84 |
85 | LICENSE
86 | -------
87 |
88 | (MIT License)
89 |
90 | Copyright (c) 2014 [Richardson & Sons, LLC](http://richardsonandsons.com/)
91 |
92 | Permission is hereby granted, free of charge, to any person obtaining
93 | a copy of this software and associated documentation files (the
94 | "Software"), to deal in the Software without restriction, including
95 | without limitation the rights to use, copy, modify, merge, publish,
96 | distribute, sublicense, and/or sell copies of the Software, and to
97 | permit persons to whom the Software is furnished to do so, subject to
98 | the following conditions:
99 |
100 | The above copyright notice and this permission notice shall be
101 | included in all copies or substantial portions of the Software.
102 |
103 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
104 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
105 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
106 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
107 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
108 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
109 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
110 |
111 | [condition]: https://rawgithub.com/robrich/ternary-stream/master/img/condition.svg
112 | [ternary]: https://rawgithub.com/robrich/ternary-stream/master/img/ternary.svg
113 |
--------------------------------------------------------------------------------
/img/condition.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/img/ternary.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------