This repository has been archived by the owner on Feb 26, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 407
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ProxySpec): create a ProxySpec which can proxy to other ZoneSpecs.
This allows changing of the zone behavior ofter it has been created.
- Loading branch information
Showing
5 changed files
with
270 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
(function () { | ||
class ProxyZoneSpec implements ZoneSpec { | ||
name: string = 'ProxyZone'; | ||
|
||
private _delegateSpec: ZoneSpec; | ||
|
||
properties: {[k: string]: any} = {'ProxyZoneSpec': this}; | ||
propertyKeys: string[] = null; | ||
|
||
static get(): ProxyZoneSpec { | ||
return Zone.current.get('ProxyZoneSpec'); | ||
} | ||
|
||
static isLoaded(): boolean { | ||
return ProxyZoneSpec.get() instanceof ProxyZoneSpec; | ||
} | ||
|
||
static assertPresent(): ProxyZoneSpec { | ||
if (!this.isLoaded()) { | ||
throw new Error(`Expected to be running in 'ProxyZone', but it was not found.`); | ||
} | ||
return ProxyZoneSpec.get(); | ||
} | ||
|
||
constructor(private defaultSpecDelegate: ZoneSpec = null) { | ||
this.setDelegate(defaultSpecDelegate); | ||
} | ||
|
||
|
||
setDelegate(delegateSpec: ZoneSpec) { | ||
this._delegateSpec = delegateSpec; | ||
this.propertyKeys && this.propertyKeys.forEach((key) => delete this.properties[key]); | ||
this.propertyKeys = null; | ||
if (delegateSpec && delegateSpec.properties) { | ||
this.propertyKeys = Object.keys(delegateSpec.properties); | ||
this.propertyKeys.forEach((k) => this.properties[k] = delegateSpec.properties[k]); | ||
} | ||
} | ||
|
||
getDelegate() { | ||
return this._delegateSpec; | ||
} | ||
|
||
|
||
resetDelegate() { | ||
this.setDelegate(this.defaultSpecDelegate); | ||
} | ||
|
||
|
||
onFork(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, | ||
zoneSpec: ZoneSpec): Zone { | ||
if (this._delegateSpec && this._delegateSpec.onFork) { | ||
return this._delegateSpec.onFork(parentZoneDelegate, currentZone, targetZone, zoneSpec); | ||
} else { | ||
return parentZoneDelegate.fork(targetZone, zoneSpec); | ||
} | ||
} | ||
|
||
|
||
onIntercept(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, | ||
delegate: Function, source: string): Function { | ||
if (this._delegateSpec && this._delegateSpec.onIntercept) { | ||
return this._delegateSpec.onIntercept(parentZoneDelegate, currentZone, targetZone, delegate, source); | ||
} else { | ||
return parentZoneDelegate.intercept(targetZone, delegate, source); | ||
} | ||
} | ||
|
||
|
||
onInvoke(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, | ||
delegate: Function, applyThis: any, applyArgs: any[], source: string): any { | ||
if (this._delegateSpec && this._delegateSpec.onInvoke) { | ||
return this._delegateSpec.onInvoke(parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source); | ||
} else { | ||
return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); | ||
} | ||
} | ||
|
||
onHandleError(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, | ||
error: any): boolean { | ||
if (this._delegateSpec && this._delegateSpec.onHandleError) { | ||
return this._delegateSpec.onHandleError(parentZoneDelegate, currentZone, targetZone, error); | ||
} else { | ||
return parentZoneDelegate.handleError(targetZone, error); | ||
} | ||
} | ||
|
||
onScheduleTask(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, | ||
task: Task): Task { | ||
if (this._delegateSpec && this._delegateSpec.onScheduleTask) { | ||
return this._delegateSpec.onScheduleTask(parentZoneDelegate, currentZone, targetZone, task); | ||
} else { | ||
return parentZoneDelegate.scheduleTask(targetZone, task); | ||
} | ||
} | ||
|
||
onInvokeTask(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, | ||
task: Task, applyThis: any, applyArgs: any): any { | ||
if (this._delegateSpec && this._delegateSpec.onFork) { | ||
return this._delegateSpec.onInvokeTask(parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs); | ||
} else { | ||
return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); | ||
} | ||
} | ||
|
||
onCancelTask(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, | ||
task: Task): any { | ||
if (this._delegateSpec && this._delegateSpec.onCancelTask) { | ||
return this._delegateSpec.onCancelTask(parentZoneDelegate, currentZone, targetZone, task); | ||
} else { | ||
return parentZoneDelegate.cancelTask(targetZone, task); | ||
} | ||
} | ||
|
||
onHasTask(delegate: ZoneDelegate, current: Zone, target: Zone, | ||
hasTaskState: HasTaskState): void { | ||
if (this._delegateSpec && this._delegateSpec.onHasTask) { | ||
this._delegateSpec.onHasTask(delegate, current, target, hasTaskState); | ||
} else { | ||
delegate.hasTask(target, hasTaskState); | ||
} | ||
} | ||
} | ||
|
||
// Export the class so that new instances can be created with proper | ||
// constructor params. | ||
Zone['ProxyZoneSpec'] = ProxyZoneSpec; | ||
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import '../../lib/zone-spec/proxy'; | ||
|
||
describe('ProxySpec', () => { | ||
let ProxyZoneSpec: any; | ||
let delegate: ZoneSpec; | ||
let proxyZoneSpec: any; | ||
let proxyZone: Zone; | ||
|
||
beforeEach(() => { | ||
ProxyZoneSpec = Zone['ProxyZoneSpec']; | ||
expect(typeof ProxyZoneSpec).toBe('function'); | ||
delegate = {name: 'delegate'}; | ||
proxyZoneSpec = new ProxyZoneSpec(delegate); | ||
proxyZone = Zone.current.fork(proxyZoneSpec); | ||
}); | ||
|
||
describe('properties', () => { | ||
it('should expose ProxyZone in the properties', () => { | ||
expect(proxyZone.get('ProxyZoneSpec')).toBe(proxyZoneSpec); | ||
}); | ||
|
||
it('should assert that it is in or out of ProxyZone', () => { | ||
expect(() => ProxyZoneSpec.assertPresent()).toThrow(); | ||
expect(ProxyZoneSpec.isLoaded()).toBe(false); | ||
expect(ProxyZoneSpec.get()).toBe(undefined); | ||
proxyZone.run(() => { | ||
expect(ProxyZoneSpec.isLoaded()).toBe(true); | ||
expect(() => ProxyZoneSpec.assertPresent()).not.toThrow(); | ||
expect(ProxyZoneSpec.get()).toBe(proxyZoneSpec); | ||
}); | ||
}); | ||
|
||
it('should reset properties', () => { | ||
expect(proxyZone.get('myTestKey')).toBe(undefined); | ||
proxyZoneSpec.setDelegate({name: 'd1', properties: {'myTestKey': 'myTestValue'}}); | ||
expect(proxyZone.get('myTestKey')).toBe('myTestValue'); | ||
proxyZoneSpec.resetDelegate(); | ||
expect(proxyZone.get('myTestKey')).toBe(undefined); | ||
}); | ||
}); | ||
|
||
describe('delegate', () => { | ||
it('should set/reset delegate', () => { | ||
const defaultDelegate: ZoneSpec = {name: 'defaultDelegate'}; | ||
const otherDelegate: ZoneSpec = {name: 'otherDelegate'}; | ||
const proxyZoneSpec = new ProxyZoneSpec(defaultDelegate); | ||
const proxyZone = Zone.current.fork(proxyZoneSpec); | ||
|
||
expect(proxyZoneSpec.getDelegate()).toEqual(defaultDelegate); | ||
|
||
proxyZoneSpec.setDelegate(otherDelegate); | ||
expect(proxyZoneSpec.getDelegate()).toEqual(otherDelegate); | ||
proxyZoneSpec.resetDelegate(); | ||
expect(proxyZoneSpec.getDelegate()).toEqual(defaultDelegate); | ||
}); | ||
}); | ||
|
||
describe('forwarding', () => { | ||
beforeEach(() => { | ||
proxyZoneSpec = new ProxyZoneSpec(); | ||
proxyZone = Zone.current.fork(proxyZoneSpec); | ||
}); | ||
|
||
it('should fork', () => { | ||
const forkeZone = proxyZone.fork({name: 'fork'}); | ||
expect(forkeZone).not.toBe(proxyZone); | ||
expect(forkeZone.name).toBe('fork'); | ||
var called = false; | ||
proxyZoneSpec.setDelegate({ | ||
name: '.', | ||
onFork: (parentZoneDelegate, currentZone, targetZone, zoneSpec) => { | ||
expect(currentZone).toBe(proxyZone); | ||
expect(targetZone).toBe(proxyZone), | ||
expect(zoneSpec.name).toBe('fork2'); | ||
called = true; | ||
} | ||
}); | ||
proxyZone.fork({name: 'fork2'}); | ||
expect(called).toBe(true); | ||
}); | ||
|
||
it('should intercept', () => { | ||
const fn = (a) => a; | ||
expect(proxyZone.wrap(fn, 'test')('works')).toEqual('works'); | ||
proxyZoneSpec.setDelegate({ | ||
name: '.', | ||
onIntercept: (parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, | ||
delegate: Function, source: string): Function => { | ||
return () => '(works)'; | ||
} | ||
}); | ||
expect(proxyZone.wrap(fn, 'test')('works')).toEqual('(works)'); | ||
}); | ||
|
||
it('should invoke', () => { | ||
const fn = () => 'works'; | ||
expect(proxyZone.run(fn)).toEqual('works'); | ||
proxyZoneSpec.setDelegate({ | ||
name: '.', | ||
onInvoke: (parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, | ||
delegate: Function, applyThis: any, applyArgs: any[], source: string) => { | ||
return `(${parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source)})`; | ||
} | ||
}); | ||
expect(proxyZone.run(fn)).toEqual('(works)'); | ||
}); | ||
|
||
it('should handleError', () => { | ||
const error = new Error("TestError"); | ||
const fn = () => { throw error }; | ||
expect(() => proxyZone.run(fn)).toThrow(error); | ||
proxyZoneSpec.setDelegate({ | ||
name: '.', | ||
onHandleError: (parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, | ||
error: any): boolean => { | ||
expect(error).toEqual(error); | ||
return false; | ||
} | ||
}); | ||
expect(() => proxyZone.runGuarded(fn)).not.toThrow(); | ||
}); | ||
|
||
it('should Task', () => { | ||
const fn = () => null; | ||
const task = proxyZone.scheduleMacroTask('test', fn, {}, () => null, () => null); | ||
expect(task.source).toEqual('test'); | ||
proxyZone.cancelTask(task); | ||
}); | ||
}); | ||
}); |