Skip to content

Commit

Permalink
fix(Observable.from): standardise arguments (remove map/context)
Browse files Browse the repository at this point in the history
BREAKING CHANGE - Observable.from no longer supports the optional map function and associated context argument.
This change has been reflected in the related constructors and their properties have been standardised.
  • Loading branch information
chrisprice committed Aug 9, 2016
1 parent bdf9094 commit aa30af2
Show file tree
Hide file tree
Showing 8 changed files with 29 additions and 256 deletions.
79 changes: 0 additions & 79 deletions spec/observables/IteratorObservable-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,6 @@ describe('IteratorObservable', () => {
}).to.throw(Error, 'object is not iterable');
});

it('should not accept non-function project', () => {
expect(() => {
IteratorObservable.create([], 42);
}).to.throw(Error, 'when provided, `project` must be a function.');
});

it('should emit members of an array iterator', (done: MochaDone) => {
const expected = [10, 20, 30, 40];
IteratorObservable.create([10, 20, 30, 40])
Expand All @@ -55,8 +49,6 @@ describe('IteratorObservable', () => {
it('should emit members of an array iterator on a particular scheduler', () => {
const source = IteratorObservable.create(
[10, 20, 30, 40],
(x: number) => x,
null,
rxTestScheduler
);

Expand All @@ -65,32 +57,12 @@ describe('IteratorObservable', () => {
expectObservable(source).toBe('(abcd|)', values);
});

it('should emit members of an array iterator on a particular scheduler, project throws', () => {
const source = IteratorObservable.create(
[10, 20, 30, 40],
(x: number) => {
if (x === 30) {
throw 'error';
}
return x * x;
},
null,
rxTestScheduler
);

const values = { a: 100, b: 400 };

expectObservable(source).toBe('(ab#)', values);
});

it('should emit members of an array iterator on a particular scheduler, ' +
'but is unsubscribed early', (done: MochaDone) => {
const expected = [10, 20, 30, 40];

const source = IteratorObservable.create(
[10, 20, 30, 40],
(x: number) => x,
null,
Rx.Scheduler.queue
);

Expand All @@ -110,43 +82,6 @@ describe('IteratorObservable', () => {
source.subscribe(subscriber);
});

it('should emit members of an array iterator, and project them', (done: MochaDone) => {
const expected = [100, 400, 900, 1600];
IteratorObservable.create([10, 20, 30, 40], (x: number) => x * x)
.subscribe(
(x: number) => { expect(x).to.equal(expected.shift()); },
(x) => {
done(new Error('should not be called'));
}, () => {
expect(expected.length).to.equal(0);
done();
}
);
});

it('should emit members of an array iterator, and project but raise an error', (done: MochaDone) => {
const expected = [100, 400];
function project(x) {
if (x === 30) {
throw new Error('boom');
} else {
return x * x;
}
}
IteratorObservable.create([10, 20, 30, 40], project)
.subscribe(
(x: number) => {
expect(x).to.equal(expected.shift());
},
(err: any) => {
expect(expected.length).to.equal(0);
expect(err.message).to.equal('boom');
done();
}, () => {
done(new Error('should not be called'));
});
});

it('should emit characters of a string iterator', (done: MochaDone) => {
const expected = ['f', 'o', 'o'];
IteratorObservable.create('foo')
Expand All @@ -161,20 +96,6 @@ describe('IteratorObservable', () => {
);
});

it('should emit characters of a string iterator, and project them', (done: MochaDone) => {
const expected = ['F', 'O', 'O'];
IteratorObservable.create('foo', (x: string) => x.toUpperCase())
.subscribe(
(x: string) => { expect(x).to.equal(expected.shift()); },
(x) => {
done(new Error('should not be called'));
}, () => {
expect(expected.length).to.equal(0);
done();
}
);
});

it('should be possible to unsubscribe in the middle of the iteration', (done: MochaDone) => {
const expected = [10, 20, 30];

Expand Down
82 changes: 2 additions & 80 deletions spec/observables/from-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ describe('Observable.from', () => {
});
});

it('should return T and map for arrays', () => {
it('should return T for arrays', () => {
type(() => {
/* tslint:disable:no-unused-variable */
let o1: Rx.Observable<number> = Observable.from(<number[]>[], x => x.toString(), null, Rx.Scheduler.asap);
let o1: Rx.Observable<number> = Observable.from(<number[]>[], Rx.Scheduler.asap);
/* tslint:enable:no-unused-variable */
});
});
Expand Down Expand Up @@ -100,83 +100,5 @@ describe('Observable.from', () => {
);
expect(nextInvoked).to.equal(false);
});
it(`should accept ${source.name} and projection`, (done: MochaDone) => {
let nextInvoked = false;
Observable.from(source.value, x => x + 'x')
.subscribe(
(x: string) => {
nextInvoked = true;
expect(x).to.equal('xx');
},
(x) => {
done(new Error('should not be called'));
},
() => {
expect(nextInvoked).to.equal(true);
done();
}
);
});
it(`should accept ${source.name}, projection and scheduler`, (done: MochaDone) => {
let nextInvoked = false;
Observable.from(source.value, x => x + 'x', Rx.Scheduler.async)
.subscribe(
(x: string) => {
nextInvoked = true;
expect(x).to.equal('xx');
},
(x) => {
done(new Error('should not be called'));
},
() => {
expect(nextInvoked).to.equal(true);
done();
}
);
expect(nextInvoked).to.equal(false);
});
it(`should accept ${source.name}, projection and context`, (done: MochaDone) => {
const projection = function(x) {
expect(this.foo).to.equal('bar');
return x + 'x';
};
let nextInvoked = false;
Observable.from(source.value, projection, { foo: 'bar' })
.subscribe(
(x: string) => {
nextInvoked = true;
expect(x).to.equal('xx');
},
(x) => {
done(new Error('should not be called'));
},
() => {
expect(nextInvoked).to.equal(true);
done();
}
);
});
it(`should accept ${source.name}, projection, context and scheduler`, (done: MochaDone) => {
const projection = function(x) {
expect(this.foo).to.equal('bar');
return x + 'x';
};
let nextInvoked = false;
Observable.from(source.value, projection, { foo: 'bar' }, Rx.Scheduler.async)
.subscribe(
(x: string) => {
nextInvoked = true;
expect(x).to.equal('xx');
},
(x) => {
done(new Error('should not be called'));
},
() => {
expect(nextInvoked).to.equal(true);
done();
}
);
expect(nextInvoked).to.equal(false);
});
}
});
2 changes: 1 addition & 1 deletion spec/support/default.opts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
--ui spec-js/helpers/testScheduler-ui.js

--reporter dot
--bail

--full-trace
--check-leaks
--globals WebSocket,FormData,XDomainRequest,ActiveXObject
Expand Down
27 changes: 10 additions & 17 deletions src/observable/ArrayLikeObservable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,19 @@ import {TeardownLogic} from '../Subscription';
*/
export class ArrayLikeObservable<T> extends Observable<T> {

private mapFn: (x: T, y: number) => T;

static create<T>(arrayLike: ArrayLike<T>, mapFn: (x: T, y: number) => T, thisArg: any, scheduler?: Scheduler): Observable<T> {
static create<T>(arrayLike: ArrayLike<T>, scheduler?: Scheduler): Observable<T> {
const length = arrayLike.length;
if (length === 0) {
return new EmptyObservable<T>();
} else if (length === 1 && !mapFn) {
} else if (length === 1) {
return new ScalarObservable<T>(<any>arrayLike[0], scheduler);
} else {
return new ArrayLikeObservable(arrayLike, mapFn, thisArg, scheduler);
return new ArrayLikeObservable(arrayLike, scheduler);
}
}

static dispatch(state: any) {
const { arrayLike, index, length, mapFn, subscriber } = state;
const { arrayLike, index, length, subscriber } = state;

if (subscriber.closed) {
return;
Expand All @@ -37,8 +35,7 @@ export class ArrayLikeObservable<T> extends Observable<T> {
return;
}

const result = mapFn ? mapFn(arrayLike[index], index) : arrayLike[index];
subscriber.next(result);
subscriber.next(arrayLike[index]);

state.index = index + 1;

Expand All @@ -48,30 +45,26 @@ export class ArrayLikeObservable<T> extends Observable<T> {
// value used if Array has one value and _isScalar
private value: any;

constructor(private arrayLike: ArrayLike<T>, mapFn: (x: T, y: number) => T, thisArg: any, private scheduler?: Scheduler) {
constructor(private arrayLike: ArrayLike<T>, private scheduler?: Scheduler) {
super();
if (!mapFn && !scheduler && arrayLike.length === 1) {
if (!scheduler && arrayLike.length === 1) {
this._isScalar = true;
this.value = arrayLike[0];
}
if (mapFn) {
this.mapFn = mapFn.bind(thisArg);
}
}

protected _subscribe(subscriber: Subscriber<T>): TeardownLogic {
let index = 0;
const { arrayLike, mapFn, scheduler } = this;
const { arrayLike, scheduler } = this;
const length = arrayLike.length;

if (scheduler) {
return scheduler.schedule(ArrayLikeObservable.dispatch, 0, {
arrayLike, index, length, mapFn, subscriber
arrayLike, index, length, subscriber
});
} else {
for (let i = 0; i < length && !subscriber.closed; i++) {
const result = mapFn ? mapFn(arrayLike[i], i) : arrayLike[i];
subscriber.next(result);
subscriber.next(arrayLike[i]);
}
subscriber.complete();
}
Expand Down
2 changes: 1 addition & 1 deletion src/observable/ArrayObservable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export class ArrayObservable<T> extends Observable<T> {
// value used if Array has one value and _isScalar
value: any;

constructor(public array: T[], public scheduler?: Scheduler) {
constructor(private array: T[], private scheduler?: Scheduler) {
super();
if (!scheduler && array.length === 1) {
this._isScalar = true;
Expand Down
29 changes: 5 additions & 24 deletions src/observable/FromObservable.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import {isArray} from '../util/isArray';
import {isFunction} from '../util/isFunction';
import {isPromise} from '../util/isPromise';
import {isScheduler} from '../util/isScheduler';
import {PromiseObservable} from './PromiseObservable';
import {IteratorObservable} from'./IteratorObservable';
import {ArrayObservable} from './ArrayObservable';
Expand All @@ -22,12 +20,12 @@ const isArrayLike = (<T>(x: any): x is ArrayLike<T> => x && typeof x.length ===
* @hide true
*/
export class FromObservable<T> extends Observable<T> {
constructor(private ish: ObservableInput<T>, private scheduler: Scheduler) {
constructor(private ish: ObservableInput<T>, private scheduler?: Scheduler) {
super(null);
}

static create<T>(ish: ObservableInput<T>, scheduler?: Scheduler): Observable<T>;
static create<T, R>(ish: ArrayLike<T>, mapFn: (x: any, y: number) => R, thisArg?: any, scheduler?: Scheduler): Observable<R>;
static create<T, R>(ish: ArrayLike<T>, scheduler?: Scheduler): Observable<R>;

/**
* Creates an Observable from an Array, an array-like object, a Promise, an
Expand Down Expand Up @@ -71,11 +69,6 @@ export class FromObservable<T> extends Observable<T> {
* @param {ObservableInput<T>} ish A subscribable object, a Promise, an
* Observable-like, an Array, an iterable or an array-like object to be
* converted.
* @param {function(x: any, i: number): T} [mapFn] A "map" function to call
* when converting array-like objects, where `x` is a value from the
* array-like and `i` is the index of that value in the sequence.
* @param {any} [thisArg] The context object to use when calling the `mapFn`,
* if provided.
* @param {Scheduler} [scheduler] The scheduler on which to schedule the
* emissions of values.
* @return {Observable<T>} The Observable whose values are originally from the
Expand All @@ -84,19 +77,7 @@ export class FromObservable<T> extends Observable<T> {
* @name from
* @owner Observable
*/
static create<T>(ish: ObservableInput<T>,
mapFnOrScheduler?: Scheduler | ((x: any, y: number) => T),
thisArg?: any,
lastScheduler?: Scheduler): Observable<T> {
let scheduler: Scheduler = null;
let mapFn: (x: any, i: number) => T = null;
if (isFunction(mapFnOrScheduler)) {
scheduler = lastScheduler || null;
mapFn = <(x: any, i: number) => T> mapFnOrScheduler;
} else if (isScheduler(scheduler)) {
scheduler = <Scheduler> mapFnOrScheduler;
}

static create<T>(ish: ObservableInput<T>, scheduler?: Scheduler): Observable<T> {
if (ish != null) {
if (typeof ish[$$observable] === 'function') {
if (ish instanceof Observable && !scheduler) {
Expand All @@ -108,9 +89,9 @@ export class FromObservable<T> extends Observable<T> {
} else if (isPromise(ish)) {
return new PromiseObservable<T>(ish, scheduler);
} else if (typeof ish[$$iterator] === 'function' || typeof ish === 'string') {
return new IteratorObservable<T>(<any>ish, null, null, scheduler);
return new IteratorObservable<T>(ish, scheduler);
} else if (isArrayLike(ish)) {
return new ArrayLikeObservable(ish, mapFn, thisArg, scheduler);
return new ArrayLikeObservable(ish, scheduler);
}
}

Expand Down
Loading

0 comments on commit aa30af2

Please sign in to comment.