Mercurial > pub > ImplabJs
view src/implab/di/ServiceDescriptor.js @ 0:fc2517695ee1
Initial commit, draft import of existing work
author | cin |
---|---|
date | Thu, 01 Jun 2017 13:20:03 +0300 |
parents | |
children | 93fb6c09f2e1 |
line wrap: on
line source
define( [ "dojo/_base/declare", "../safe", "dojo/_base/lang", "dojo/_base/array", "./Descriptor", "./ValueDescriptor" ], function (declare, safe, lang, array, Descriptor, Value) { var SINGLETON_ACTIVATION = 1, CONTAINER_ACTIVATION = 2, CONTEXT_ACTIVATION = 3, CALL_ACTIVATION = 4, HIERARCHY_ACTIVATION = 5; var injectMethod = function (target, method, context, args) { var m = target[method]; if (!m) throw new Error("Method '" + method + "' not found"); if (args instanceof Array) m.apply(target, context.parse(args, "." + method)); else m.call(target, context.parse(args, "." + method)); }; var makeClenupCallback = function (target, method) { if (typeof (method) === "string") { return function () { target[method](); }; } else { return function () { method(target); }; } }; var cacheId = 0; var cls = declare( Descriptor, { _instance: null, _hasInstance: false, _activationType: CALL_ACTIVATION, _services: null, _type: null, _typeMap: null, _factory: null, _params: undefined, _inject: null, _cleanup: null, _cacheId: null, _owner: null, constructor: function (opts) { safe.argumentNotNull(opts, "opts"); safe.argumentNotNull(opts.owner, "opts.owner"); this._owner = opts.owner; if (!(opts.type || opts.factory)) throw new Error( "Either a type or a factory must be specified"); if (typeof (opts.type) === "string" && !opts.typeMap) throw new Error( "The typeMap is required when the type is specified by its name"); if (opts.activation) this._activationType = opts.activation; if (opts.type) this._type = opts.type; if (opts.params) this._params = opts.params; if (opts.inject) this._inject = opts.inject instanceof Array ? opts.inject : [opts.inject]; if (opts.services) this._services = opts.services; if (opts.factory) this._factory = opts.factory; if (opts.typeMap) this._typeMap = opts.typeMap; 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 = opts.cleanup; } this._cacheId = ++cacheId; }, activate: function (context, name) { // if we have a local service records, register them first var instance; switch (this._activationType) { case 1: // SINGLETON // if the value is cached return it if (this._hasInstance) return this._instance; var tof = this._type || this._factory; // create the persistent cache identifier for the type if (safe.isPrimitive(tof)) this._cacheId = this._type; else this._cacheId = safe.oid(tof); // singletons are bound to the root container var container = context.container.getRootContainer(); if (container.has(this._cacheId)) { instance = container.get(this._cacheId); } else { instance = this._create(context, name); container.store(this._cacheId, instance); if (this._cleanup) container.onDispose( makeClenupCallback(instance, this._cleanup)); } this._hasInstance = true; return (this._instance = instance); case 2: // CONTAINER //return a cached value if (this._hasInstance) return this._instance; // create an instance instance = this._create(context, name); // the instance is bound to the container if (this._cleanup) this._owner.onDispose( makeClenupCallback(instance, this._cleanup)); // cache and return the instance this._hasInstance = true; return (this._instance = instance); case 3: // CONTEXT //return a cached value if one exists if (context.has(this._cacheId)) return context.get(this._cacheId); // context context activated instances are controlled by callers return context.store(this._cacheId, this._create( context, name)); case 4: // CALL // per-call created instances are controlled by callers return this._create(context, name); case 5: // HIERARCHY // hierarchy activated instances are behave much like container activated // except they are created and bound to the child container // return a cached value if (context.container.has(this._cacheId)) return context.container.get(this._cacheId); instance = this._create(context, name); if (this._cleanup) context.container.onDispose(makeClenupCallback( instance, this._cleanup)); return context.container.store(this._cacheId, instance); default: throw "Invalid activation type: " + this._activationType; } }, isInstanceCreated: function () { return this._hasInstance; }, getInstance: function () { return this._instance; }, _create: function (context, name) { context.enter(name, this, Boolean(this._services)); if (this._activationType != CALL_ACTIVATION && context.visit(this._cacheId) > 0) throw new Error("Recursion detected"); if (this._services) { for (var p in this._services) { var sv = this._services[p]; context.register(p, sv instanceof Descriptor ? sv : new Value(sv, false)); } } var instance; if (!this._factory) { var ctor, type = this._type; if (typeof (type) === "string") { ctor = this._typeMap[type]; if (!ctor) throw new Error("Failed to resolve the type '" + type + "'"); } else { ctor = type; } if (this._params === undefined) { this._factory = function () { return new ctor(); }; } else if (this._params instanceof Array) { this._factory = function () { var inst = lang.delegate(ctor.prototype); var ret = ctor.apply(inst, arguments); return typeof (ret) === "object" ? ret : inst; }; } else { this._factory = function (param) { return new ctor(param); }; } } if (this._params === undefined) { instance = this._factory(); } else if (this._params instanceof Array) { instance = this._factory.apply(this, context.parse( this._params, ".params")); } else { instance = this._factory(context.parse( this._params, ".params")); } if (this._inject) { array.forEach(this._inject, function (spec) { for (var m in spec) injectMethod(instance, m, context, spec[m]); }); } context.leave(); return instance; }, // @constructor {singleton} foo/bar/Baz // @factory {singleton} toString: function () { var parts = []; parts.push(this._type ? "@constructor" : "@factory"); parts.push(activationNames[this._activationType]); if (typeof (this._type) === "string") parts.push(this._type); return parts.join(" "); } }); cls.SINGLETON = SINGLETON_ACTIVATION; cls.CONTAINER = CONTAINER_ACTIVATION; cls.CONTEXT = CONTEXT_ACTIVATION; cls.CALL = CALL_ACTIVATION; cls.HIERARCHY = HIERARCHY_ACTIVATION; var activationNames = [ "", "{singleton}", "{container}", "{context}", "{call}", "{hierarchy}" ]; return cls; });