Mercurial > pub > ImplabJs
comparison src/implab/di/Container.js @ 0:fc2517695ee1
Initial commit, draft import of existing work
author | cin |
---|---|
date | Thu, 01 Jun 2017 13:20:03 +0300 |
parents | |
children | 93fb6c09f2e1 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:fc2517695ee1 |
---|---|
1 define([ | |
2 "../declare", | |
3 "../safe", | |
4 "../UUID", | |
5 "dojo/Deferred", | |
6 "./ActivationContext", | |
7 "./Descriptor", | |
8 "./ValueDescriptor", | |
9 "./ReferenceDescriptor", | |
10 "./ServiceDescriptor", | |
11 "./ActivationError" | |
12 ], function ( | |
13 declare, | |
14 array, | |
15 safe, | |
16 UUID, | |
17 Deferred, | |
18 ActivationContext, | |
19 Descriptor, | |
20 Value, | |
21 Reference, | |
22 Service, | |
23 ActivationError) { | |
24 var Container = declare(null, { | |
25 _services: null, | |
26 _cache: null, | |
27 _cleanup: null, | |
28 _root: null, | |
29 _parent: null, | |
30 | |
31 constructor: function (parent) { | |
32 this._parent = parent; | |
33 this._services = parent ? safe.create(parent._services) : {}; | |
34 this._cache = {}; | |
35 this._cleanup = []; | |
36 this._root = parent ? parent.getRootContainer() : this; | |
37 this._services.container = new Value(this, true); | |
38 }, | |
39 | |
40 getRootContainer: function () { | |
41 return this._root; | |
42 }, | |
43 | |
44 getParent: function () { | |
45 return this._parent; | |
46 }, | |
47 | |
48 getService: function (name, def) { | |
49 var d = this._services[name]; | |
50 if (!d) | |
51 if (arguments.length > 1) | |
52 return def; | |
53 else | |
54 throw new Error("Service '" + name + "' isn't found"); | |
55 if (d.isInstanceCreated()) | |
56 return d.getInstance(); | |
57 | |
58 var context = new ActivationContext(this, this._services); | |
59 | |
60 try { | |
61 return d.activate(context, name); | |
62 } catch (error) { | |
63 throw new ActivationError(name, context.getStack(), error); | |
64 } | |
65 }, | |
66 | |
67 register: function (name, service) { | |
68 if (arguments.length == 1) { | |
69 var data = name; | |
70 for (name in data) | |
71 this.register(name, data[name]); | |
72 } else { | |
73 if (!(service instanceof Descriptor)) | |
74 service = new Value(service, true); | |
75 this._services[name] = service; | |
76 } | |
77 return this; | |
78 }, | |
79 | |
80 onDispose: function (callback) { | |
81 if (!(callback instanceof Function)) | |
82 throw new Error("The callback must be a function"); | |
83 this._cleanup.push(callback); | |
84 }, | |
85 | |
86 dispose: function () { | |
87 if (this._cleanup) { | |
88 for (var i = 0; i < this._cleanup.length; i++) | |
89 this._cleanup[i].call(null); | |
90 this._cleanup = null; | |
91 } | |
92 }, | |
93 | |
94 /** | |
95 * @param{String|Object} config | |
96 * The configuration of the contaier. Can be either a string or an object, | |
97 * if the configuration is an object it's treated as a collection of | |
98 * services which will be registed in the contaier. | |
99 * | |
100 * @param{Function} opts.contextRequire | |
101 * The function which will be used to load a configuration or types for services. | |
102 * | |
103 */ | |
104 configure: function (config, opts) { | |
105 var p, me = this, | |
106 contextRequire = (opts && opts.contextRequire); | |
107 | |
108 if (typeof (config) === "string") { | |
109 p = new Deferred(); | |
110 if (!contextRequire) { | |
111 var shim = config + "-" + UUID(); | |
112 define(shim, ["require", config], function (ctx, data) { | |
113 p.resolve([data, { | |
114 contextRequire: ctx | |
115 }]); | |
116 }); | |
117 require([shim]); | |
118 } else { | |
119 // TODO how to get correct contextRequire for the relative config module? | |
120 contextRequire([config], function (data) { | |
121 p.resolve([data, { | |
122 contextRequire: contextRequire | |
123 }]); | |
124 }); | |
125 } | |
126 | |
127 return p.then(function (args) { | |
128 return me._configure.apply(me, args); | |
129 }); | |
130 } else { | |
131 return me._configure(config, opts); | |
132 } | |
133 }, | |
134 | |
135 createChildContainer: function () { | |
136 return new Container(this); | |
137 }, | |
138 | |
139 has: function (id) { | |
140 return id in this._cache; | |
141 }, | |
142 | |
143 get: function (id) { | |
144 return this._cache[id]; | |
145 }, | |
146 | |
147 store: function (id, value) { | |
148 return (this._cache[id] = value); | |
149 }, | |
150 | |
151 _configure: function (data, opts) { | |
152 var typemap = {}, | |
153 d = new Deferred(), | |
154 me = this, | |
155 p, | |
156 contextRequire = (opts && opts.contextRequire) || require; | |
157 | |
158 var services = {}; | |
159 | |
160 for (p in data) { | |
161 var service = me._parse(data[p], typemap); | |
162 if (!(service instanceof Descriptor)) | |
163 service = new Value(service, false); | |
164 services[p] = service; | |
165 } | |
166 | |
167 me.register(services); | |
168 | |
169 var names = []; | |
170 | |
171 for (p in typemap) | |
172 names.push(p); | |
173 | |
174 if (names.length) { | |
175 contextRequire(names, function () { | |
176 for (var i = 0; i < names.length; i++) | |
177 typemap[names[i]] = arguments[i]; | |
178 d.resolve(me); | |
179 }); | |
180 } else { | |
181 d.resolve(me); | |
182 } | |
183 return d.promise; | |
184 }, | |
185 | |
186 _parse: function (data, typemap) { | |
187 if (safe.isPrimitive(data) || data instanceof Descriptor) | |
188 return data; | |
189 if (data.$dependency) | |
190 return new Reference( | |
191 data.$dependency, | |
192 data.lazy, | |
193 data.optional, | |
194 data["default"], | |
195 data.services && this._parseObject(data.services, typemap)); | |
196 if (data.$value) { | |
197 var raw = !data.parse; | |
198 return new Value(raw ? data.$value : this._parse( | |
199 data.$value, | |
200 typemap), raw); | |
201 } | |
202 if (data.$type || data.$factory) | |
203 return this._parseService(data, typemap); | |
204 if (data instanceof Array) | |
205 return this._parseArray(data, typemap); | |
206 | |
207 return this._parseObject(data, typemap); | |
208 }, | |
209 | |
210 _parseService: function (data, typemap) { | |
211 var me = this, | |
212 opts = { | |
213 owner: this | |
214 }; | |
215 if (data.$type) { | |
216 | |
217 opts.type = data.$type; | |
218 | |
219 if (typeof (data.$type) === "string") { | |
220 typemap[data.$type] = null; | |
221 opts.typeMap = typemap; | |
222 } | |
223 } | |
224 | |
225 if (data.$factory) | |
226 opts.factory = data.$factory; | |
227 | |
228 if (data.services) | |
229 opts.services = me._parseObject(data.services, typemap); | |
230 if (data.inject) | |
231 opts.inject = data.inject instanceof Array ? array.map( | |
232 data.inject, | |
233 function (x) { | |
234 return me._parseObject(x, typemap); | |
235 }) : me._parseObject(data.inject, typemap); | |
236 if (data.params) | |
237 opts.params = me._parse(data.params, typemap); | |
238 | |
239 if (data.activation) { | |
240 if (typeof (data.activation) === "string") { | |
241 switch (data.activation.toLowerCase()) { | |
242 case "singleton": | |
243 opts.activation = Service.SINGLETON; | |
244 break; | |
245 case "container": | |
246 opts.activation = Service.CONTAINER; | |
247 break; | |
248 case "hierarchy": | |
249 opts.activation = Service.HIERARCHY; | |
250 break; | |
251 case "context": | |
252 opts.activation = Service.CONTEXT; | |
253 break; | |
254 case "call": | |
255 opts.activation = Service.CALL; | |
256 break; | |
257 default: | |
258 throw new Error("Unknown activation type: " + | |
259 data.activation); | |
260 } | |
261 } else { | |
262 opts.activation = Number(data.activation); | |
263 } | |
264 } | |
265 | |
266 if (data.cleanup) | |
267 opts.cleanup = data.cleanup; | |
268 | |
269 return new Service(opts); | |
270 }, | |
271 | |
272 _parseObject: function (data, typemap) { | |
273 if (data.constructor && | |
274 data.constructor.prototype !== Object.prototype) | |
275 return new Value(data, true); | |
276 | |
277 var o = {}; | |
278 | |
279 for (var p in data) | |
280 o[p] = this._parse(data[p], typemap); | |
281 | |
282 return o; | |
283 }, | |
284 | |
285 _parseArray: function (data, typemap) { | |
286 if (data.constructor && | |
287 data.constructor.prototype !== Array.prototype) | |
288 return new Value(data, true); | |
289 | |
290 var me = this; | |
291 return array.map(data, function (x) { | |
292 return me._parse(x, typemap); | |
293 }); | |
294 } | |
295 | |
296 }); | |
297 | |
298 return Container; | |
299 }); |