Skip to content

Commit 9b3b3d3

Browse files
committed
feat(facade): added support for observables
1 parent 43f4374 commit 9b3b3d3

10 files changed

Lines changed: 187 additions & 4 deletions

File tree

gulpfile.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ var _HTML_DEFAULT_SCRIPTS_JS = [
5050
{src: 'node_modules/zone.js/long-stack-trace-zone.js', mimeType: 'text/javascript', copy: true},
5151
{src: 'node_modules/systemjs/dist/system.src.js', mimeType: 'text/javascript', copy: true},
5252
{src: 'node_modules/systemjs/lib/extension-register.js', mimeType: 'text/javascript', copy: true},
53+
{src: 'node_modules/systemjs/lib/extension-cjs.js', mimeType: 'text/javascript', copy: true},
54+
{src: 'node_modules/rx/dist/rx.all.js', mimeType: 'text/javascript', copy: true},
5355
{src: 'tools/build/snippets/runtime_paths.js', mimeType: 'text/javascript', copy: true},
5456
{
5557
inline: 'System.import(\'$MODULENAME$\').then(function(m) { m.main(); }, console.error.bind(console))',

karma-js.conf.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ module.exports = function(config) {
1818
// Including systemjs because it defines `__eval`, which produces correct stack traces.
1919
'node_modules/systemjs/dist/system.src.js',
2020
'node_modules/systemjs/lib/extension-register.js',
21+
'node_modules/systemjs/lib/extension-cjs.js',
22+
'node_modules/rx/dist/rx.all.js',
2123
'node_modules/zone.js/zone.js',
2224
'node_modules/zone.js/long-stack-trace-zone.js',
2325

modules/angular2/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"dependencies": {
1010
"traceur": "<%= packageJson.dependencies.traceur %>",
1111
"rtts_assert": "<%= packageJson.version %>",
12+
"rx": "<%= packageJson.dependencies['rx'] %>",
1213
"zone.js": "<%= packageJson.dependencies['zone.js'] %>"
1314
},
1415
"devDependencies": <%= JSON.stringify(packageJson.devDependencies) %>

modules/angular2/src/facade/async.dart

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
library angular.core.facade.async;
22

33
import 'dart:async';
4-
export 'dart:async' show Future;
4+
export 'dart:async' show Future, Stream, StreamController, StreamSubscription;
55

66
class PromiseWrapper {
77
static Future resolve(obj) => new Future.value(obj);
@@ -32,6 +32,32 @@ class PromiseWrapper {
3232
}
3333
}
3434

35+
class ObservableWrapper {
36+
static StreamSubscription subscribe(Stream s, Function onNext, [onError, onComplete]) {
37+
return s.listen(onNext, onError: onError, onDone: onComplete, cancelOnError: true);
38+
}
39+
40+
static StreamController createController() {
41+
return new StreamController.broadcast();
42+
}
43+
44+
static Stream createObservable(StreamController controller) {
45+
return controller.stream;
46+
}
47+
48+
static void callNext(StreamController controller, value) {
49+
controller.add(value);
50+
}
51+
52+
static void callThrow(StreamController controller, error) {
53+
controller.addError(error);
54+
}
55+
56+
static void callReturn(StreamController controller) {
57+
controller.close();
58+
}
59+
}
60+
3561
class _Completer {
3662
final Completer c;
3763

modules/angular2/src/facade/async.es6

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import {int, global} from 'angular2/src/facade/lang';
1+
import {int, global, isPresent} from 'angular2/src/facade/lang';
22
import {List} from 'angular2/src/facade/collection';
3+
import Rx from 'rx/dist/rx.all';
34

45
export var Promise = global.Promise;
56

@@ -51,3 +52,47 @@ export class PromiseWrapper {
5152
return maybePromise instanceof Promise;
5253
}
5354
}
55+
56+
57+
/**
58+
* Use Rx.Observable but provides an adapter to make it work as specified here:
59+
* https://github.com/jhusain/observable-spec
60+
*
61+
* Once a reference implementation of the spec is available, switch to it.
62+
*/
63+
export var Observable = Rx.Observable;
64+
export var ObservableController = Rx.Subject;
65+
66+
export class ObservableWrapper {
67+
static createController():Rx.Subject {
68+
return new Rx.Subject();
69+
}
70+
71+
static createObservable(subject:Rx.Subject):Observable {
72+
return subject;
73+
}
74+
75+
static subscribe(observable:Observable, generatorOrOnNext, onThrow = null, onReturn = null) {
76+
if (isPresent(generatorOrOnNext.next)) {
77+
return observable.observeOn(Rx.Scheduler.timeout).subscribe(
78+
(value) => generatorOrOnNext.next(value),
79+
(error) => generatorOrOnNext.throw(error),
80+
() => generatorOrOnNext.return()
81+
);
82+
} else {
83+
return observable.observeOn(Rx.Scheduler.timeout).subscribe(generatorOrOnNext, onThrow, onReturn);
84+
}
85+
}
86+
87+
static callNext(subject:Rx.Subject, value:any) {
88+
subject.onNext(value);
89+
}
90+
91+
static callThrow(subject:Rx.Subject, error:any) {
92+
subject.onError(error);
93+
}
94+
95+
static callReturn(subject:Rx.Subject) {
96+
subject.onCompleted();
97+
}
98+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import {describe, it, expect, beforeEach, ddescribe, iit, xit, el,
2+
SpyObject, AsyncTestCompleter, inject, IS_DARTIUM} from 'angular2/test_lib';
3+
4+
import {ObservableWrapper, Observable, ObservableController, PromiseWrapper} from 'angular2/src/facade/async';
5+
6+
export function main() {
7+
describe('Observable', () => {
8+
var obs:Observable;
9+
var controller:ObservableController;
10+
11+
beforeEach(() => {
12+
controller = ObservableWrapper.createController();
13+
obs = ObservableWrapper.createObservable(controller);
14+
});
15+
16+
it("should call the next callback", inject([AsyncTestCompleter], (async) => {
17+
ObservableWrapper.subscribe(obs, (value) => {
18+
expect(value).toEqual(99);
19+
async.done();
20+
});
21+
22+
ObservableWrapper.callNext(controller, 99);
23+
}));
24+
25+
it("should call the throw callback", inject([AsyncTestCompleter], (async) => {
26+
ObservableWrapper.subscribe(obs, (_) => {}, (error) => {
27+
expect(error).toEqual("Boom");
28+
async.done();
29+
});
30+
ObservableWrapper.callThrow(controller, "Boom");
31+
}));
32+
33+
it("should call the return callback", inject([AsyncTestCompleter], (async) => {
34+
ObservableWrapper.subscribe(obs, (_) => {}, (_) => {}, () => {
35+
async.done();
36+
});
37+
38+
ObservableWrapper.callReturn(controller);
39+
}));
40+
41+
it("should subscribe to the wrapper asynchronously", () => {
42+
var called = false;
43+
ObservableWrapper.subscribe(obs, (value) => {
44+
called = true;
45+
});
46+
47+
ObservableWrapper.callNext(controller, 99);
48+
expect(called).toBe(false);
49+
});
50+
51+
if (!IS_DARTIUM) {
52+
// See here: https://github.com/jhusain/observable-spec
53+
describe("Generator", () => {
54+
var generator;
55+
56+
beforeEach(() => {
57+
generator = new SpyObject();
58+
generator.spy("next");
59+
generator.spy("throw");
60+
generator.spy("return");
61+
});
62+
63+
it("should call next on the given generator", inject([AsyncTestCompleter], (async) => {
64+
generator.spy("next").andCallFake((value) => {
65+
expect(value).toEqual(99);
66+
async.done();
67+
});
68+
69+
ObservableWrapper.subscribe(obs, generator);
70+
ObservableWrapper.callNext(controller, 99);
71+
}));
72+
73+
it("should call throw on the given generator", inject([AsyncTestCompleter], (async) => {
74+
generator.spy("throw").andCallFake((error) => {
75+
expect(error).toEqual("Boom");
76+
async.done();
77+
});
78+
ObservableWrapper.subscribe(obs, generator);
79+
ObservableWrapper.callThrow(controller, "Boom");
80+
}));
81+
82+
it("should call return on the given generator", inject([AsyncTestCompleter], (async) => {
83+
generator.spy("return").andCallFake(() => {
84+
async.done();
85+
});
86+
ObservableWrapper.subscribe(obs, generator);
87+
ObservableWrapper.callReturn(controller);
88+
}));
89+
});
90+
}
91+
92+
//TODO: vsavkin: add tests cases
93+
//should call dispose on the subscription if generator returns {done:true}
94+
//should call dispose on the subscription on throw
95+
//should call dispose on the subscription on return
96+
});
97+
}
98+
99+
//make sure rx observables are async

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"es6-module-loader": "^0.9.2",
2828
"systemjs": "^0.9.1",
2929
"traceur": "0.0.82",
30+
"rx": "2.4.6",
3031
"which": "~1",
3132
"zone.js": "0.4.0",
3233
"googleapis": "1.0.x",

test-main.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Use "register" extension from systemjs.
22
// That's what Traceur outputs: `System.register()`.
33
register(System);
4+
cjs(System);
45

56
jasmine.DEFAULT_TIMEOUT_INTERVAL = 50;
67

@@ -14,7 +15,8 @@ System.baseURL = '/base/modules/';
1415
// So that we can import packages like `core/foo`, instead of `core/src/foo`.
1516
System.paths = {
1617
'*': './*.js',
17-
'transpiler/*': '../tools/transpiler/*.js'
18+
'transpiler/*': '../tools/transpiler/*.js',
19+
'rx/*': '../node_modules/rx/*.js'
1820
}
1921

2022
// Import all the specs, execute their `main()` method and kick off Karma (Jasmine).
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
System.paths = {
2-
'*': '/*.js'
2+
'*': '/*.js',
3+
'rx/dist/*': '*.js'
34
};
45
register(System);
6+
cjs(System);
7+

tools/transpiler/src/type_mapping.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ export var typeMapping = {
55
'string': 'String',
66
'any': 'dynamic',
77
'Promise': 'Future',
8+
'Observable': 'Stream',
9+
'ObservableController': 'StreamController',
810
'Date': 'DateTime',
911
'StringMap': 'Map'
1012
};

0 commit comments

Comments
 (0)