Mercurial > pub > ImplabJs
comparison 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 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:fc2517695ee1 |
---|---|
1 define( | |
2 [ | |
3 "dojo/_base/declare", | |
4 "../safe", | |
5 "dojo/_base/lang", | |
6 "dojo/_base/array", | |
7 "./Descriptor", | |
8 "./ValueDescriptor" | |
9 ], | |
10 | |
11 function (declare, safe, lang, array, Descriptor, Value) { | |
12 var SINGLETON_ACTIVATION = 1, | |
13 CONTAINER_ACTIVATION = 2, | |
14 CONTEXT_ACTIVATION = 3, | |
15 CALL_ACTIVATION = 4, | |
16 HIERARCHY_ACTIVATION = 5; | |
17 | |
18 var injectMethod = function (target, method, context, args) { | |
19 var m = target[method]; | |
20 if (!m) | |
21 throw new Error("Method '" + method + "' not found"); | |
22 | |
23 if (args instanceof Array) | |
24 m.apply(target, context.parse(args, "." + method)); | |
25 else | |
26 m.call(target, context.parse(args, "." + method)); | |
27 }; | |
28 | |
29 var makeClenupCallback = function (target, method) { | |
30 if (typeof (method) === "string") { | |
31 return function () { | |
32 target[method](); | |
33 }; | |
34 } else { | |
35 return function () { | |
36 method(target); | |
37 }; | |
38 } | |
39 }; | |
40 | |
41 var cacheId = 0; | |
42 | |
43 var cls = declare( | |
44 Descriptor, { | |
45 _instance: null, | |
46 _hasInstance: false, | |
47 _activationType: CALL_ACTIVATION, | |
48 _services: null, | |
49 _type: null, | |
50 _typeMap: null, | |
51 _factory: null, | |
52 _params: undefined, | |
53 _inject: null, | |
54 _cleanup: null, | |
55 _cacheId: null, | |
56 _owner: null, | |
57 | |
58 constructor: function (opts) { | |
59 safe.argumentNotNull(opts, "opts"); | |
60 safe.argumentNotNull(opts.owner, "opts.owner"); | |
61 | |
62 this._owner = opts.owner; | |
63 | |
64 if (!(opts.type || opts.factory)) | |
65 throw new Error( | |
66 "Either a type or a factory must be specified"); | |
67 | |
68 if (typeof (opts.type) === "string" && !opts.typeMap) | |
69 throw new Error( | |
70 "The typeMap is required when the type is specified by its name"); | |
71 | |
72 if (opts.activation) | |
73 this._activationType = opts.activation; | |
74 if (opts.type) | |
75 this._type = opts.type; | |
76 if (opts.params) | |
77 this._params = opts.params; | |
78 if (opts.inject) | |
79 this._inject = opts.inject instanceof Array ? opts.inject : [opts.inject]; | |
80 if (opts.services) | |
81 this._services = opts.services; | |
82 if (opts.factory) | |
83 this._factory = opts.factory; | |
84 if (opts.typeMap) | |
85 this._typeMap = opts.typeMap; | |
86 if (opts.cleanup) { | |
87 if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function)) | |
88 throw new Error( | |
89 "The cleanup parameter must be either a function or a function name"); | |
90 | |
91 this._cleanup = opts.cleanup; | |
92 } | |
93 | |
94 this._cacheId = ++cacheId; | |
95 }, | |
96 | |
97 activate: function (context, name) { | |
98 | |
99 // if we have a local service records, register them first | |
100 | |
101 var instance; | |
102 | |
103 switch (this._activationType) { | |
104 case 1: // SINGLETON | |
105 // if the value is cached return it | |
106 if (this._hasInstance) | |
107 return this._instance; | |
108 | |
109 var tof = this._type || this._factory; | |
110 | |
111 // create the persistent cache identifier for the type | |
112 if (safe.isPrimitive(tof)) | |
113 this._cacheId = this._type; | |
114 else | |
115 this._cacheId = safe.oid(tof); | |
116 | |
117 // singletons are bound to the root container | |
118 var container = context.container.getRootContainer(); | |
119 | |
120 if (container.has(this._cacheId)) { | |
121 instance = container.get(this._cacheId); | |
122 } else { | |
123 instance = this._create(context, name); | |
124 container.store(this._cacheId, instance); | |
125 if (this._cleanup) | |
126 container.onDispose( | |
127 makeClenupCallback(instance, this._cleanup)); | |
128 } | |
129 | |
130 this._hasInstance = true; | |
131 return (this._instance = instance); | |
132 | |
133 case 2: // CONTAINER | |
134 //return a cached value | |
135 if (this._hasInstance) | |
136 return this._instance; | |
137 | |
138 // create an instance | |
139 instance = this._create(context, name); | |
140 | |
141 // the instance is bound to the container | |
142 if (this._cleanup) | |
143 this._owner.onDispose( | |
144 makeClenupCallback(instance, this._cleanup)); | |
145 | |
146 // cache and return the instance | |
147 this._hasInstance = true; | |
148 return (this._instance = instance); | |
149 case 3: // CONTEXT | |
150 //return a cached value if one exists | |
151 if (context.has(this._cacheId)) | |
152 return context.get(this._cacheId); | |
153 // context context activated instances are controlled by callers | |
154 return context.store(this._cacheId, this._create( | |
155 context, | |
156 name)); | |
157 case 4: // CALL | |
158 // per-call created instances are controlled by callers | |
159 return this._create(context, name); | |
160 case 5: // HIERARCHY | |
161 // hierarchy activated instances are behave much like container activated | |
162 // except they are created and bound to the child container | |
163 | |
164 // return a cached value | |
165 if (context.container.has(this._cacheId)) | |
166 return context.container.get(this._cacheId); | |
167 | |
168 instance = this._create(context, name); | |
169 | |
170 if (this._cleanup) | |
171 context.container.onDispose(makeClenupCallback( | |
172 instance, | |
173 this._cleanup)); | |
174 | |
175 return context.container.store(this._cacheId, instance); | |
176 default: | |
177 throw "Invalid activation type: " + this._activationType; | |
178 } | |
179 }, | |
180 | |
181 isInstanceCreated: function () { | |
182 return this._hasInstance; | |
183 }, | |
184 | |
185 getInstance: function () { | |
186 return this._instance; | |
187 }, | |
188 | |
189 _create: function (context, name) { | |
190 context.enter(name, this, Boolean(this._services)); | |
191 | |
192 if (this._activationType != CALL_ACTIVATION && | |
193 context.visit(this._cacheId) > 0) | |
194 throw new Error("Recursion detected"); | |
195 | |
196 if (this._services) { | |
197 for (var p in this._services) { | |
198 var sv = this._services[p]; | |
199 context.register(p, sv instanceof Descriptor ? sv : new Value(sv, false)); | |
200 } | |
201 } | |
202 | |
203 var instance; | |
204 | |
205 if (!this._factory) { | |
206 var ctor, type = this._type; | |
207 | |
208 if (typeof (type) === "string") { | |
209 ctor = this._typeMap[type]; | |
210 if (!ctor) | |
211 throw new Error("Failed to resolve the type '" + | |
212 type + "'"); | |
213 } else { | |
214 ctor = type; | |
215 } | |
216 | |
217 if (this._params === undefined) { | |
218 this._factory = function () { | |
219 return new ctor(); | |
220 }; | |
221 } else if (this._params instanceof Array) { | |
222 this._factory = function () { | |
223 var inst = lang.delegate(ctor.prototype); | |
224 var ret = ctor.apply(inst, arguments); | |
225 return typeof (ret) === "object" ? ret : inst; | |
226 }; | |
227 } else { | |
228 this._factory = function (param) { | |
229 return new ctor(param); | |
230 }; | |
231 } | |
232 } | |
233 | |
234 if (this._params === undefined) { | |
235 instance = this._factory(); | |
236 } else if (this._params instanceof Array) { | |
237 instance = this._factory.apply(this, context.parse( | |
238 this._params, | |
239 ".params")); | |
240 } else { | |
241 instance = this._factory(context.parse( | |
242 this._params, | |
243 ".params")); | |
244 } | |
245 | |
246 if (this._inject) { | |
247 array.forEach(this._inject, function (spec) { | |
248 for (var m in spec) | |
249 injectMethod(instance, m, context, spec[m]); | |
250 }); | |
251 } | |
252 | |
253 context.leave(); | |
254 | |
255 return instance; | |
256 }, | |
257 | |
258 // @constructor {singleton} foo/bar/Baz | |
259 // @factory {singleton} | |
260 toString: function () { | |
261 var parts = []; | |
262 | |
263 parts.push(this._type ? "@constructor" : "@factory"); | |
264 | |
265 parts.push(activationNames[this._activationType]); | |
266 | |
267 if (typeof (this._type) === "string") | |
268 parts.push(this._type); | |
269 | |
270 return parts.join(" "); | |
271 } | |
272 | |
273 }); | |
274 | |
275 cls.SINGLETON = SINGLETON_ACTIVATION; | |
276 cls.CONTAINER = CONTAINER_ACTIVATION; | |
277 cls.CONTEXT = CONTEXT_ACTIVATION; | |
278 cls.CALL = CALL_ACTIVATION; | |
279 cls.HIERARCHY = HIERARCHY_ACTIVATION; | |
280 | |
281 var activationNames = [ | |
282 "", | |
283 "{singleton}", | |
284 "{container}", | |
285 "{context}", | |
286 "{call}", | |
287 "{hierarchy}" | |
288 ]; | |
289 | |
290 return cls; | |
291 }); |