Skip to content

Commit

Permalink
feat(SelectOperator): Add select operator
Browse files Browse the repository at this point in the history
  • Loading branch information
MikeRyanDev committed May 8, 2016
1 parent c2d999f commit 7703763
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 0 deletions.
10 changes: 10 additions & 0 deletions lib/add/operator/select.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Observable } from 'rxjs/Observable';
import { select, SelectSignature } from '../../operator/select';

Observable.prototype.select = select;

declare module 'rxjs/Observable' {
interface Observable<T> {
select: SelectSignature<T>;
}
}
26 changes: 26 additions & 0 deletions lib/operator/select.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { pluck } from 'rxjs/operator/pluck';
import { map } from 'rxjs/operator/map';
import { distinctUntilChanged } from 'rxjs/operator/distinctUntilChanged';
import { Observable } from 'rxjs/Observable';

export interface SelectSignature<T> {
<R>(...paths: string[]): Observable<R>;
<R>(mapFn: (state: T) => R): Observable<R>;
}

export function select<T, R>(pathOrMapFn: any, ...paths: string[]): Observable<R> {
let mapped$: Observable<R>;

if (typeof pathOrMapFn === 'string') {
mapped$ = pluck.call(this, pathOrMapFn, ...paths);
}
else if (typeof pathOrMapFn === 'function') {
mapped$ = map.call(this, pathOrMapFn);
}
else {
throw new TypeError(`Unexpected type ${ typeof pathOrMapFn } in select operator,`
+ ` expected 'string' or 'function'`);
}

return distinctUntilChanged.call(mapped$);
}
70 changes: 70 additions & 0 deletions spec/select.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import 'rxjs/add/observable/of';
import { Observable } from 'rxjs/Observable';

import '../lib/add/operator/select';

const state = {
todos: {
a: {
title: 'First Todo'
},
b: {
title: 'Second Todo'
}
},
filter: 'all'
};

describe('select Operator', function() {
it('should accept a string and return that property from the object', function(done) {
Observable.of(state)
.select('filter')
.subscribe({
next(val) {
expect(val).toBe(state.filter);
},
error: done,
complete: done
});
});

it('should return a list of strings and deeply map into the object', function(done) {
Observable.of(state)
.select('todos', 'a', 'title')
.subscribe({
next(val) {
expect(val).toBe(state.todos.a.title);
},
error: done,
complete: done
});
});

it('should accept a map function instead of a string', function(done) {
Observable.of(state)
.select(s => s.todos.b.title)
.subscribe({
next(val) {
expect(val).toBe(state.todos.b.title);
},
error: done,
complete: done
});
});

it('should not emit a new value if there is no change', function(done) {
let callCount = 0;
Observable.of(state, state, state)
.select('todos')
.subscribe({
next() {
++callCount;
},
error: done,
complete() {
expect(callCount).toEqual(1);
done();
}
});
});
});

0 comments on commit 7703763

Please sign in to comment.