Skip to content

Commit 4b0340c

Browse files
author
Einar Norðfjörð
committed
Fix issue with takeUntil in nested stream
This issue was caused by #180 which made sure that nested streams were updated properly. Updating them properly caused nested takeUntil'd streams to end too soon.
1 parent 48799a9 commit 4b0340c

File tree

4 files changed

+257
-5
lines changed

4 files changed

+257
-5
lines changed

examples/drag-and-drop/build.js

Lines changed: 231 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -994,8 +994,238 @@ StreamTransformer.prototype['@@transducer/step'] = function(s, v) { return v; };
994994

995995
var lib = flyd;
996996

997+
/**
998+
* Tests whether or not an object is an array.
999+
*
1000+
* @private
1001+
* @param {*} val The object to test.
1002+
* @return {Boolean} `true` if `val` is an array, `false` otherwise.
1003+
* @example
1004+
*
1005+
* _isArray([]); //=> true
1006+
* _isArray(null); //=> false
1007+
* _isArray({}); //=> false
1008+
*/
1009+
var _isArray = Array.isArray || function _isArray(val) {
1010+
return val != null && val.length >= 0 && Object.prototype.toString.call(val) === '[object Array]';
1011+
};
1012+
1013+
function _isTransformer(obj) {
1014+
return typeof obj['@@transducer/step'] === 'function';
1015+
}
1016+
var _isTransformer_1 = _isTransformer;
1017+
1018+
/**
1019+
* Returns a function that dispatches with different strategies based on the
1020+
* object in list position (last argument). If it is an array, executes [fn].
1021+
* Otherwise, if it has a function with one of the given method names, it will
1022+
* execute that function (functor case). Otherwise, if it is a transformer,
1023+
* uses transducer [xf] to return a new transformer (transducer case).
1024+
* Otherwise, it will default to executing [fn].
1025+
*
1026+
* @private
1027+
* @param {Array} methodNames properties to check for a custom implementation
1028+
* @param {Function} xf transducer to initialize if object is transformer
1029+
* @param {Function} fn default ramda implementation
1030+
* @return {Function} A function that dispatches on object in list position
1031+
*/
1032+
1033+
1034+
function _dispatchable(methodNames, xf, fn) {
1035+
return function () {
1036+
if (arguments.length === 0) {
1037+
return fn();
1038+
}
1039+
var args = Array.prototype.slice.call(arguments, 0);
1040+
var obj = args.pop();
1041+
if (!_isArray(obj)) {
1042+
var idx = 0;
1043+
while (idx < methodNames.length) {
1044+
if (typeof obj[methodNames[idx]] === 'function') {
1045+
return obj[methodNames[idx]].apply(obj, args);
1046+
}
1047+
idx += 1;
1048+
}
1049+
if (_isTransformer_1(obj)) {
1050+
var transducer = xf.apply(null, args);
1051+
return transducer(obj);
1052+
}
1053+
}
1054+
return fn.apply(this, arguments);
1055+
};
1056+
}
1057+
var _dispatchable_1 = _dispatchable;
1058+
1059+
var _xfBase = {
1060+
init: function () {
1061+
return this.xf['@@transducer/init']();
1062+
},
1063+
result: function (result) {
1064+
return this.xf['@@transducer/result'](result);
1065+
}
1066+
};
1067+
1068+
var XDrop = /*#__PURE__*/function () {
1069+
1070+
function XDrop(n, xf) {
1071+
this.xf = xf;
1072+
this.n = n;
1073+
}
1074+
XDrop.prototype['@@transducer/init'] = _xfBase.init;
1075+
XDrop.prototype['@@transducer/result'] = _xfBase.result;
1076+
XDrop.prototype['@@transducer/step'] = function (result, input) {
1077+
if (this.n > 0) {
1078+
this.n -= 1;
1079+
return result;
1080+
}
1081+
return this.xf['@@transducer/step'](result, input);
1082+
};
1083+
1084+
return XDrop;
1085+
}();
1086+
1087+
var _xdrop = /*#__PURE__*/_curry2_1(function _xdrop(n, xf) {
1088+
return new XDrop(n, xf);
1089+
});
1090+
var _xdrop_1 = _xdrop;
1091+
1092+
/**
1093+
* This checks whether a function has a [methodname] function. If it isn't an
1094+
* array it will execute that function otherwise it will default to the ramda
1095+
* implementation.
1096+
*
1097+
* @private
1098+
* @param {Function} fn ramda implemtation
1099+
* @param {String} methodname property to check for a custom implementation
1100+
* @return {Object} Whatever the return value of the method is.
1101+
*/
1102+
1103+
1104+
function _checkForMethod(methodname, fn) {
1105+
return function () {
1106+
var length = arguments.length;
1107+
if (length === 0) {
1108+
return fn();
1109+
}
1110+
var obj = arguments[length - 1];
1111+
return _isArray(obj) || typeof obj[methodname] !== 'function' ? fn.apply(this, arguments) : obj[methodname].apply(obj, Array.prototype.slice.call(arguments, 0, length - 1));
1112+
};
1113+
}
1114+
var _checkForMethod_1 = _checkForMethod;
1115+
1116+
/**
1117+
* Optimized internal three-arity curry function.
1118+
*
1119+
* @private
1120+
* @category Function
1121+
* @param {Function} fn The function to curry.
1122+
* @return {Function} The curried function.
1123+
*/
1124+
1125+
1126+
function _curry3(fn) {
1127+
return function f3(a, b, c) {
1128+
switch (arguments.length) {
1129+
case 0:
1130+
return f3;
1131+
case 1:
1132+
return _isPlaceholder_1(a) ? f3 : _curry2_1(function (_b, _c) {
1133+
return fn(a, _b, _c);
1134+
});
1135+
case 2:
1136+
return _isPlaceholder_1(a) && _isPlaceholder_1(b) ? f3 : _isPlaceholder_1(a) ? _curry2_1(function (_a, _c) {
1137+
return fn(_a, b, _c);
1138+
}) : _isPlaceholder_1(b) ? _curry2_1(function (_b, _c) {
1139+
return fn(a, _b, _c);
1140+
}) : _curry1_1(function (_c) {
1141+
return fn(a, b, _c);
1142+
});
1143+
default:
1144+
return _isPlaceholder_1(a) && _isPlaceholder_1(b) && _isPlaceholder_1(c) ? f3 : _isPlaceholder_1(a) && _isPlaceholder_1(b) ? _curry2_1(function (_a, _b) {
1145+
return fn(_a, _b, c);
1146+
}) : _isPlaceholder_1(a) && _isPlaceholder_1(c) ? _curry2_1(function (_a, _c) {
1147+
return fn(_a, b, _c);
1148+
}) : _isPlaceholder_1(b) && _isPlaceholder_1(c) ? _curry2_1(function (_b, _c) {
1149+
return fn(a, _b, _c);
1150+
}) : _isPlaceholder_1(a) ? _curry1_1(function (_a) {
1151+
return fn(_a, b, c);
1152+
}) : _isPlaceholder_1(b) ? _curry1_1(function (_b) {
1153+
return fn(a, _b, c);
1154+
}) : _isPlaceholder_1(c) ? _curry1_1(function (_c) {
1155+
return fn(a, b, _c);
1156+
}) : fn(a, b, c);
1157+
}
1158+
};
1159+
}
1160+
var _curry3_1 = _curry3;
1161+
1162+
/**
1163+
* Returns the elements of the given list or string (or object with a `slice`
1164+
* method) from `fromIndex` (inclusive) to `toIndex` (exclusive).
1165+
*
1166+
* Dispatches to the `slice` method of the third argument, if present.
1167+
*
1168+
* @func
1169+
* @memberOf R
1170+
* @since v0.1.4
1171+
* @category List
1172+
* @sig Number -> Number -> [a] -> [a]
1173+
* @sig Number -> Number -> String -> String
1174+
* @param {Number} fromIndex The start index (inclusive).
1175+
* @param {Number} toIndex The end index (exclusive).
1176+
* @param {*} list
1177+
* @return {*}
1178+
* @example
1179+
*
1180+
* R.slice(1, 3, ['a', 'b', 'c', 'd']); //=> ['b', 'c']
1181+
* R.slice(1, Infinity, ['a', 'b', 'c', 'd']); //=> ['b', 'c', 'd']
1182+
* R.slice(0, -1, ['a', 'b', 'c', 'd']); //=> ['a', 'b', 'c']
1183+
* R.slice(-3, -1, ['a', 'b', 'c', 'd']); //=> ['b', 'c']
1184+
* R.slice(0, 3, 'ramda'); //=> 'ram'
1185+
*/
1186+
1187+
1188+
var slice = /*#__PURE__*/_curry3_1( /*#__PURE__*/_checkForMethod_1('slice', function slice(fromIndex, toIndex, list) {
1189+
return Array.prototype.slice.call(list, fromIndex, toIndex);
1190+
}));
1191+
var slice_1 = slice;
1192+
1193+
/**
1194+
* Returns all but the first `n` elements of the given list, string, or
1195+
* transducer/transformer (or object with a `drop` method).
1196+
*
1197+
* Dispatches to the `drop` method of the second argument, if present.
1198+
*
1199+
* @func
1200+
* @memberOf R
1201+
* @since v0.1.0
1202+
* @category List
1203+
* @sig Number -> [a] -> [a]
1204+
* @sig Number -> String -> String
1205+
* @param {Number} n
1206+
* @param {*} list
1207+
* @return {*} A copy of list without the first `n` elements
1208+
* @see R.take, R.transduce, R.dropLast, R.dropWhile
1209+
* @example
1210+
*
1211+
* R.drop(1, ['foo', 'bar', 'baz']); //=> ['bar', 'baz']
1212+
* R.drop(2, ['foo', 'bar', 'baz']); //=> ['baz']
1213+
* R.drop(3, ['foo', 'bar', 'baz']); //=> []
1214+
* R.drop(4, ['foo', 'bar', 'baz']); //=> []
1215+
* R.drop(3, 'ramda'); //=> 'da'
1216+
*/
1217+
1218+
1219+
var drop = /*#__PURE__*/_curry2_1( /*#__PURE__*/_dispatchable_1(['drop'], _xdrop_1, function drop(n, xs) {
1220+
return slice_1(Math.max(0, n), Infinity, xs);
1221+
}));
1222+
var drop_1 = drop;
1223+
1224+
var dropCurrentValue = lib.transduce(drop_1(1));
1225+
9971226
var takeuntil = lib.curryN(2, function(src, term) {
998-
return lib.endsOn(lib.merge(term, src.end), lib.combine(function(src, self) {
1227+
var end$ = term.hasVal ? dropCurrentValue(term) : term;
1228+
return lib.endsOn(lib.merge(end$, src.end), lib.combine(function(src, self) {
9991229
self(src());
10001230
}, [src]));
10011231
});

module/switchlatest/index.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
var flyd = require('../../lib');
22
var takeUntil = require('../takeuntil');
3-
var drop = require('ramda/src/drop');
43

5-
var dropCurrentValue = flyd.transduce(drop(1));
64

75
module.exports = function(s) {
86
return flyd.combine(function(stream$, self) {
97
var value$ = stream$();
10-
flyd.on(self, takeUntil(value$, dropCurrentValue(stream$)));
8+
flyd.on(self, takeUntil(value$, stream$));
119
}, [s]);
1210
};

module/takeuntil/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
var flyd = require('../../lib');
2+
var drop = require('ramda/src/drop');
3+
4+
var dropCurrentValue = flyd.transduce(drop(1));
25

36
module.exports = flyd.curryN(2, function(src, term) {
4-
return flyd.endsOn(flyd.merge(term, src.end), flyd.combine(function(src, self) {
7+
var end$ = term.hasVal ? dropCurrentValue(term) : term;
8+
return flyd.endsOn(flyd.merge(end$, src.end), flyd.combine(function(src, self) {
59
self(src());
610
}, [src]));
711
});

module/takeuntil/test/index.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,24 @@ describe('takeUntil', function() {
3838
assert.deepEqual(result, [1]);
3939
assert(s.end());
4040
});
41+
42+
it('works in nested streams', function() {
43+
var source = stream(1);
44+
var terminator = stream(true);
45+
46+
var value = stream(1).chain(function() {
47+
return takeUntil(source, terminator);
48+
})
49+
.map(function(val) {
50+
return val + 1;
51+
});
52+
53+
source(2)(3)(4)(5);
54+
55+
terminator(true);
56+
57+
source(6)(7)(8)(9);
58+
59+
assert.equal(value(), 6);
60+
})
4161
});

0 commit comments

Comments
 (0)