Skip to content

Commit 315a1f6

Browse files
feat(fp): add fp operators
generates the new operators using the existing prototype operators, and a script to generate the resulting fp types.
1 parent 7276d7e commit 315a1f6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+5036
-109
lines changed

package.json

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@
5656
"commitmsg": "validate-commit-msg",
5757
"info": "npm-scripts-info",
5858
"build_all": "npm-run-all build_cjs build_global generate_packages",
59-
"build_cjs": "npm-run-all clean_dist_cjs copy_src_cjs compile_dist_cjs",
60-
"build_es6": "npm-run-all clean_dist_es6 copy_src_es6 compile_module_es6",
61-
"build_es6_for_docs": "npm-run-all clean_dist_es6 copy_src_es6 compile_dist_es6_for_docs",
59+
"build_cjs": "npm-run-all generate_fp clean_dist_cjs copy_src_cjs compile_dist_cjs",
60+
"build_es6": "npm-run-all generate_fp clean_dist_es6 copy_src_es6 compile_module_es6",
61+
"build_es6_for_docs": "npm-run-all generate_fp clean_dist_es6 copy_src_es6 compile_dist_es6_for_docs",
6262
"build_closure_core": "node ./tools/make-closure-core.js",
6363
"build_global": "npm-run-all clean_dist_global build_es6 && mkdirp ./dist/global && node ./tools/make-umd-bundle.js && npm-run-all build_closure_core clean_dist_es6",
6464
"build_perf": "webdriver-manager update && npm-run-all build_cjs build_global perf",
@@ -75,13 +75,14 @@
7575
"copy_src_cjs": "mkdirp ./dist/cjs/src && shx cp -r ./src/* ./dist/cjs/src",
7676
"copy_src_es6": "mkdirp ./dist/es6/src && shx cp -r ./src/* ./dist/es6/src",
7777
"commit": "git-cz",
78-
"compile_dist_cjs": "tsc ./dist/cjs/src/Rx.ts ./dist/cjs/src/add/observable/of.ts -m commonjs --lib es5,es2015.iterable,es2015.collection,es2015.promise,dom --sourceMap --outDir ./dist/cjs --target ES5 -d --diagnostics --pretty --noImplicitAny --noImplicitReturns --noImplicitThis --suppressImplicitAnyIndexErrors --moduleResolution node",
79-
"compile_module_es6": "tsc ./dist/es6/src/Rx.ts ./dist/es6/src/add/observable/of.ts -m es2015 --sourceMap --outDir ./dist/es6 --target ES5 -d --diagnostics --pretty --noImplicitAny --noImplicitReturns --noImplicitThis --suppressImplicitAnyIndexErrors --moduleResolution node --noEmitHelpers --lib es5,es2015.iterable,es2015.collection,es2015.promise,dom ",
80-
"compile_dist_es6_for_docs": "tsc ./dist/es6/src/Rx.ts ./dist/es6/src/add/observable/of.ts ./dist/es6/src/MiscJSDoc.ts -m es2015 --sourceMap --outDir ./dist/es6 --target ES6 -d --diagnostics --pretty --noImplicitAny --noImplicitReturns --noImplicitThis --suppressImplicitAnyIndexErrors --moduleResolution node",
81-
"cover": "shx rm -rf dist/cjs && tsc src/Rx.ts src/add/observable/of.ts -m commonjs --lib es5,es2015.iterable,es2015.collection,es2015.promise,dom --outDir dist/cjs --sourceMap --target ES5 -d && nyc --reporter=lcov --reporter=html --exclude=spec/support/**/* --exclude=spec-js/**/* --exclude=node_modules mocha --opts spec/support/default.opts spec-js",
78+
"compile_dist_cjs": "tsc ./dist/cjs/src/Rx.ts ./dist/cjs/src/fp.ts ./dist/cjs/src/add/observable/of.ts -m commonjs --lib es5,es2015.iterable,es2015.collection,es2015.promise,dom --sourceMap --outDir ./dist/cjs --target ES5 -d --diagnostics --pretty --noImplicitAny --noImplicitReturns --noImplicitThis --suppressImplicitAnyIndexErrors --moduleResolution node",
79+
"compile_module_es6": "tsc ./dist/es6/src/Rx.ts ./dist/es6/src/fp.ts ./dist/es6/src/add/observable/of.ts -m es2015 --sourceMap --outDir ./dist/es6 --target ES5 -d --diagnostics --pretty --noImplicitAny --noImplicitReturns --noImplicitThis --suppressImplicitAnyIndexErrors --moduleResolution node --noEmitHelpers --lib es5,es2015.iterable,es2015.collection,es2015.promise,dom ",
80+
"compile_dist_es6_for_docs": "tsc ./dist/es6/src/Rx.ts ./dist/es6/src/fp.ts ./dist/es6/src/add/observable/of.ts ./dist/es6/src/MiscJSDoc.ts -m es2015 --sourceMap --outDir ./dist/es6 --target ES6 -d --diagnostics --pretty --noImplicitAny --noImplicitReturns --noImplicitThis --suppressImplicitAnyIndexErrors --moduleResolution node",
81+
"cover": "shx rm -rf dist/cjs && tsc src/Rx.ts src/fp.ts src/add/observable/of.ts -m commonjs --lib es5,es2015.iterable,es2015.collection,es2015.promise,dom --outDir dist/cjs --sourceMap --target ES5 -d && nyc --reporter=lcov --reporter=html --exclude=spec/support/**/* --exclude=spec-js/**/* --exclude=node_modules mocha --opts spec/support/default.opts spec-js",
8282
"decision_tree_widget": "cd doc/decision-tree-widget && npm run build && cd ../..",
8383
"doctoc": "doctoc CONTRIBUTING.md",
8484
"generate_packages": "node .make-packages.js",
85+
"generate_fp": "node tools/make-fp.js",
8586
"lint_perf": "eslint perf/",
8687
"lint_spec": "tslint -c tslint.json \"spec/**/*.ts\"",
8788
"lint_src": "tslint -c tslint.json \"src/**/*.ts\"",

spec/Observable-spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ describe('Observable.create', () => {
656656

657657
describe('pipe', () => {
658658
it('should exist', () => {
659-
expect(Observable.prototype.pipe).to.exist;
659+
expect(Observable.prototype.let).to.exist;
660660
});
661661

662662
it('should pipe through multiple functions', () => {
@@ -666,7 +666,7 @@ describe('Observable.create', () => {
666666
const ob4 = Observable.of(4);
667667
const results = [];
668668

669-
const piped = ob1.pipe(
669+
const piped = ob1.let(
670670
s => {
671671
expect(s).to.equal(ob1);
672672
return ob2;

spec/operators/groupBy-spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {expect} from 'chai';
22
import * as Rx from '../../dist/cjs/Rx';
3-
import {GroupedObservable} from '../../dist/cjs/operator/groupBy';
3+
import {GroupedObservable} from '../../dist/cjs/observable/GroupedObservable';
44
import marbleTestingSignature = require('../helpers/marble-testing'); // tslint:disable-line:no-require-imports
55

66
declare const { asDiagram };

spec/pipe/map-spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ declare const cold: typeof marbleTestingSignature.cold;
88
declare const expectObservable: typeof marbleTestingSignature.expectObservable;
99
declare const expectSubscriptions: typeof marbleTestingSignature.expectSubscriptions;
1010

11-
describe('map-pipe', () => {
12-
it('should pipe through the map operator', () => {
11+
describe('map-let', () => {
12+
it('should let through the map operator', () => {
1313
const s1 = cold( '---a--b--c--|');
1414
const project = x => x + x;
15-
expectObservable(Rx.Pipe.map(project)(s1)).toBe(s1.map(project));
15+
expectObservable(Rx.fp.map(project)(s1)).toBe(s1.map(project));
1616
});
1717

18-
it('should work with Observable.prototype.pipe', () => {
18+
it('should work with Observable.prototype.let', () => {
1919
const values = {
2020
a: 1,
2121
b: 2,
@@ -27,9 +27,9 @@ describe('map-pipe', () => {
2727

2828
const s1 = cold( '---a---b---c---|', values);
2929
const expected = '---x---y---z---|';
30-
const { map } = Rx.Pipe;
30+
const { map } = Rx.fp;
3131

32-
const t1 = s1.pipe(
32+
const t1 = s1.let(
3333
map((x: number) => x + 1),
3434
map((x: number) => x / 2)
3535
);

src/Observable.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { toSubscriber } from './util/toSubscriber';
77
import { IfObservable } from './observable/IfObservable';
88
import { ErrorObservable } from './observable/ErrorObservable';
99
import { observable as Symbol_observable } from './symbol/observable';
10-
import { compose } from './util/compose';
1110
import { letProto } from './operator/let';
1211

1312
export interface Subscribable<T> {
@@ -171,11 +170,6 @@ export class Observable<T> implements Subscribable<T> {
171170
});
172171
}
173172

174-
pipe(...fns: ((x: Observable<T>) => Observable<T>)[]): Observable<T> {
175-
const composed = compose.apply(this, fns);
176-
return composed(this);
177-
}
178-
179173
protected _subscribe(subscriber: Subscriber<any>): TeardownLogic {
180174
return this.source.subscribe(subscriber);
181175
}

src/Rx.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -176,15 +176,8 @@ import { observable } from './symbol/observable';
176176
/* tslint:enable:no-unused-variable */
177177
export { compose } from './util/compose';
178178

179-
// pull in pipe definitions
180-
import { map as mapPipe } from './pipe/map';
181-
182-
/**
183-
* @typedef {Object} Rx.Pipe
184-
*/
185-
let Pipe = {
186-
map: mapPipe
187-
};
179+
// pull in fp definitions
180+
import * as fp from './fp';
188181

189182
/**
190183
* @typedef {Object} Rx.Scheduler
@@ -228,6 +221,6 @@ let Symbol = {
228221
export {
229222
Scheduler,
230223
Symbol,
231-
Pipe,
224+
fp,
232225
Observable
233226
};

src/fp.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
export { audit } from './fp/audit';
2+
export { auditTime } from './fp/auditTime';
3+
export { buffer } from './fp/buffer';
4+
export { bufferCount } from './fp/bufferCount';
5+
export { bufferTime } from './fp/bufferTime';
6+
export { bufferToggle } from './fp/bufferToggle';
7+
export { bufferWhen } from './fp/bufferWhen';
8+
export { _catch } from './fp/catch';
9+
export { combineAll } from './fp/combineAll';
10+
export { combineLatest } from './fp/combineLatest';
11+
export { concat } from './fp/concat';
12+
export { concatAll } from './fp/concatAll';
13+
export { concatMap } from './fp/concatMap';
14+
export { concatMapTo } from './fp/concatMapTo';
15+
export { count } from './fp/count';
16+
export { debounce } from './fp/debounce';
17+
export { debounceTime } from './fp/debounceTime';
18+
export { defaultIfEmpty } from './fp/defaultIfEmpty';
19+
export { delay } from './fp/delay';
20+
export { delayWhen } from './fp/delayWhen';
21+
export { dematerialize } from './fp/dematerialize';
22+
export { distinct } from './fp/distinct';
23+
export { distinctUntilChanged } from './fp/distinctUntilChanged';
24+
export { distinctUntilKeyChanged } from './fp/distinctUntilKeyChanged';
25+
export { _do } from './fp/do';
26+
export { elementAt } from './fp/elementAt';
27+
export { every } from './fp/every';
28+
export { exhaust } from './fp/exhaust';
29+
export { exhaustMap } from './fp/exhaustMap';
30+
export { expand } from './fp/expand';
31+
export { filter } from './fp/filter';
32+
export { _finally } from './fp/finally';
33+
export { find } from './fp/find';
34+
export { findIndex } from './fp/findIndex';
35+
export { first } from './fp/first';
36+
export { groupBy } from './fp/groupBy';
37+
export { ignoreElements } from './fp/ignoreElements';
38+
export { isEmpty } from './fp/isEmpty';
39+
export { last } from './fp/last';
40+
export { letProto } from './fp/let';
41+
export { map } from './fp/map';
42+
export { mapTo } from './fp/mapTo';
43+
export { materialize } from './fp/materialize';
44+
export { max } from './fp/max';
45+
export { merge } from './fp/merge';
46+
export { mergeAll } from './fp/mergeAll';
47+
export { mergeMap } from './fp/mergeMap';
48+
export { mergeMapTo } from './fp/mergeMapTo';
49+
export { mergeScan } from './fp/mergeScan';
50+
export { min } from './fp/min';
51+
export { multicast } from './fp/multicast';
52+
export { observeOn } from './fp/observeOn';
53+
export { onErrorResumeNext } from './fp/onErrorResumeNext';
54+
export { pairwise } from './fp/pairwise';
55+
export { partition } from './fp/partition';
56+
export { pluck } from './fp/pluck';
57+
export { publish } from './fp/publish';
58+
export { publishBehavior } from './fp/publishBehavior';
59+
export { publishLast } from './fp/publishLast';
60+
export { publishReplay } from './fp/publishReplay';
61+
export { race } from './fp/race';
62+
export { reduce } from './fp/reduce';
63+
export { repeat } from './fp/repeat';
64+
export { repeatWhen } from './fp/repeatWhen';
65+
export { retry } from './fp/retry';
66+
export { retryWhen } from './fp/retryWhen';
67+
export { sample } from './fp/sample';
68+
export { sampleTime } from './fp/sampleTime';
69+
export { scan } from './fp/scan';
70+
export { sequenceEqual } from './fp/sequenceEqual';
71+
export { share } from './fp/share';
72+
export { single } from './fp/single';
73+
export { skip } from './fp/skip';
74+
export { skipUntil } from './fp/skipUntil';
75+
export { skipWhile } from './fp/skipWhile';
76+
export { startWith } from './fp/startWith';
77+
export { subscribeOn } from './fp/subscribeOn';
78+
export { _switch } from './fp/switch';
79+
export { switchMap } from './fp/switchMap';
80+
export { switchMapTo } from './fp/switchMapTo';
81+
export { take } from './fp/take';
82+
export { takeLast } from './fp/takeLast';
83+
export { takeUntil } from './fp/takeUntil';
84+
export { takeWhile } from './fp/takeWhile';
85+
export { throttle } from './fp/throttle';
86+
export { throttleTime } from './fp/throttleTime';
87+
export { timeInterval } from './fp/timeInterval';
88+
export { timeout } from './fp/timeout';
89+
export { timeoutWith } from './fp/timeoutWith';
90+
export { timestamp } from './fp/timestamp';
91+
export { toArray } from './fp/toArray';
92+
export { toPromise } from './fp/toPromise';
93+
export { window } from './fp/window';
94+
export { windowCount } from './fp/windowCount';
95+
export { windowTime } from './fp/windowTime';
96+
export { windowToggle } from './fp/windowToggle';
97+
export { windowWhen } from './fp/windowWhen';
98+
export { withLatestFrom } from './fp/withLatestFrom';
99+
export { zipProto } from './fp/zip';
100+
export { zipAll } from './fp/zipAll';

src/fp/audit.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Generated code ahead... there be dragons!
2+
// tslint:disable
3+
import { audit as auditBase } from '../operator/audit';
4+
import { Subscriber } from '../Subscriber';
5+
import { Observable, SubscribableOrPromise } from '../Observable';
6+
import { Subscription, TeardownLogic } from '../Subscription';
7+
8+
import { tryCatch } from '../util/tryCatch';
9+
import { errorObject } from '../util/errorObject';
10+
import { OuterSubscriber } from '../OuterSubscriber';
11+
import { subscribeToResult } from '../util/subscribeToResult';
12+
13+
/**
14+
* Ignores source values for a duration determined by another Observable, then
15+
* emits the most recent value from the source Observable, then repeats this
16+
* process.
17+
*
18+
* <span class="informal">It's like {@link auditTime}, but the silencing
19+
* duration is determined by a second Observable.</span>
20+
*
21+
* <img src="./img/audit.png" width="100%">
22+
*
23+
* `audit` is similar to `throttle`, but emits the last value from the silenced
24+
* time window, instead of the first value. `audit` emits the most recent value
25+
* from the source Observable on the output Observable as soon as its internal
26+
* timer becomes disabled, and ignores source values while the timer is enabled.
27+
* Initially, the timer is disabled. As soon as the first source value arrives,
28+
* the timer is enabled by calling the `durationSelector` function with the
29+
* source value, which returns the "duration" Observable. When the duration
30+
* Observable emits a value or completes, the timer is disabled, then the most
31+
* recent source value is emitted on the output Observable, and this process
32+
* repeats for the next source value.
33+
*
34+
* @example <caption>Emit clicks at a rate of at most one click per second</caption>
35+
* var clicks = Rx.Observable.fromEvent(document, 'click');
36+
* var result = clicks.audit(ev => Rx.Observable.interval(1000));
37+
* result.subscribe(x => console.log(x));
38+
*
39+
* @see {@link auditTime}
40+
* @see {@link debounce}
41+
* @see {@link delayWhen}
42+
* @see {@link sample}
43+
* @see {@link throttle}
44+
*
45+
* @param {function(value: T): SubscribableOrPromise} durationSelector A function
46+
* that receives a value from the source Observable, for computing the silencing
47+
* duration, returned as an Observable or a Promise.
48+
* @return {Observable<T>} An Observable that performs rate-limiting of
49+
* emissions from the source Observable.
50+
* @method audit
51+
* @owner Observable
52+
*/
53+
export function audit<T>( durationSelector: (value: T) => SubscribableOrPromise<any>): (source: Observable<T>) => Observable<T> {
54+
return (source: Observable<T>) => auditBase.call(source, durationSelector);
55+
}

src/fp/auditTime.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Generated code ahead... there be dragons!
2+
// tslint:disable
3+
import { auditTime as auditTimeBase } from '../operator/auditTime';
4+
import { async } from '../scheduler/async';
5+
import { IScheduler } from '../Scheduler';
6+
import { Subscriber } from '../Subscriber';
7+
import { Observable } from '../Observable';
8+
import { Subscription, TeardownLogic } from '../Subscription';
9+
10+
/**
11+
* Ignores source values for `duration` milliseconds, then emits the most recent
12+
* value from the source Observable, then repeats this process.
13+
*
14+
* <span class="informal">When it sees a source values, it ignores that plus
15+
* the next ones for `duration` milliseconds, and then it emits the most recent
16+
* value from the source.</span>
17+
*
18+
* <img src="./img/auditTime.png" width="100%">
19+
*
20+
* `auditTime` is similar to `throttleTime`, but emits the last value from the
21+
* silenced time window, instead of the first value. `auditTime` emits the most
22+
* recent value from the source Observable on the output Observable as soon as
23+
* its internal timer becomes disabled, and ignores source values while the
24+
* timer is enabled. Initially, the timer is disabled. As soon as the first
25+
* source value arrives, the timer is enabled. After `duration` milliseconds (or
26+
* the time unit determined internally by the optional `scheduler`) has passed,
27+
* the timer is disabled, then the most recent source value is emitted on the
28+
* output Observable, and this process repeats for the next source value.
29+
* Optionally takes a {@link IScheduler} for managing timers.
30+
*
31+
* @example <caption>Emit clicks at a rate of at most one click per second</caption>
32+
* var clicks = Rx.Observable.fromEvent(document, 'click');
33+
* var result = clicks.auditTime(1000);
34+
* result.subscribe(x => console.log(x));
35+
*
36+
* @see {@link audit}
37+
* @see {@link debounceTime}
38+
* @see {@link delay}
39+
* @see {@link sampleTime}
40+
* @see {@link throttleTime}
41+
*
42+
* @param {number} duration Time to wait before emitting the most recent source
43+
* value, measured in milliseconds or the time unit determined internally
44+
* by the optional `scheduler`.
45+
* @param {Scheduler} [scheduler=async] The {@link IScheduler} to use for
46+
* managing the timers that handle the rate-limiting behavior.
47+
* @return {Observable<T>} An Observable that performs rate-limiting of
48+
* emissions from the source Observable.
49+
* @method auditTime
50+
* @owner Observable
51+
*/
52+
export function auditTime<T>( duration: number, scheduler: IScheduler = async): (source: Observable<T>) => Observable<T> {
53+
return (source: Observable<T>) => auditTimeBase.call(source, duration, scheduler);
54+
}

src/fp/buffer.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Generated code ahead... there be dragons!
2+
// tslint:disable
3+
import { buffer as bufferBase } from '../operator/buffer';
4+
import { Subscriber } from '../Subscriber';
5+
import { Observable } from '../Observable';
6+
7+
import { OuterSubscriber } from '../OuterSubscriber';
8+
import { InnerSubscriber } from '../InnerSubscriber';
9+
import { subscribeToResult } from '../util/subscribeToResult';
10+
11+
/**
12+
* Buffers the source Observable values until `closingNotifier` emits.
13+
*
14+
* <span class="informal">Collects values from the past as an array, and emits
15+
* that array only when another Observable emits.</span>
16+
*
17+
* <img src="./img/buffer.png" width="100%">
18+
*
19+
* Buffers the incoming Observable values until the given `closingNotifier`
20+
* Observable emits a value, at which point it emits the buffer on the output
21+
* Observable and starts a new buffer internally, awaiting the next time
22+
* `closingNotifier` emits.
23+
*
24+
* @example <caption>On every click, emit array of most recent interval events</caption>
25+
* var clicks = Rx.Observable.fromEvent(document, 'click');
26+
* var interval = Rx.Observable.interval(1000);
27+
* var buffered = interval.buffer(clicks);
28+
* buffered.subscribe(x => console.log(x));
29+
*
30+
* @see {@link bufferCount}
31+
* @see {@link bufferTime}
32+
* @see {@link bufferToggle}
33+
* @see {@link bufferWhen}
34+
* @see {@link window}
35+
*
36+
* @param {Observable<any>} closingNotifier An Observable that signals the
37+
* buffer to be emitted on the output Observable.
38+
* @return {Observable<T[]>} An Observable of buffers, which are arrays of
39+
* values.
40+
* @method buffer
41+
* @owner Observable
42+
*/
43+
export function buffer<T>( closingNotifier: Observable<any>): (source: Observable<T>) => Observable<T[]> {
44+
return (source: Observable<T>) => bufferBase.call(source, closingNotifier);
45+
}

0 commit comments

Comments
 (0)