Mercurial > pub > ImplabJs
annotate src/implab/di/Container.js @ 5:3d124d0b9078
improved declare/override, added override.before, override.after, override.hide,
inherited.arguments.
author | cin |
---|---|
date | Fri, 16 Jun 2017 02:14:25 +0300 |
parents | fcc63f34d0a2 |
children |
rev | line source |
---|---|
0 | 1 define([ |
2 "../declare", | |
3 "../safe", | |
1 | 4 "../Uuid", |
5 "../Deferred", | |
0 | 6 "./ActivationContext", |
7 "./Descriptor", | |
8 "./ValueDescriptor", | |
9 "./ReferenceDescriptor", | |
10 "./ServiceDescriptor", | |
11 "./ActivationError" | |
12 ], function ( | |
13 declare, | |
14 safe, | |
1 | 15 Uuid, |
0 | 16 Deferred, |
17 ActivationContext, | |
18 Descriptor, | |
19 Value, | |
20 Reference, | |
21 Service, | |
22 ActivationError) { | |
23 var Container = declare(null, { | |
24 _services: null, | |
25 _cache: null, | |
26 _cleanup: null, | |
27 _root: null, | |
28 _parent: null, | |
29 | |
30 constructor: function (parent) { | |
31 this._parent = parent; | |
3 | 32 this._services = parent ? Object.create(parent._services) : {}; |
0 | 33 this._cache = {}; |
34 this._cleanup = []; | |
35 this._root = parent ? parent.getRootContainer() : this; | |
36 this._services.container = new Value(this, true); | |
37 }, | |
38 | |
39 getRootContainer: function () { | |
40 return this._root; | |
41 }, | |
42 | |
43 getParent: function () { | |
44 return this._parent; | |
45 }, | |
46 | |
1 | 47 /** |
48 * | |
49 */ | |
0 | 50 getService: function (name, def) { |
51 var d = this._services[name]; | |
52 if (!d) | |
53 if (arguments.length > 1) | |
54 return def; | |
55 else | |
56 throw new Error("Service '" + name + "' isn't found"); | |
57 if (d.isInstanceCreated()) | |
58 return d.getInstance(); | |
59 | |
60 var context = new ActivationContext(this, this._services); | |
61 | |
62 try { | |
63 return d.activate(context, name); | |
64 } catch (error) { | |
65 throw new ActivationError(name, context.getStack(), error); | |
66 } | |
67 }, | |
68 | |
69 register: function (name, service) { | |
70 if (arguments.length == 1) { | |
71 var data = name; | |
72 for (name in data) | |
73 this.register(name, data[name]); | |
74 } else { | |
75 if (!(service instanceof Descriptor)) | |
76 service = new Value(service, true); | |
77 this._services[name] = service; | |
78 } | |
79 return this; | |
80 }, | |
81 | |
82 onDispose: function (callback) { | |
83 if (!(callback instanceof Function)) | |
84 throw new Error("The callback must be a function"); | |
85 this._cleanup.push(callback); | |
86 }, | |
87 | |
88 dispose: function () { | |
89 if (this._cleanup) { | |
90 for (var i = 0; i < this._cleanup.length; i++) | |
91 this._cleanup[i].call(null); | |
92 this._cleanup = null; | |
93 } | |
94 }, | |
95 | |
96 /** | |
97 * @param{String|Object} config | |
98 * The configuration of the contaier. Can be either a string or an object, | |
99 * if the configuration is an object it's treated as a collection of | |
100 * services which will be registed in the contaier. | |
101 * | |
102 * @param{Function} opts.contextRequire | |
103 * The function which will be used to load a configuration or types for services. | |
104 * | |
105 */ | |
106 configure: function (config, opts) { | |
107 var p, me = this, | |
108 contextRequire = (opts && opts.contextRequire); | |
109 | |
110 if (typeof (config) === "string") { | |
111 p = new Deferred(); | |
112 if (!contextRequire) { | |
4
fcc63f34d0a2
fixed container configuration when the config path contains only a package name
cin
parents:
3
diff
changeset
|
113 var shim = [config, new Uuid()].join(config.indexOf("/") != -1 ? "-" : "/"); |
0 | 114 define(shim, ["require", config], function (ctx, data) { |
115 p.resolve([data, { | |
116 contextRequire: ctx | |
117 }]); | |
118 }); | |
119 require([shim]); | |
120 } else { | |
121 // TODO how to get correct contextRequire for the relative config module? | |
122 contextRequire([config], function (data) { | |
123 p.resolve([data, { | |
124 contextRequire: contextRequire | |
125 }]); | |
126 }); | |
127 } | |
128 | |
129 return p.then(function (args) { | |
130 return me._configure.apply(me, args); | |
131 }); | |
132 } else { | |
133 return me._configure(config, opts); | |
134 } | |
135 }, | |
136 | |
137 createChildContainer: function () { | |
138 return new Container(this); | |
139 }, | |
140 | |
141 has: function (id) { | |
142 return id in this._cache; | |
143 }, | |
144 | |
145 get: function (id) { | |
146 return this._cache[id]; | |
147 }, | |
148 | |
149 store: function (id, value) { | |
150 return (this._cache[id] = value); | |
151 }, | |
152 | |
153 _configure: function (data, opts) { | |
154 var typemap = {}, | |
155 d = new Deferred(), | |
156 me = this, | |
157 p, | |
158 contextRequire = (opts && opts.contextRequire) || require; | |
159 | |
160 var services = {}; | |
161 | |
162 for (p in data) { | |
163 var service = me._parse(data[p], typemap); | |
164 if (!(service instanceof Descriptor)) | |
165 service = new Value(service, false); | |
166 services[p] = service; | |
167 } | |
168 | |
169 me.register(services); | |
170 | |
171 var names = []; | |
172 | |
173 for (p in typemap) | |
174 names.push(p); | |
175 | |
176 if (names.length) { | |
177 contextRequire(names, function () { | |
178 for (var i = 0; i < names.length; i++) | |
179 typemap[names[i]] = arguments[i]; | |
180 d.resolve(me); | |
181 }); | |
182 } else { | |
183 d.resolve(me); | |
184 } | |
185 return d.promise; | |
186 }, | |
187 | |
188 _parse: function (data, typemap) { | |
189 if (safe.isPrimitive(data) || data instanceof Descriptor) | |
190 return data; | |
191 if (data.$dependency) | |
192 return new Reference( | |
193 data.$dependency, | |
194 data.lazy, | |
195 data.optional, | |
196 data["default"], | |
197 data.services && this._parseObject(data.services, typemap)); | |
198 if (data.$value) { | |
199 var raw = !data.parse; | |
200 return new Value(raw ? data.$value : this._parse( | |
201 data.$value, | |
202 typemap), raw); | |
203 } | |
204 if (data.$type || data.$factory) | |
205 return this._parseService(data, typemap); | |
206 if (data instanceof Array) | |
207 return this._parseArray(data, typemap); | |
208 | |
209 return this._parseObject(data, typemap); | |
210 }, | |
211 | |
212 _parseService: function (data, typemap) { | |
213 var me = this, | |
214 opts = { | |
215 owner: this | |
216 }; | |
217 if (data.$type) { | |
218 | |
219 opts.type = data.$type; | |
220 | |
221 if (typeof (data.$type) === "string") { | |
222 typemap[data.$type] = null; | |
223 opts.typeMap = typemap; | |
224 } | |
225 } | |
226 | |
227 if (data.$factory) | |
228 opts.factory = data.$factory; | |
229 | |
230 if (data.services) | |
231 opts.services = me._parseObject(data.services, typemap); | |
232 if (data.inject) | |
1 | 233 opts.inject = data.inject instanceof Array ? data.inject.map(function (x) { |
234 return me._parseObject(x, typemap); | |
235 }) : me._parseObject(data.inject, typemap); | |
0 | 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; | |
1 | 291 return data.map(function (x) { |
0 | 292 return me._parse(x, typemap); |
293 }); | |
294 } | |
295 | |
296 }); | |
297 | |
298 return Container; | |
299 }); |