Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit 2d02e39

Browse files
IgorMinarmhevery
authored andcommitted
feat(ProxySpec): create a ProxySpec which can proxy to other ZoneSpecs.
This allows changing of the zone behavior ofter it has been created.
1 parent dafad98 commit 2d02e39

File tree

5 files changed

+270
-0
lines changed

5 files changed

+270
-0
lines changed

gulpfile.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,14 @@ gulp.task('build/long-stack-trace-zone.min.js', function(cb) {
9797
return generateBrowserScript('./lib/zone-spec/long-stack-trace.ts', 'long-stack-trace-zone.min.js', true, cb);
9898
});
9999

100+
gulp.task('build/proxy-zone.js', function(cb) {
101+
return generateBrowserScript('./lib/zone-spec/proxy.ts', 'proxy-zone.js', false, cb);
102+
});
103+
104+
gulp.task('build/proxy-zone.min.js', function(cb) {
105+
return generateBrowserScript('./lib/zone-spec/proxy.ts', 'proxy-zone.min.js', true, cb);
106+
});
107+
100108
gulp.task('build/wtf.js', function(cb) {
101109
return generateBrowserScript('./lib/zone-spec/wtf.ts', 'wtf.js', false, cb);
102110
});
@@ -126,6 +134,8 @@ gulp.task('build', [
126134
'build/jasmine-patch.min.js',
127135
'build/long-stack-trace-zone.js',
128136
'build/long-stack-trace-zone.min.js',
137+
'build/proxy-zone.js',
138+
'build/proxy-zone.min.js',
129139
'build/wtf.js',
130140
'build/wtf.min.js',
131141
'build/async-test.js',

lib/zone-spec/proxy.ts

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
(function () {
2+
class ProxyZoneSpec implements ZoneSpec {
3+
name: string = 'ProxyZone';
4+
5+
private _delegateSpec: ZoneSpec;
6+
7+
properties: {[k: string]: any} = {'ProxyZoneSpec': this};
8+
propertyKeys: string[] = null;
9+
10+
static get(): ProxyZoneSpec {
11+
return Zone.current.get('ProxyZoneSpec');
12+
}
13+
14+
static isLoaded(): boolean {
15+
return ProxyZoneSpec.get() instanceof ProxyZoneSpec;
16+
}
17+
18+
static assertPresent(): ProxyZoneSpec {
19+
if (!this.isLoaded()) {
20+
throw new Error(`Expected to be running in 'ProxyZone', but it was not found.`);
21+
}
22+
return ProxyZoneSpec.get();
23+
}
24+
25+
constructor(private defaultSpecDelegate: ZoneSpec = null) {
26+
this.setDelegate(defaultSpecDelegate);
27+
}
28+
29+
30+
setDelegate(delegateSpec: ZoneSpec) {
31+
this._delegateSpec = delegateSpec;
32+
this.propertyKeys && this.propertyKeys.forEach((key) => delete this.properties[key]);
33+
this.propertyKeys = null;
34+
if (delegateSpec && delegateSpec.properties) {
35+
this.propertyKeys = Object.keys(delegateSpec.properties);
36+
this.propertyKeys.forEach((k) => this.properties[k] = delegateSpec.properties[k]);
37+
}
38+
}
39+
40+
getDelegate() {
41+
return this._delegateSpec;
42+
}
43+
44+
45+
resetDelegate() {
46+
this.setDelegate(this.defaultSpecDelegate);
47+
}
48+
49+
50+
onFork(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
51+
zoneSpec: ZoneSpec): Zone {
52+
if (this._delegateSpec && this._delegateSpec.onFork) {
53+
return this._delegateSpec.onFork(parentZoneDelegate, currentZone, targetZone, zoneSpec);
54+
} else {
55+
return parentZoneDelegate.fork(targetZone, zoneSpec);
56+
}
57+
}
58+
59+
60+
onIntercept(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
61+
delegate: Function, source: string): Function {
62+
if (this._delegateSpec && this._delegateSpec.onIntercept) {
63+
return this._delegateSpec.onIntercept(parentZoneDelegate, currentZone, targetZone, delegate, source);
64+
} else {
65+
return parentZoneDelegate.intercept(targetZone, delegate, source);
66+
}
67+
}
68+
69+
70+
onInvoke(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
71+
delegate: Function, applyThis: any, applyArgs: any[], source: string): any {
72+
if (this._delegateSpec && this._delegateSpec.onInvoke) {
73+
return this._delegateSpec.onInvoke(parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source);
74+
} else {
75+
return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source);
76+
}
77+
}
78+
79+
onHandleError(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
80+
error: any): boolean {
81+
if (this._delegateSpec && this._delegateSpec.onHandleError) {
82+
return this._delegateSpec.onHandleError(parentZoneDelegate, currentZone, targetZone, error);
83+
} else {
84+
return parentZoneDelegate.handleError(targetZone, error);
85+
}
86+
}
87+
88+
onScheduleTask(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
89+
task: Task): Task {
90+
if (this._delegateSpec && this._delegateSpec.onScheduleTask) {
91+
return this._delegateSpec.onScheduleTask(parentZoneDelegate, currentZone, targetZone, task);
92+
} else {
93+
return parentZoneDelegate.scheduleTask(targetZone, task);
94+
}
95+
}
96+
97+
onInvokeTask(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
98+
task: Task, applyThis: any, applyArgs: any): any {
99+
if (this._delegateSpec && this._delegateSpec.onFork) {
100+
return this._delegateSpec.onInvokeTask(parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs);
101+
} else {
102+
return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs);
103+
}
104+
}
105+
106+
onCancelTask(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
107+
task: Task): any {
108+
if (this._delegateSpec && this._delegateSpec.onCancelTask) {
109+
return this._delegateSpec.onCancelTask(parentZoneDelegate, currentZone, targetZone, task);
110+
} else {
111+
return parentZoneDelegate.cancelTask(targetZone, task);
112+
}
113+
}
114+
115+
onHasTask(delegate: ZoneDelegate, current: Zone, target: Zone,
116+
hasTaskState: HasTaskState): void {
117+
if (this._delegateSpec && this._delegateSpec.onHasTask) {
118+
this._delegateSpec.onHasTask(delegate, current, target, hasTaskState);
119+
} else {
120+
delegate.hasTask(target, hasTaskState);
121+
}
122+
}
123+
}
124+
125+
// Export the class so that new instances can be created with proper
126+
// constructor params.
127+
Zone['ProxyZoneSpec'] = ProxyZoneSpec;
128+
})();

test/browser_entry_point.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import '../lib/zone';
66
import '../lib/browser/browser.ts';
77
import '../lib/zone-spec/long-stack-trace';
88
import '../lib/zone-spec/wtf';
9+
import '../lib/zone-spec/proxy';
910

1011
// Setup test environment
1112
import './test-env-setup';

test/common_tests.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ import './zone-spec/long-stack-trace-zone.spec';
88
import './zone-spec/async-test.spec';
99
import './zone-spec/sync-test.spec';
1010
import './zone-spec/fake-async-test.spec';
11+
import './zone-spec/proxy.spec';

test/zone-spec/proxy.spec.ts

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import '../../lib/zone-spec/proxy';
2+
3+
describe('ProxySpec', () => {
4+
let ProxyZoneSpec: any;
5+
let delegate: ZoneSpec;
6+
let proxyZoneSpec: any;
7+
let proxyZone: Zone;
8+
9+
beforeEach(() => {
10+
ProxyZoneSpec = Zone['ProxyZoneSpec'];
11+
expect(typeof ProxyZoneSpec).toBe('function');
12+
delegate = {name: 'delegate'};
13+
proxyZoneSpec = new ProxyZoneSpec(delegate);
14+
proxyZone = Zone.current.fork(proxyZoneSpec);
15+
});
16+
17+
describe('properties', () => {
18+
it('should expose ProxyZone in the properties', () => {
19+
expect(proxyZone.get('ProxyZoneSpec')).toBe(proxyZoneSpec);
20+
});
21+
22+
it('should assert that it is in or out of ProxyZone', () => {
23+
expect(() => ProxyZoneSpec.assertPresent()).toThrow();
24+
expect(ProxyZoneSpec.isLoaded()).toBe(false);
25+
expect(ProxyZoneSpec.get()).toBe(undefined);
26+
proxyZone.run(() => {
27+
expect(ProxyZoneSpec.isLoaded()).toBe(true);
28+
expect(() => ProxyZoneSpec.assertPresent()).not.toThrow();
29+
expect(ProxyZoneSpec.get()).toBe(proxyZoneSpec);
30+
});
31+
});
32+
33+
it('should reset properties', () => {
34+
expect(proxyZone.get('myTestKey')).toBe(undefined);
35+
proxyZoneSpec.setDelegate({name: 'd1', properties: {'myTestKey': 'myTestValue'}});
36+
expect(proxyZone.get('myTestKey')).toBe('myTestValue');
37+
proxyZoneSpec.resetDelegate();
38+
expect(proxyZone.get('myTestKey')).toBe(undefined);
39+
});
40+
});
41+
42+
describe('delegate', () => {
43+
it('should set/reset delegate', () => {
44+
const defaultDelegate: ZoneSpec = {name: 'defaultDelegate'};
45+
const otherDelegate: ZoneSpec = {name: 'otherDelegate'};
46+
const proxyZoneSpec = new ProxyZoneSpec(defaultDelegate);
47+
const proxyZone = Zone.current.fork(proxyZoneSpec);
48+
49+
expect(proxyZoneSpec.getDelegate()).toEqual(defaultDelegate);
50+
51+
proxyZoneSpec.setDelegate(otherDelegate);
52+
expect(proxyZoneSpec.getDelegate()).toEqual(otherDelegate);
53+
proxyZoneSpec.resetDelegate();
54+
expect(proxyZoneSpec.getDelegate()).toEqual(defaultDelegate);
55+
});
56+
});
57+
58+
describe('forwarding', () => {
59+
beforeEach(() => {
60+
proxyZoneSpec = new ProxyZoneSpec();
61+
proxyZone = Zone.current.fork(proxyZoneSpec);
62+
});
63+
64+
it('should fork', () => {
65+
const forkeZone = proxyZone.fork({name: 'fork'});
66+
expect(forkeZone).not.toBe(proxyZone);
67+
expect(forkeZone.name).toBe('fork');
68+
var called = false;
69+
proxyZoneSpec.setDelegate({
70+
name: '.',
71+
onFork: (parentZoneDelegate, currentZone, targetZone, zoneSpec) => {
72+
expect(currentZone).toBe(proxyZone);
73+
expect(targetZone).toBe(proxyZone),
74+
expect(zoneSpec.name).toBe('fork2');
75+
called = true;
76+
}
77+
});
78+
proxyZone.fork({name: 'fork2'});
79+
expect(called).toBe(true);
80+
});
81+
82+
it('should intercept', () => {
83+
const fn = (a) => a;
84+
expect(proxyZone.wrap(fn, 'test')('works')).toEqual('works');
85+
proxyZoneSpec.setDelegate({
86+
name: '.',
87+
onIntercept: (parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
88+
delegate: Function, source: string): Function => {
89+
return () => '(works)';
90+
}
91+
});
92+
expect(proxyZone.wrap(fn, 'test')('works')).toEqual('(works)');
93+
});
94+
95+
it('should invoke', () => {
96+
const fn = () => 'works';
97+
expect(proxyZone.run(fn)).toEqual('works');
98+
proxyZoneSpec.setDelegate({
99+
name: '.',
100+
onInvoke: (parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
101+
delegate: Function, applyThis: any, applyArgs: any[], source: string) => {
102+
return `(${parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source)})`;
103+
}
104+
});
105+
expect(proxyZone.run(fn)).toEqual('(works)');
106+
});
107+
108+
it('should handleError', () => {
109+
const error = new Error("TestError");
110+
const fn = () => { throw error };
111+
expect(() => proxyZone.run(fn)).toThrow(error);
112+
proxyZoneSpec.setDelegate({
113+
name: '.',
114+
onHandleError: (parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
115+
error: any): boolean => {
116+
expect(error).toEqual(error);
117+
return false;
118+
}
119+
});
120+
expect(() => proxyZone.runGuarded(fn)).not.toThrow();
121+
});
122+
123+
it('should Task', () => {
124+
const fn = () => null;
125+
const task = proxyZone.scheduleMacroTask('test', fn, {}, () => null, () => null);
126+
expect(task.source).toEqual('test');
127+
proxyZone.cancelTask(task);
128+
});
129+
});
130+
});

0 commit comments

Comments
 (0)