Mercurial > pub > ImplabJs
view src/implab/di/Container.js @ 14:5fd2a35d65c0
Слияние
author | cin |
---|---|
date | Fri, 08 Sep 2017 13:45:10 +0300 |
parents | fcc63f34d0a2 |
children |
line wrap: on
line source
define([ "../declare", "../safe", "../Uuid", "../Deferred", "./ActivationContext", "./Descriptor", "./ValueDescriptor", "./ReferenceDescriptor", "./ServiceDescriptor", "./ActivationError" ], function ( declare, safe, Uuid, Deferred, ActivationContext, Descriptor, Value, Reference, Service, ActivationError) { var Container = declare(null, { _services: null, _cache: null, _cleanup: null, _root: null, _parent: null, constructor: function (parent) { this._parent = parent; this._services = parent ? Object.create(parent._services) : {}; this._cache = {}; this._cleanup = []; this._root = parent ? parent.getRootContainer() : this; this._services.container = new Value(this, true); }, getRootContainer: function () { return this._root; }, getParent: function () { return this._parent; }, /** * */ getService: function (name, def) { var d = this._services[name]; if (!d) if (arguments.length > 1) return def; else throw new Error("Service '" + name + "' isn't found"); if (d.isInstanceCreated()) return d.getInstance(); var context = new ActivationContext(this, this._services); try { return d.activate(context, name); } catch (error) { throw new ActivationError(name, context.getStack(), error); } }, register: function (name, service) { if (arguments.length == 1) { var data = name; for (name in data) this.register(name, data[name]); } else { if (!(service instanceof Descriptor)) service = new Value(service, true); this._services[name] = service; } return this; }, onDispose: function (callback) { if (!(callback instanceof Function)) throw new Error("The callback must be a function"); this._cleanup.push(callback); }, dispose: function () { if (this._cleanup) { for (var i = 0; i < this._cleanup.length; i++) this._cleanup[i].call(null); 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. * * @param{Function} opts.contextRequire * The function which will be used to load a configuration or types for services. * */ configure: function (config, opts) { var p, me = this, contextRequire = (opts && opts.contextRequire); if (typeof (config) === "string") { p = new Deferred(); if (!contextRequire) { var shim = [config, new Uuid()].join(config.indexOf("/") != -1 ? "-" : "/"); define(shim, ["require", config], function (ctx, data) { p.resolve([data, { contextRequire: ctx }]); }); require([shim]); } else { // TODO how to get correct contextRequire for the relative config module? contextRequire([config], function (data) { p.resolve([data, { contextRequire: contextRequire }]); }); } return p.then(function (args) { return me._configure.apply(me, args); }); } else { return me._configure(config, opts); } }, createChildContainer: function () { return new Container(this); }, has: function (id) { return id in this._cache; }, get: function (id) { return this._cache[id]; }, store: function (id, value) { return (this._cache[id] = value); }, _configure: function (data, opts) { var typemap = {}, d = new Deferred(), me = this, p, contextRequire = (opts && opts.contextRequire) || require; var services = {}; for (p in data) { var service = me._parse(data[p], typemap); if (!(service instanceof Descriptor)) service = new Value(service, false); services[p] = service; } me.register(services); var names = []; for (p in typemap) names.push(p); if (names.length) { contextRequire(names, function () { for (var i = 0; i < names.length; i++) typemap[names[i]] = arguments[i]; d.resolve(me); }); } else { d.resolve(me); } return d.promise; }, _parse: function (data, typemap) { if (safe.isPrimitive(data) || data instanceof Descriptor) return data; if (data.$dependency) return new Reference( data.$dependency, data.lazy, data.optional, data["default"], data.services && this._parseObject(data.services, typemap)); if (data.$value) { var raw = !data.parse; return new Value(raw ? data.$value : this._parse( data.$value, typemap), raw); } if (data.$type || data.$factory) return this._parseService(data, typemap); if (data instanceof Array) return this._parseArray(data, typemap); return this._parseObject(data, typemap); }, _parseService: function (data, typemap) { var me = this, opts = { owner: this }; if (data.$type) { opts.type = data.$type; if (typeof (data.$type) === "string") { typemap[data.$type] = null; opts.typeMap = typemap; } } if (data.$factory) opts.factory = data.$factory; if (data.services) opts.services = me._parseObject(data.services, typemap); if (data.inject) opts.inject = data.inject instanceof Array ? data.inject.map(function (x) { return me._parseObject(x, typemap); }) : me._parseObject(data.inject, typemap); if (data.params) opts.params = me._parse(data.params, typemap); if (data.activation) { if (typeof (data.activation) === "string") { switch (data.activation.toLowerCase()) { case "singleton": opts.activation = Service.SINGLETON; break; case "container": opts.activation = Service.CONTAINER; break; case "hierarchy": opts.activation = Service.HIERARCHY; break; case "context": opts.activation = Service.CONTEXT; break; case "call": opts.activation = Service.CALL; 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 Service(opts); }, _parseObject: function (data, typemap) { if (data.constructor && data.constructor.prototype !== Object.prototype) return new Value(data, true); var o = {}; for (var p in data) o[p] = this._parse(data[p], typemap); return o; }, _parseArray: function (data, typemap) { if (data.constructor && data.constructor.prototype !== Array.prototype) return new Value(data, true); var me = this; return data.map(function (x) { return me._parse(x, typemap); }); } }); return Container; });