##// END OF EJS Templates
tests
tests

File last commit:

r41:eae7e609c38a di-typescript
r41:eae7e609c38a di-typescript
Show More
Container.ts
279 lines | 9.1 KiB | video/mp2t | TypeScriptLexer
/ src / ts / di / Container.ts
cin
minor fixes, code cleanup...
r33 import { ActivationContext } from "./ActivationContext";
cin
ported IoC container to typescript...
r34 import { ValueDescriptor } from "./ValueDescriptor";
import { ActivationError } from "./ActivationError";
cin
tests
r41 import { isDescriptor, ActivationType, ServiceMap, isDependencyRegistration, isValueRegistration, ServiceRegistration, DependencyRegistration } from "./interfaces";
cin
ported IoC container to typescript...
r34 import { AggregateDescriptor } from "./AggregateDescriptor";
cin
tests
r41 import { isPrimitive, pmap } from "../safe";
cin
ported IoC container to typescript...
r34 import { ReferenceDescriptor } from "./ReferenceDescriptor";
cin
working on IoC container
r38 import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor";
import { ModuleResolverBase } from "./ModuleResolverBase";
import format = require("../text/format");
cin
tests
r41 import { throws } from "assert";
cin
minor fixes, code cleanup...
r33
export class Container {
cin
working on IoC container
r38 _services: ServiceMap;
cin
minor fixes, code cleanup...
r33
cin
working on IoC container
r38 _cache: object;
_cleanup: (() => void)[];
cin
minor fixes, code cleanup...
r33
cin
working on IoC container
r38 _root: Container;
cin
minor fixes, code cleanup...
r33
cin
working on IoC container
r38 _parent: Container;
cin
minor fixes, code cleanup...
r33
cin
working on IoC container
r38 _resolver: ModuleResolverBase;
cin
minor fixes, code cleanup...
r33
constructor(parent?: Container) {
this._parent = parent;
this._services = parent ? Object.create(parent._services) : {};
this._cache = {};
this._cleanup = [];
this._root = parent ? parent.getRootContainer() : this;
this._services.container = new ValueDescriptor(this);
}
getRootContainer() {
return this._root;
}
getParent() {
return this._parent;
}
cin
tests
r41 resolve(name: string, def?) {
cin
working on IoC container
r38 const d = this._services[name];
cin
tests
r41 if (d === undefined) {
cin
minor fixes, code cleanup...
r33 if (arguments.length > 1)
return def;
else
throw new Error("Service '" + name + "' isn't found");
cin
tests
r41 }
cin
minor fixes, code cleanup...
r33
cin
working on IoC container
r38 const context = new ActivationContext(this, this._services);
cin
minor fixes, code cleanup...
r33 try {
cin
tests
r41 return context.activate(d, name);
cin
minor fixes, code cleanup...
r33 } catch (error) {
throw new ActivationError(name, context.getStack(), error);
}
}
cin
tests
r41 getService(name: string, def?) {
return this.resolve.apply(this, arguments);
}
cin
minor fixes, code cleanup...
r33 register(nameOrCollection, service?) {
cin
working on IoC container
r38 if (arguments.length === 1) {
const data = nameOrCollection;
for (const name in data)
cin
minor fixes, code cleanup...
r33 this.register(name, data[name]);
} else {
cin
tests
r41 if (!isDescriptor(service))
throw new Error("The service parameter must be a descriptor");
cin
minor fixes, code cleanup...
r33 this._services[nameOrCollection] = service;
}
return this;
}
onDispose(callback) {
if (!(callback instanceof Function))
throw new Error("The callback must be a function");
this._cleanup.push(callback);
}
dispose() {
if (this._cleanup) {
cin
working on IoC container
r38 for (const f of this._cleanup)
f();
cin
minor fixes, code cleanup...
r33 this._cleanup = null;
}
}
/**
* @param{String|Object} config
* The configuration of the contaier. Can be either a string or an object,
* if the configuration is an object it's treated as a collection of
* services which will be registed in the contaier.
cin
working on IoC container
r38 *
cin
minor fixes, code cleanup...
r33 * @param{Function} opts.contextRequire
* The function which will be used to load a configuration or types for services.
cin
working on IoC container
r38 *
cin
minor fixes, code cleanup...
r33 */
cin
working on IoC container
r38 async configure(config: string | object, opts?: object) {
cin
minor fixes, code cleanup...
r33 if (typeof (config) === "string") {
cin
working on IoC container
r38 const resolver = await this._resolver.createResolver(config, opts);
const data = await this._resolver.loadModule(config);
return this._configure(data, { resolver });
cin
minor fixes, code cleanup...
r33 } else {
cin
working on IoC container
r38 return this._configure(config);
cin
minor fixes, code cleanup...
r33 }
}
createChildContainer() {
return new Container(this);
}
has(id) {
return id in this._cache;
}
get(id) {
return this._cache[id];
}
store(id, value) {
return (this._cache[id] = value);
}
cin
working on IoC container
r38 async _configure(data: object, opts?: { resolver: ModuleResolverBase }) {
const resolver = (opts && opts.resolver) || this._resolver;
cin
tests
r41 const services = await this._parseRegistrations(data, resolver);
cin
minor fixes, code cleanup...
r33
cin
working on IoC container
r38 this.register(services);
cin
minor fixes, code cleanup...
r33 }
cin
working on IoC container
r38 async _parse(registration: any, resolver: ModuleResolverBase) {
if (isPrimitive(registration) || isDescriptor(registration))
return registration;
if (isDependencyRegistration(registration)) {
cin
tests
r41 return this._paseReference(registration, resolver);
cin
working on IoC container
r38 } else if (isValueRegistration(registration)) {
return !registration.parse ?
new ValueDescriptor(registration.$value) :
new AggregateDescriptor(this._parse(registration.$value, resolver));
} else if (registration.$type || registration.$factory) {
return this._parseService(registration, resolver);
} else if (registration instanceof Array) {
return this._parseArray(registration, resolver);
cin
minor fixes, code cleanup...
r33 }
cin
working on IoC container
r38 return this._parseObject(registration, resolver);
cin
minor fixes, code cleanup...
r33 }
cin
tests
r41 async _paseReference(registration: DependencyRegistration, resolver: ModuleResolverBase) {
return new ReferenceDescriptor({
name: registration.$dependency,
lazy: registration.lazy,
optional: registration.optional,
default: registration.default,
services: registration.services && await this._parseRegistrations(registration.services, resolver)
});
}
cin
working on IoC container
r38 async _parseService(data: ServiceRegistration, resolver: ModuleResolverBase) {
const opts: ServiceDescriptorParams = {
owner: this
};
cin
minor fixes, code cleanup...
r33
cin
working on IoC container
r38 if (data.$type) {
if (data.$type instanceof Function)
opts.type = data.$type;
else if (typeof data.$type === "string")
opts.type = await resolver.resolve(data.$type);
else
throw new Error(format("Unsupported type specification: {0:json}", data.$type));
} else {
if (data.$factory instanceof Function)
opts.factory = data.$factory;
else if (typeof data.$factory === "string")
opts.factory = await resolver.resolve(data.$factory);
else
throw new Error(format("Unsupported factory specification: {0:json}", data.$factory));
}
cin
minor fixes, code cleanup...
r33
if (data.services)
cin
tests
r41 opts.services = await this._parseRegistrations(data.services, resolver);
cin
working on IoC container
r38
cin
tests
r41 if (data.inject) {
if (data.inject instanceof Array)
opts.inject = await Promise.all(data.inject.map(x => this._parseObject(x, resolver)));
else
opts.inject = [await this._parseObject(data.inject, resolver)];
}
cin
working on IoC container
r38
cin
minor fixes, code cleanup...
r33 if (data.params)
cin
working on IoC container
r38 opts.params = this._parse(data.params, resolver);
cin
minor fixes, code cleanup...
r33
if (data.activation) {
if (typeof (data.activation) === "string") {
switch (data.activation.toLowerCase()) {
case "singleton":
cin
working on IoC container
r38 opts.activation = ActivationType.Singleton;
cin
minor fixes, code cleanup...
r33 break;
case "container":
cin
working on IoC container
r38 opts.activation = ActivationType.Container;
cin
minor fixes, code cleanup...
r33 break;
case "hierarchy":
cin
working on IoC container
r38 opts.activation = ActivationType.Hierarchy;
cin
minor fixes, code cleanup...
r33 break;
case "context":
cin
working on IoC container
r38 opts.activation = ActivationType.Context;
cin
minor fixes, code cleanup...
r33 break;
case "call":
cin
working on IoC container
r38 opts.activation = ActivationType.Call;
cin
minor fixes, code cleanup...
r33 break;
default:
throw new Error("Unknown activation type: " +
data.activation);
}
} else {
opts.activation = Number(data.activation);
}
}
if (data.cleanup)
opts.cleanup = data.cleanup;
return new ServiceDescriptor(opts);
}
cin
tests
r41 async _parseObject(data: object, resolver: ModuleResolverBase) {
cin
minor fixes, code cleanup...
r33 if (data.constructor &&
data.constructor.prototype !== Object.prototype)
return new ValueDescriptor(data);
cin
working on IoC container
r38 const o = {};
cin
minor fixes, code cleanup...
r33
cin
working on IoC container
r38 for (const p in data)
cin
tests
r41 o[p] = await this._parse(data[p], resolver);
// TODO: handle inline descriptors properly
// const ex = {
// activate(ctx) {
// const value = ctx.activate(this.prop, "prop");
// // some code
// },
// // will be turned to ReferenceDescriptor
// prop: { $dependency: "depName" }
// };
cin
minor fixes, code cleanup...
r33
return o;
}
cin
tests
r41 async _parseArray(data: Array<any>, resolver: ModuleResolverBase) {
cin
minor fixes, code cleanup...
r33 if (data.constructor &&
data.constructor.prototype !== Array.prototype)
return new ValueDescriptor(data);
cin
tests
r41 return pmap(data, x => this._parse(x, resolver));
}
async _parseRegistrations(data: object, resolver: ModuleResolverBase) {
if (data.constructor &&
data.constructor.prototype !== Object.prototype)
throw new Error("Registrations must be a simple object");
const o: ServiceMap = {};
for (const p of Object.keys(data)) {
const v = await this._parse(data[p], resolver);
o[p] = isDescriptor(v) ? v : new AggregateDescriptor(v);
}
return o;
cin
minor fixes, code cleanup...
r33 }
cin
working on IoC container
r38 }