##// END OF EJS Templates
added provided and configure methods to the fluent container configuration, added applyConfig method to the container
added provided and configure methods to the fluent container configuration, added applyConfig method to the container

File last commit:

r135:03e32ec7c20b ioc ts support
r142:be7edf08a115 v1.4.0-rc3 default
Show More
ServiceDescriptor.ts
154 lines | 4.2 KiB | video/mp2t | TypeScriptLexer
/ src / main / ts / di / ServiceDescriptor.ts
import { ActivationContext } from "./ActivationContext";
import { Descriptor, ServiceMap, PartialServiceMap, ILifetime } from "./interfaces";
import { isPrimitive, keys, isNull } from "../safe";
import { TraceSource } from "../log/TraceSource";
import { isDescriptor } from "./traits";
import { LifetimeManager } from "./LifetimeManager";
import { MatchingMemberKeys } from "../interfaces";
const trace = TraceSource.get("@implab/core/di/ActivationContext");
function injectMethod<T, M extends keyof T, S extends object, A>(target: T, method: M, context: ActivationContext<S>, args: A) {
const m = target[method];
if (!m || typeof m !== "function")
throw new Error("Method '" + method + "' not found");
if (args instanceof Array)
return m.apply(target, _parse(args, context, "." + method));
else
return m.call(target, _parse(args, context, "." + method));
}
function makeCleanupCallback<T>(method: Cleaner<T>) {
if (typeof (method) === "function") {
return (target: T) => {
method(target);
};
} else {
return (target: T) => {
const m = target[method] as any;
m.apply(target);
};
}
}
function _parse(value: any, context: ActivationContext<any>, path: string): any {
if (isPrimitive(value))
return value as any;
trace.debug("parse {0}", path);
if (isDescriptor(value))
return context.activate(value, path);
if (value instanceof Array)
return value.map((x, i) => _parse(x, context, `${path}[${i}]`)) as any;
const t: any = {};
keys(value).forEach(p => t[p] = _parse(value[p], context, `${path}.${p}`));
return t;
}
export type Cleaner<T> = ((x: T) => void) | MatchingMemberKeys<() => void, T>;
export type InjectionSpec<T> = {
[m in keyof T]?: any;
};
export interface ServiceDescriptorParams<S extends object, T, P extends any[]> {
lifetime?: ILifetime;
params?: P;
inject?: InjectionSpec<T>[];
services?: PartialServiceMap<S>;
cleanup?: Cleaner<T>;
}
export class ServiceDescriptor<S extends object, T, P extends any[]> implements Descriptor<S, T> {
_services: ServiceMap<S>;
_params: P | undefined;
_inject: InjectionSpec<T>[];
_cleanup: ((item: T) => void) | undefined;
_lifetime = LifetimeManager.empty();
_objectLifetime: ILifetime | undefined;
constructor(opts: ServiceDescriptorParams<S, T, P>) {
if (opts.lifetime)
this._lifetime = opts.lifetime;
if (!isNull(opts.params))
this._params = opts.params;
this._inject = opts.inject || [];
this._services = (opts.services || {}) as ServiceMap<S>;
if (opts.cleanup) {
if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
throw new Error(
"The cleanup parameter must be either a function or a function name");
this._cleanup = makeCleanupCallback(opts.cleanup);
}
}
activate(context: ActivationContext<S>) {
const lifetime = this._lifetime;
if (lifetime.has()) {
return lifetime.get();
} else {
lifetime.initialize(context);
const instance = this._create(context);
lifetime.store(instance, this._cleanup);
return instance;
}
}
_factory(...params: any[]): T {
throw Error("Not implemented");
}
_create(context: ActivationContext<S>) {
trace.debug(`constructing ${context._name}`);
if (this._services) {
keys(this._services).forEach(p => context.register(p, this._services[p]));
}
let instance: T;
if (this._params === undefined) {
instance = this._factory();
} else if (this._params instanceof Array) {
instance = this._factory.apply(this, _parse(this._params, context, "args"));
} else {
instance = this._factory(_parse(this._params, context, "args"));
}
if (this._inject) {
this._inject.forEach(spec => {
for (const m in spec)
injectMethod(instance, m, context, spec[m]);
});
}
return instance;
}
clone() {
return Object.create(this);
}
}