@lumjs/core 1.35.1 → 1.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/events/listener.js +5 -1
- package/lib/events/registry.js +136 -36
- package/lib/obj/clone.js +3 -0
- package/lib/obj/index.js +2 -1
- package/lib/obj/unlocked.js +87 -0
- package/lib/{traits.js → traits/funcs.js} +4 -255
- package/lib/traits/index.js +13 -0
- package/lib/traits/registry.js +166 -0
- package/lib/traits/trait.js +194 -0
- package/package.json +1 -1
package/lib/events/listener.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const {F,isObj} = require('../types');
|
|
3
|
+
const {F,isObj,def} = require('../types');
|
|
4
4
|
const Event = require('./event');
|
|
5
5
|
|
|
6
6
|
const REMOVE_OPTS = ['listener','handler','eventNames'];
|
|
@@ -127,6 +127,10 @@ class LumEventListener
|
|
|
127
127
|
return event;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
static get classProps()
|
|
131
|
+
{
|
|
132
|
+
return Object.getOwnPropertyNames(this.prototype);
|
|
133
|
+
}
|
|
130
134
|
}
|
|
131
135
|
|
|
132
136
|
LumEventListener.isListener = isListener;
|
package/lib/events/registry.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const {S,F,isObj,def,isIterable} = require('../types');
|
|
4
4
|
const Listener = require('./listener');
|
|
5
5
|
const RegSym = Symbol('@lumjs/core/events:registry');
|
|
6
|
+
const cp = Object.assign;
|
|
6
7
|
|
|
7
8
|
const DEF_EXTENDS =
|
|
8
9
|
{
|
|
@@ -13,6 +14,13 @@ const DEF_EXTENDS =
|
|
|
13
14
|
once: null,
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
const INT_EXTENDS =
|
|
18
|
+
{
|
|
19
|
+
listeners: true,
|
|
20
|
+
results: true,
|
|
21
|
+
onDemand: false,
|
|
22
|
+
}
|
|
23
|
+
|
|
16
24
|
const DEF_OPTIONS =
|
|
17
25
|
{
|
|
18
26
|
delimiter: /\s+/,
|
|
@@ -20,6 +28,12 @@ const DEF_OPTIONS =
|
|
|
20
28
|
wildcard: '*',
|
|
21
29
|
}
|
|
22
30
|
|
|
31
|
+
const RES_PROPS =
|
|
32
|
+
[
|
|
33
|
+
'eventNames', 'targets', 'multiMatch', 'onceRemoved', 'stopEmitting',
|
|
34
|
+
'emitted', 'targetListeners', 'registry',
|
|
35
|
+
]
|
|
36
|
+
|
|
23
37
|
/**
|
|
24
38
|
* Has a target object been registered with an event registry?
|
|
25
39
|
* @param {object} target
|
|
@@ -98,29 +112,53 @@ class LumEventRegistry
|
|
|
98
112
|
* If you want to use a `function` as an actual target, you'll need to
|
|
99
113
|
* wrap it in an array or other iterable object.
|
|
100
114
|
*
|
|
101
|
-
* @param {object} [opts] Options
|
|
115
|
+
* @param {object} [opts] Options
|
|
116
|
+
*
|
|
117
|
+
* A _compiled_ version is saved to the `options` property.
|
|
118
|
+
* The compiled version includes a bunch of defaults, and various
|
|
119
|
+
* compose rules (mostly for the `.extend` nested options).
|
|
102
120
|
*
|
|
103
121
|
* @param {(RegExp|string)} [opts.delimiter=/\s+/] Used to split event names
|
|
104
122
|
*
|
|
105
|
-
* @param {
|
|
106
|
-
*
|
|
107
|
-
*
|
|
123
|
+
* @param {object} [opts.extend] Options for wrapper methods/properties
|
|
124
|
+
*
|
|
125
|
+
* The `boolean` options determine if extension methods will be added to
|
|
126
|
+
* certain types of objects (and in some cases, when to do so).
|
|
108
127
|
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
128
|
+
* The `?string` options are each the names of properties/methods in the
|
|
129
|
+
* Registry class. If they are set to a `string` then that will be the
|
|
130
|
+
* name used for the wrapper property/method added to objects. If it
|
|
131
|
+
* is explicitly set to `null` it means skip adding a wrapper for that
|
|
132
|
+
* method/property. If it is omitted entirely the default will be used.
|
|
111
133
|
*
|
|
112
|
-
*
|
|
113
|
-
* it disables adding extension properties entirely.
|
|
134
|
+
* @param {boolean} [opts.extend.targets] Extend target objects?
|
|
114
135
|
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
136
|
+
* The default will be `true` when `targets` is an `object`, or `false`
|
|
137
|
+
* when `targets` is a `function`.
|
|
138
|
+
*
|
|
139
|
+
* @param {boolean} [opts.extend.listeners=true] Extend Listener instances?
|
|
140
|
+
* As returned by `makeListener()`, `listen()`, and `once()`
|
|
141
|
+
* @param {boolean} [opts.extend.results=true] Extend `emit()` results?
|
|
142
|
+
* @param {boolean} [opts.extend.onDemand=false] On-demand target setup
|
|
143
|
+
*
|
|
144
|
+
* If `targets` was a `function` and this is set to `true`, then
|
|
145
|
+
* we'll perform the target setup on every emit() call. The setup process
|
|
146
|
+
* is skipped on any targets that have already been set up, so this
|
|
147
|
+
* is meant for dynamic targets that may change on every call.
|
|
148
|
+
*
|
|
149
|
+
* @param {?string} [opts.extend.registry="events"] Registry property;
|
|
150
|
+
* only added to `targets`, never to listeners or results which have
|
|
151
|
+
* their own inherent `registry` property already.
|
|
117
152
|
*
|
|
118
|
-
* @param {?string} [opts.extend.registry="events"] Registry property
|
|
119
153
|
* @param {?string} [opts.extend.emit="emit"] `emit()` proxy method
|
|
120
154
|
* @param {?string} [opts.extend.listen="on"] `listen()` proxy method
|
|
121
155
|
* @param {?string} [opts.extend.once=null] `once()` proxy method
|
|
122
156
|
* @param {?string} [opts.extend.remove=null] `remove()` proxy method
|
|
123
157
|
*
|
|
158
|
+
* The `remove` wrapper method added to Listener instances is slightly
|
|
159
|
+
* different than the one added to other objects, as if you call it
|
|
160
|
+
* with no arguments, it will pass the Listener itself as the argument.
|
|
161
|
+
*
|
|
124
162
|
* @param {boolean} [opts.multiMatch=false]
|
|
125
163
|
* If a registered listener has multiple event names, and a call
|
|
126
164
|
* to `emit()` also has multiple event names, the value of this
|
|
@@ -159,7 +197,7 @@ class LumEventRegistry
|
|
|
159
197
|
*/
|
|
160
198
|
constructor(targets, opts={})
|
|
161
199
|
{
|
|
162
|
-
let defExt; // Default opts.extend value
|
|
200
|
+
let defExt; // Default opts.extend.targets value
|
|
163
201
|
if (typeof targets === F)
|
|
164
202
|
{ // A dynamic getter method
|
|
165
203
|
this.funTargets = true;
|
|
@@ -181,47 +219,53 @@ class LumEventRegistry
|
|
|
181
219
|
defExt = true;
|
|
182
220
|
}
|
|
183
221
|
|
|
184
|
-
|
|
222
|
+
// Build composite extend rules
|
|
223
|
+
const extend = cp(
|
|
224
|
+
{targets: defExt},
|
|
225
|
+
INT_EXTENDS,
|
|
226
|
+
DEF_EXTENDS,
|
|
227
|
+
opts.extend);
|
|
228
|
+
|
|
229
|
+
// Now compile the final options
|
|
230
|
+
this.options = cp({}, DEF_OPTIONS, opts, {extend});
|
|
185
231
|
|
|
186
232
|
this.allListeners = new Set();
|
|
187
233
|
this.listenersFor = new Map();
|
|
188
234
|
|
|
189
|
-
this.
|
|
235
|
+
this.setupTargets(targets);
|
|
190
236
|
} // constructor()
|
|
191
237
|
|
|
192
238
|
/**
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
239
|
+
* Set up target objects
|
|
240
|
+
*
|
|
241
|
+
* Always sets the necessary metadata on each target.
|
|
242
|
+
* May also extend the targets with wrapper properties and methods
|
|
243
|
+
* depending on the `options.extend` values set.
|
|
244
|
+
*
|
|
245
|
+
* Not meant to be called from outside code.
|
|
196
246
|
* @private
|
|
197
247
|
* @param {Iterable} targets - Targets to extend
|
|
198
248
|
* @returns {module:@lumjs/core/events.Registry} `this`
|
|
199
249
|
*/
|
|
200
|
-
|
|
250
|
+
setupTargets(targets)
|
|
201
251
|
{
|
|
202
252
|
const opts = this.options;
|
|
203
253
|
const extOpts = opts.extend;
|
|
204
|
-
|
|
205
|
-
let intNames = null, extNames = null;
|
|
206
|
-
|
|
207
|
-
if (extOpts)
|
|
208
|
-
{
|
|
209
|
-
intNames = Object.keys(DEF_EXTENDS);
|
|
210
|
-
extNames = Object.assign({}, DEF_EXTENDS, extOpts);
|
|
211
|
-
}
|
|
254
|
+
const intNames = extOpts.targets ? Object.keys(DEF_EXTENDS) : null;
|
|
212
255
|
|
|
213
256
|
for (const target of targets)
|
|
214
257
|
{
|
|
215
258
|
const tps = {}, tpm = getMetadata(target, true);
|
|
259
|
+
if (tpm.r.has(this)) continue; // Already set up with this registry.
|
|
216
260
|
tpm.r.set(this, tps);
|
|
217
261
|
|
|
218
|
-
if (extOpts)
|
|
262
|
+
if (extOpts.targets)
|
|
219
263
|
{
|
|
220
264
|
for (const iname of intNames)
|
|
221
265
|
{
|
|
222
|
-
if (typeof
|
|
266
|
+
if (typeof extOpts[iname] === S && extOpts[iname].trim() !== '')
|
|
223
267
|
{
|
|
224
|
-
const ename =
|
|
268
|
+
const ename = extOpts[iname];
|
|
225
269
|
const value = iname === 'registry'
|
|
226
270
|
? this // The registry instance itself
|
|
227
271
|
: (...args) => this[iname](...args) // A proxy method
|
|
@@ -240,6 +284,48 @@ class LumEventRegistry
|
|
|
240
284
|
}
|
|
241
285
|
}
|
|
242
286
|
}
|
|
287
|
+
|
|
288
|
+
return this;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Add extension methods to internal objects;
|
|
293
|
+
* currently supports Listener instances and result/status objects.
|
|
294
|
+
* Not meant to be called from outside code.
|
|
295
|
+
* @private
|
|
296
|
+
* @param {object} obj - Internal object to extend
|
|
297
|
+
* @returns {module:@lumjs/core/events.Registry} `this`
|
|
298
|
+
*/
|
|
299
|
+
extendInternal(obj)
|
|
300
|
+
{
|
|
301
|
+
const opts = this.options;
|
|
302
|
+
const extOpts = opts.extend;
|
|
303
|
+
const intNames = Object.keys(DEF_EXTENDS);
|
|
304
|
+
const isLs = (obj instanceof Listener);
|
|
305
|
+
const reserved = isLs ? Listener.classProps : RES_PROPS;
|
|
306
|
+
|
|
307
|
+
for (const iname of intNames)
|
|
308
|
+
{
|
|
309
|
+
if (iname === 'registry') continue; // skip the registry property
|
|
310
|
+
if (typeof extOpts[iname] === S && extOpts[iname].trim() !== '')
|
|
311
|
+
{
|
|
312
|
+
const ename = extOpts[iname];
|
|
313
|
+
if (reserved.includes(ename))
|
|
314
|
+
{ // Skip reserved names
|
|
315
|
+
console.warn("reserved property", {ename, obj, registry: this});
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const value = (isLs && iname === 'remove')
|
|
320
|
+
? (what=obj) => this.remove(what) // special remove wrapper
|
|
321
|
+
: (...args) => this[iname](...args) // regular wrapper method
|
|
322
|
+
if (opts.overwrite || obj[ename] === undefined)
|
|
323
|
+
{
|
|
324
|
+
def(obj, ename, {value});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
243
329
|
return this;
|
|
244
330
|
}
|
|
245
331
|
|
|
@@ -305,16 +391,21 @@ class LumEventRegistry
|
|
|
305
391
|
}
|
|
306
392
|
else if (args.length === 1 && isObj(args[0]))
|
|
307
393
|
{ // listen(spec)
|
|
308
|
-
spec =
|
|
394
|
+
spec = cp({}, args[0]);
|
|
309
395
|
}
|
|
310
396
|
else
|
|
311
397
|
{ // listen(eventNames, listener, [spec])
|
|
312
|
-
spec =
|
|
398
|
+
spec = cp({}, args[2]);
|
|
313
399
|
spec.eventNames = args[0];
|
|
314
400
|
spec.handler = args[1];
|
|
315
401
|
}
|
|
316
402
|
|
|
317
|
-
|
|
403
|
+
const lsnr = new Listener(this, spec);
|
|
404
|
+
if (this.options.extend.listeners)
|
|
405
|
+
{
|
|
406
|
+
this.extendInternal(lsnr);
|
|
407
|
+
}
|
|
408
|
+
return lsnr;
|
|
318
409
|
}
|
|
319
410
|
|
|
320
411
|
/**
|
|
@@ -524,6 +615,7 @@ class LumEventRegistry
|
|
|
524
615
|
*/
|
|
525
616
|
emit(eventNames, ...args)
|
|
526
617
|
{
|
|
618
|
+
const extOpts = this.options.extend;
|
|
527
619
|
const sti =
|
|
528
620
|
{
|
|
529
621
|
eventNames: this.getEventNames(eventNames),
|
|
@@ -531,11 +623,21 @@ class LumEventRegistry
|
|
|
531
623
|
onceRemoved: new Set(),
|
|
532
624
|
stopEmitting: false,
|
|
533
625
|
emitted: [],
|
|
626
|
+
registry: this,
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (extOpts.results)
|
|
630
|
+
{
|
|
631
|
+
this.extendInternal(sti);
|
|
534
632
|
}
|
|
535
633
|
|
|
536
634
|
{ // Get the targets.
|
|
537
635
|
const tgs = this.getTargets(sti);
|
|
538
636
|
sti.targets = (tgs instanceof Set) ? tgs : new Set(tgs);
|
|
637
|
+
if (this.funTargets && extOpts.onDemand)
|
|
638
|
+
{
|
|
639
|
+
this.setupTargets(sti.targets);
|
|
640
|
+
}
|
|
539
641
|
}
|
|
540
642
|
|
|
541
643
|
const wilds = this.listenersFor.get(this.options.wildcard);
|
|
@@ -579,8 +681,6 @@ class LumEventRegistry
|
|
|
579
681
|
return sti;
|
|
580
682
|
}
|
|
581
683
|
|
|
582
|
-
|
|
583
|
-
|
|
584
684
|
/**
|
|
585
685
|
* Register additional target objects
|
|
586
686
|
* @param {...object} addTargets - Target objects to register
|
|
@@ -617,7 +717,7 @@ class LumEventRegistry
|
|
|
617
717
|
}
|
|
618
718
|
}
|
|
619
719
|
|
|
620
|
-
this.
|
|
720
|
+
this.setupTargets(addTargets);
|
|
621
721
|
return this;
|
|
622
722
|
}
|
|
623
723
|
|
|
@@ -735,7 +835,7 @@ class LumEventRegistry
|
|
|
735
835
|
|
|
736
836
|
}
|
|
737
837
|
|
|
738
|
-
|
|
838
|
+
cp(LumEventRegistry,
|
|
739
839
|
{
|
|
740
840
|
isRegistered, getMetadata, targetsAre,
|
|
741
841
|
});
|
package/lib/obj/clone.js
CHANGED
|
@@ -296,6 +296,9 @@ exports.addClone = addClone;
|
|
|
296
296
|
*
|
|
297
297
|
* If not, if the object has a `clone()` method it will be used.
|
|
298
298
|
* Otherwise use our `clone()` function.
|
|
299
|
+
*
|
|
300
|
+
* **NOTE**: A newer replacement for this function exists, see
|
|
301
|
+
* {@link module:@lumjs/core/obj.unlocked} for details.
|
|
299
302
|
*
|
|
300
303
|
* @param {object} obj - The object to clone if needed.
|
|
301
304
|
* @param {object} [opts] - Options to pass to `clone()` method.
|
package/lib/obj/index.js
CHANGED
|
@@ -14,6 +14,7 @@ const {lock,addLock} = require('./lock');
|
|
|
14
14
|
const {mergeNested,syncNested} = require('./merge');
|
|
15
15
|
const ns = require('./ns');
|
|
16
16
|
const cp = require('./cp');
|
|
17
|
+
const unlocked = require('./unlocked');
|
|
17
18
|
|
|
18
19
|
const
|
|
19
20
|
{
|
|
@@ -27,5 +28,5 @@ module.exports =
|
|
|
27
28
|
mergeNested, syncNested, copyProps, copyAll, ns,
|
|
28
29
|
getObjectPath, setObjectPath, getNamespace, setNamespace,
|
|
29
30
|
getProperty, duplicateAll, duplicateOne, getMethods, signatureOf,
|
|
30
|
-
MethodFilter, apply, flip, flipKeyVal, flipMap,
|
|
31
|
+
MethodFilter, apply, flip, flipKeyVal, flipMap, unlocked,
|
|
31
32
|
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {F,S,isObj,needObj} = require('../types');
|
|
4
|
+
|
|
5
|
+
const CLONE = 'clone';
|
|
6
|
+
const clone = obj => Object.assign({}, obj);
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get an unlocked object
|
|
10
|
+
*
|
|
11
|
+
* @param {object} obj - The target object;
|
|
12
|
+
*
|
|
13
|
+
* If the object is extensible it will be returned _as is_.
|
|
14
|
+
*
|
|
15
|
+
* If the object is frozen, sealed, or otherwise non-extensible,
|
|
16
|
+
* a cloning function will be used to make an unlocked copy.
|
|
17
|
+
*
|
|
18
|
+
* @param {(object|function|string)} [opts] Options to customize the behaviour
|
|
19
|
+
*
|
|
20
|
+
* - If this is a `function` it will be used as the `opts.fn` value.
|
|
21
|
+
* - If this is a `string` it will be used as the `opts.method` value.
|
|
22
|
+
*
|
|
23
|
+
* @param {string} [opts.method='clone'] Object method to use
|
|
24
|
+
*
|
|
25
|
+
* Set to an empty string `""` to skip checking for an object method.
|
|
26
|
+
*
|
|
27
|
+
* If the method exists on the target object, it will be called to
|
|
28
|
+
* perform the cloning procedure, otherwise `opts.fn` will be used.
|
|
29
|
+
*
|
|
30
|
+
* @param {Array} [opts.args] Arguments for `opts.method` method
|
|
31
|
+
*
|
|
32
|
+
* If not specified, the default value is: `[opts]`
|
|
33
|
+
*
|
|
34
|
+
* @param {function} [opts.fn] A function to perform the clone operation
|
|
35
|
+
*
|
|
36
|
+
* Must take only a single parameter, which is the object to clone.
|
|
37
|
+
* Must return an extensible clone of the object.
|
|
38
|
+
*
|
|
39
|
+
* If for whatever reason you need the `opts` in the function,
|
|
40
|
+
* then use a unbound function, and `opts` will be available
|
|
41
|
+
* as `this` in the function body. Arrow functions (closures) are
|
|
42
|
+
* always considered bound and cannot have a `this` value assigned.
|
|
43
|
+
*
|
|
44
|
+
* The default value is a closure: `obj => Object.assign({}, obj)`;
|
|
45
|
+
*
|
|
46
|
+
* @return {object}
|
|
47
|
+
*
|
|
48
|
+
* @alias module:@lumjs/core/obj.unlocked
|
|
49
|
+
*/
|
|
50
|
+
function unlocked(obj, opts={})
|
|
51
|
+
{
|
|
52
|
+
needObj(obj, true, "invalid obj value");
|
|
53
|
+
|
|
54
|
+
if (Object.isExtensible(obj))
|
|
55
|
+
{ // We're done here.
|
|
56
|
+
return obj;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (typeof opts === F)
|
|
60
|
+
{
|
|
61
|
+
opts = {fn: opts}
|
|
62
|
+
}
|
|
63
|
+
else if (typeof opts === S)
|
|
64
|
+
{
|
|
65
|
+
opts = {method: opts}
|
|
66
|
+
}
|
|
67
|
+
else if (!isObj(opts))
|
|
68
|
+
{
|
|
69
|
+
console.error({obj, opts});
|
|
70
|
+
throw new TypeError("invalid opts value");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const fn = (typeof opts.fn === F) ? opts.fn : clone;
|
|
74
|
+
const meth = (typeof opts.method === S) ? opts.method.trim() : CLONE;
|
|
75
|
+
const args = (Array.isArray(opts.args)) ? opts.args : [opts];
|
|
76
|
+
|
|
77
|
+
if (meth && typeof obj[meth] === F)
|
|
78
|
+
{ // Use a clone method
|
|
79
|
+
return obj[meth](...args);
|
|
80
|
+
}
|
|
81
|
+
else
|
|
82
|
+
{ // Use a clone function
|
|
83
|
+
return fn.call(opts, obj);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = unlocked;
|
|
@@ -1,18 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A very simplistic Trait system.
|
|
3
|
-
* @module @lumjs/core/traits
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
"use strict";
|
|
7
2
|
|
|
8
|
-
const getProp = require('
|
|
9
|
-
|
|
3
|
+
const getProp = require('../obj/getproperty');
|
|
10
4
|
const
|
|
11
5
|
{
|
|
12
|
-
def,F,B,
|
|
6
|
+
def,F,B,needObj,
|
|
13
7
|
isObj,isArray,isConstructor,isProperty,isClassObject,
|
|
14
|
-
|
|
15
|
-
} = require('./types');
|
|
8
|
+
} = require('../types');
|
|
16
9
|
|
|
17
10
|
// Symbol for private storage of composed traits.
|
|
18
11
|
const COMPOSED_TRAITS = Symbol.for('@lumjs/core/traits~ComposedTraits');
|
|
@@ -568,254 +561,10 @@ function decompose(specTarget, specSource, opts={})
|
|
|
568
561
|
return c;
|
|
569
562
|
}
|
|
570
563
|
|
|
571
|
-
/**
|
|
572
|
-
* An abstract class for Traits.
|
|
573
|
-
*
|
|
574
|
-
* Simply offers a couple static methods and APIs that
|
|
575
|
-
* wrap the `compose()` and `composeFully()` functions,
|
|
576
|
-
* and makes it fairly simple to create Trait classes.
|
|
577
|
-
*
|
|
578
|
-
* @alias module:@lumjs/core/traits.Trait
|
|
579
|
-
*/
|
|
580
|
-
class CoreTrait
|
|
581
|
-
{
|
|
582
|
-
/**
|
|
583
|
-
* Extend another class or object instance with the methods and
|
|
584
|
-
* getter/setter properties from a Trait.
|
|
585
|
-
*
|
|
586
|
-
* The sub-class of Trait this static method is called on will always
|
|
587
|
-
* be the `source` argument.
|
|
588
|
-
*
|
|
589
|
-
* @param {(function|object)} target - Target class or instance.
|
|
590
|
-
*
|
|
591
|
-
* @param {object} [protoOpts] Options for `compose()` function.
|
|
592
|
-
*
|
|
593
|
-
* If this is not specified, or is any value other than an `object`,
|
|
594
|
-
* we will look for defaults in a `composeOptions` static property:
|
|
595
|
-
*
|
|
596
|
-
* ```js
|
|
597
|
-
* static get composeOptions() { return {your: default, options: here}; }
|
|
598
|
-
* ```
|
|
599
|
-
*
|
|
600
|
-
* @param {(object|true)} [staticOpts] Static options.
|
|
601
|
-
*
|
|
602
|
-
* If this is set we'll use `composeFully()` instead of using `compose()`.
|
|
603
|
-
*
|
|
604
|
-
* If this value is an `object` it will be used as the `staticOpts`.
|
|
605
|
-
*
|
|
606
|
-
* If this is the special value `true`, then we will look for the options
|
|
607
|
-
* in a `staticOptions` static property:
|
|
608
|
-
*
|
|
609
|
-
* ```js
|
|
610
|
-
* static get staticOptions() { return {your: static, options: here}; }
|
|
611
|
-
* ```
|
|
612
|
-
*
|
|
613
|
-
* If this any value other than an `object` or `true`, it will be ignored
|
|
614
|
-
* entirely, and the regular `compose()` call will be used.
|
|
615
|
-
*
|
|
616
|
-
* @returns {object} Return value from the `setupTrait()` static method.
|
|
617
|
-
*
|
|
618
|
-
*/
|
|
619
|
-
static composeInto(target, protoOpts, staticOpts)
|
|
620
|
-
{
|
|
621
|
-
if (!isObj(protoOpts))
|
|
622
|
-
{
|
|
623
|
-
protoOpts = this.composeOptions ?? {};
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
if (staticOpts === true)
|
|
627
|
-
{
|
|
628
|
-
staticOpts = this.staticOptions ?? {};
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
let composed;
|
|
632
|
-
|
|
633
|
-
if (isObj(staticOpts))
|
|
634
|
-
{
|
|
635
|
-
composed = composeFully(target, this, protoOpts, staticOpts);
|
|
636
|
-
}
|
|
637
|
-
else
|
|
638
|
-
{
|
|
639
|
-
composed = compose(target, this, protoOpts);
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
return this.setupTrait({target, protoOpts, staticOpts, composed});
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
/**
|
|
646
|
-
* A static method called by `composeInto()`
|
|
647
|
-
* _after_ composing the trait properties into the target.
|
|
648
|
-
*
|
|
649
|
-
* @param {object} info - Metadata from `composeInto()`
|
|
650
|
-
* @param {(function|object)} info.target - The `target` argument
|
|
651
|
-
* @param {object} info.protoOpts - The `protoOpts` used
|
|
652
|
-
* @param {object} [info.staticOpts] The `staticOpts` if used
|
|
653
|
-
* @param {module:@lumjs/core/traits~Composed} info.composed
|
|
654
|
-
* The return value from `compose()` or `composeFully()`.
|
|
655
|
-
*
|
|
656
|
-
* @returns {object} The `info` object, with any changes made
|
|
657
|
-
* by an overridden `setupTrait()` method in the sub-class.
|
|
658
|
-
*
|
|
659
|
-
* The default implementation is a placeholder that returns the
|
|
660
|
-
* `info` object without making any changes.
|
|
661
|
-
*
|
|
662
|
-
*/
|
|
663
|
-
static setupTrait(info)
|
|
664
|
-
{
|
|
665
|
-
if (this.debug)
|
|
666
|
-
{
|
|
667
|
-
console.debug(this.name, "setupTrait()", info, this);
|
|
668
|
-
}
|
|
669
|
-
return info;
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
/**
|
|
673
|
-
* A method wrapping {@link module:@lumjs/core/traits.decompose}
|
|
674
|
-
* where the `source` is always the Trait sub-class constructor.
|
|
675
|
-
*
|
|
676
|
-
* See the `decompose()` docs for descriptions of the other arguments.
|
|
677
|
-
*
|
|
678
|
-
* @param {(function|object)} target
|
|
679
|
-
* @param {object} [opts]
|
|
680
|
-
* @returns {object} Return value from the `removedTrait()` static method.
|
|
681
|
-
*/
|
|
682
|
-
static decomposeFrom(target, opts)
|
|
683
|
-
{
|
|
684
|
-
const info = {target, ok:true};
|
|
685
|
-
info.composed = this.getComposed(target);
|
|
686
|
-
this.removeTrait(info);
|
|
687
|
-
|
|
688
|
-
if (info.ok)
|
|
689
|
-
{
|
|
690
|
-
info.count = decompose(target, this, opts);
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
return this.removedTrait(info);
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
/**
|
|
697
|
-
* A static method called by `decomposeFrom()`
|
|
698
|
-
* _before_ decomposing the trait properties from the target.
|
|
699
|
-
*
|
|
700
|
-
* @param {object} info - Metadata from `decomposeFrom()`
|
|
701
|
-
* @param {(function|object)} info.target - The `target` argument
|
|
702
|
-
* @param {module:@lumjs/core/traits~Composed} info.composed
|
|
703
|
-
* The property map that was previously composed.
|
|
704
|
-
* @param {boolean} info.ok - Will always be `true` initially.
|
|
705
|
-
*
|
|
706
|
-
* If an overridden `removeTrait()` method sets this to `false`,
|
|
707
|
-
* then the decomposeFrom() operation will skip the step of
|
|
708
|
-
* actually decomposing the trait.
|
|
709
|
-
*
|
|
710
|
-
* @returns {*} Return value is not used.
|
|
711
|
-
*/
|
|
712
|
-
static removeTrait(info)
|
|
713
|
-
{
|
|
714
|
-
if (this.debug)
|
|
715
|
-
{
|
|
716
|
-
console.debug(this.name, "removeTrait()", info, this);
|
|
717
|
-
}
|
|
718
|
-
return info;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
/**
|
|
722
|
-
* A static method called by `decomposeFrom()`
|
|
723
|
-
* _after_ decomposing the trait properties from the target.
|
|
724
|
-
*
|
|
725
|
-
* @param {object} info - The same as `removeTrait()`, plus:
|
|
726
|
-
* @param {number} info.count - The number of properties decomposed;
|
|
727
|
-
*
|
|
728
|
-
* @returns {object} The `info` object, with any changes made
|
|
729
|
-
* by `removeTrait()` and `removedTrait()` methods in the
|
|
730
|
-
* sub-class.
|
|
731
|
-
*
|
|
732
|
-
* The default implementation is a placeholder that returns the
|
|
733
|
-
* `info` object without making any changes.
|
|
734
|
-
*
|
|
735
|
-
*/
|
|
736
|
-
static removedTrait(info)
|
|
737
|
-
{
|
|
738
|
-
if (this.debug)
|
|
739
|
-
{
|
|
740
|
-
console.debug(this.name, "removedTrait()", info, this);
|
|
741
|
-
}
|
|
742
|
-
return info;
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
/**
|
|
746
|
-
* A method wrapping {@link module:@lumjs/core/traits.getComposed}
|
|
747
|
-
* where the `source` is always the Trait sub-class constructor.
|
|
748
|
-
*
|
|
749
|
-
* @param {(function|object)} target
|
|
750
|
-
* @returns {mixed} Return value from `getComposed()`
|
|
751
|
-
*/
|
|
752
|
-
static getComposed(target)
|
|
753
|
-
{
|
|
754
|
-
return getComposed(target, this);
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
} // CoreTrait class
|
|
758
|
-
|
|
759
|
-
/**
|
|
760
|
-
* Build a Trait registry.
|
|
761
|
-
*
|
|
762
|
-
* @param {object} registry - Object for the registry.
|
|
763
|
-
*
|
|
764
|
-
* Generally the `exports` from a Node.js module would be good here.
|
|
765
|
-
*
|
|
766
|
-
* It will have a `Trait` property added, which is an alias to
|
|
767
|
-
* the `Trait` class constructor.
|
|
768
|
-
*
|
|
769
|
-
* It will also have a `registerTrait(name, value)` function added.
|
|
770
|
-
* This function will add new traits to the registry, using
|
|
771
|
-
* the `name` as its property key. The `value` is either the
|
|
772
|
-
* Trait sub-class constructor `function` itself, or a _lazy-loading_
|
|
773
|
-
* closure `function` that must load and return the actual sub-class
|
|
774
|
-
* constructor when executed. The registry DOES NOT support `object`
|
|
775
|
-
* type traits, or any class that doesn't extend the `Trait` class.
|
|
776
|
-
*
|
|
777
|
-
* @returns {function} The `registerTrait()` function created above.
|
|
778
|
-
* @alias module:@lumjs/core/traits.makeRegistry
|
|
779
|
-
*/
|
|
780
|
-
function makeTraitRegistry(registry={})
|
|
781
|
-
{
|
|
782
|
-
needObj(registry, false, 'invalid trait registry object');
|
|
783
|
-
|
|
784
|
-
def(registry, 'Trait', CoreTrait);
|
|
785
|
-
|
|
786
|
-
function registerTrait(name, value)
|
|
787
|
-
{
|
|
788
|
-
needType(S, name, 'invalid trait name');
|
|
789
|
-
needType(F, value, 'invalid trait loader value');
|
|
790
|
-
|
|
791
|
-
if (registry[name] !== undefined)
|
|
792
|
-
{
|
|
793
|
-
console.error("trait already registered", {name,value,registry});
|
|
794
|
-
return;
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
if (CoreTrait.isPrototypeOf(value))
|
|
798
|
-
{ // Make it available directly.
|
|
799
|
-
def(registry, name, value, def.e);
|
|
800
|
-
}
|
|
801
|
-
else
|
|
802
|
-
{ // Lazy-loading engaged.
|
|
803
|
-
def.lazy(registry, name, value, def.e);
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
def(registry, 'registerTrait', registerTrait);
|
|
808
|
-
|
|
809
|
-
return registerTrait;
|
|
810
|
-
}
|
|
811
|
-
|
|
812
564
|
module.exports =
|
|
813
565
|
{
|
|
814
566
|
compose, composeFully, getComposed, decompose,
|
|
815
|
-
|
|
816
|
-
ensureProto, ensureConstructor,
|
|
817
|
-
makeRegistry: makeTraitRegistry,
|
|
818
|
-
|
|
567
|
+
IGNORE_STATIC, ensureProto, ensureConstructor,
|
|
819
568
|
// Undocumented:
|
|
820
569
|
hasOwn,
|
|
821
570
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A very simplistic Trait system.
|
|
3
|
+
* @module @lumjs/core/traits
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
const funcs = require('./funcs');
|
|
9
|
+
const Trait = require('./trait');
|
|
10
|
+
const regfns = require('./registry');
|
|
11
|
+
|
|
12
|
+
// Export all the things
|
|
13
|
+
Object.assign(exports, funcs, regfns, {Trait});
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {def,F,S,needObj,needType} = require('../types');
|
|
4
|
+
const Trait = require('./trait');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get a trait from the specified registry
|
|
8
|
+
*
|
|
9
|
+
* The exported function version of this is not generally called directly.
|
|
10
|
+
* Instead use the bound `getTrait()` method of a registry object.
|
|
11
|
+
*
|
|
12
|
+
* @alias module:@lumjs/core/traits.getTrait
|
|
13
|
+
*
|
|
14
|
+
* @param {module:@lumjs/core/traits~Registry} registry
|
|
15
|
+
* @param {(string|function)} trait - Trait to get
|
|
16
|
+
*
|
|
17
|
+
* This should almost always be the name of the trait you want
|
|
18
|
+
* to get out of this registry.
|
|
19
|
+
*
|
|
20
|
+
* If this happens to be a Trait class constructor already,
|
|
21
|
+
* it will be returned as is.
|
|
22
|
+
*
|
|
23
|
+
* @returns {?function} A trait class constructor, or `null` if the
|
|
24
|
+
* specified trait name does not exist in the registry.
|
|
25
|
+
*/
|
|
26
|
+
function getTrait(registry,trait)
|
|
27
|
+
{
|
|
28
|
+
if (typeof trait === S)
|
|
29
|
+
{ // Assume a string is the name of a trait in this registry
|
|
30
|
+
trait = registry[trait];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (typeof trait === F && Trait.isPrototypeOf(trait))
|
|
34
|
+
{
|
|
35
|
+
return trait;
|
|
36
|
+
}
|
|
37
|
+
else
|
|
38
|
+
{
|
|
39
|
+
console.error("invalid trait", {trait});
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get a bunch of traits from the specified registry
|
|
46
|
+
*
|
|
47
|
+
* The exported function version of this is not generally called directly.
|
|
48
|
+
* Instead use the bound `getTraits()` method from a registry object.
|
|
49
|
+
*
|
|
50
|
+
* @alias module:@lumjs/core/traits.getTraits
|
|
51
|
+
*
|
|
52
|
+
* @param {module:@lumjs/core/traits~Registry} registry
|
|
53
|
+
* @param {Iterable<(string|function)>} inList - List of traits to get
|
|
54
|
+
*
|
|
55
|
+
* Every item from this list will be passed to `getTrait()` to get
|
|
56
|
+
* the Trait class constructors for the returned set.
|
|
57
|
+
*
|
|
58
|
+
* @param {Set} [outSet=new Set] Will be populated with requested traits
|
|
59
|
+
*
|
|
60
|
+
* @returns {Set} The `outSet` with all traits added to it
|
|
61
|
+
*/
|
|
62
|
+
function getTraits(registry, inList, outSet)
|
|
63
|
+
{
|
|
64
|
+
if (!outSet)
|
|
65
|
+
{
|
|
66
|
+
outSet=new Set();
|
|
67
|
+
}
|
|
68
|
+
else if (!(outSet instanceof Set))
|
|
69
|
+
{
|
|
70
|
+
console.error({inList, outSet});
|
|
71
|
+
throw new TypeError("Invalid Set object");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
for (const ti of inList)
|
|
75
|
+
{
|
|
76
|
+
const tc = getTrait(registry, ti);
|
|
77
|
+
if (tc)
|
|
78
|
+
{
|
|
79
|
+
outSet.add(tc);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return outSet;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Register a trait class in a registry
|
|
88
|
+
*
|
|
89
|
+
* The exported function version of this is not generally called directly.
|
|
90
|
+
* Instead use the bound `registerTrait()` method from a registry object.
|
|
91
|
+
*
|
|
92
|
+
* @function module:@lumjs/core/traits.registerTrait
|
|
93
|
+
*
|
|
94
|
+
* @param {module:@lumjs/core/traits~Registry} registry
|
|
95
|
+
* @param {string} name - Name to use for the trait in the registry
|
|
96
|
+
* @param {function} getTrait
|
|
97
|
+
*
|
|
98
|
+
* If the function passed is a class constructor with the `Trait`
|
|
99
|
+
* class in its prototype chain, it will be used as the trait itself.
|
|
100
|
+
*
|
|
101
|
+
* If it is just a regular function or closure, it will be used as
|
|
102
|
+
* a lazy-loader that must return the actual class constructor.
|
|
103
|
+
*
|
|
104
|
+
* @param {boolean} [overwrite=false]
|
|
105
|
+
*
|
|
106
|
+
* @returns {module:@lumjs/core/traits~Registry} The registry object
|
|
107
|
+
*
|
|
108
|
+
* As a special exception to the argument type checking, if you omit both
|
|
109
|
+
* arguments, then the registry object will be returned immediately.
|
|
110
|
+
*
|
|
111
|
+
* @throws {TypeError} If `name` or `getTrait` were not valid values
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
function registerTrait(registry, name, getTrait, overwrite=false)
|
|
115
|
+
{
|
|
116
|
+
if (name === undefined && getTrait === undefined)
|
|
117
|
+
{ // A special case, return the registry object
|
|
118
|
+
return registry;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
needType(S, name, 'invalid trait name');
|
|
122
|
+
needType(F, getTrait, 'invalid trait loader value');
|
|
123
|
+
|
|
124
|
+
if (!overwrite && registry[name] !== undefined)
|
|
125
|
+
{
|
|
126
|
+
console.error("trait already registered", {name,getTrait,registry});
|
|
127
|
+
return registry;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (Trait.isPrototypeOf(getTrait))
|
|
131
|
+
{ // Make it available directly.
|
|
132
|
+
def(registry, name, getTrait, def.e);
|
|
133
|
+
}
|
|
134
|
+
else
|
|
135
|
+
{ // Lazy-loading engaged.
|
|
136
|
+
def.lazy(registry, name, getTrait, def.e);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return registry;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Build a Trait registry.
|
|
144
|
+
*
|
|
145
|
+
* @param {object} [registry={}] Object for the registry
|
|
146
|
+
*
|
|
147
|
+
* Generally the `exports` from a Node.js module would be good here.
|
|
148
|
+
* The object will have several properties and methods assigned to it
|
|
149
|
+
* see {@link module:@lumjs/core/traits~Registry} for details.
|
|
150
|
+
*
|
|
151
|
+
* @returns {function} A
|
|
152
|
+
* @alias module:@lumjs/core/traits.makeRegistry
|
|
153
|
+
*/
|
|
154
|
+
function makeTraitRegistry(registry={})
|
|
155
|
+
{
|
|
156
|
+
needObj(registry, false, 'invalid trait registry object');
|
|
157
|
+
|
|
158
|
+
def(registry, 'Trait', Trait);
|
|
159
|
+
def(registry, 'getTrait', getTrait.bind(registry, registry));
|
|
160
|
+
def(registry, 'getTraits', getTraits.bind(registry, registry));
|
|
161
|
+
def(registry, 'registerTrait', registerTrait.bind(registry, registry));
|
|
162
|
+
|
|
163
|
+
return registerTrait;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
module.exports = makeTraitRegistry;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {isObj} = require('../types');
|
|
4
|
+
const {getComposed,decompose,compose,composeFully} = require('./funcs');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* An abstract class for Traits.
|
|
8
|
+
*
|
|
9
|
+
* Simply offers a couple static methods and APIs that
|
|
10
|
+
* wrap the `compose()` and `composeFully()` functions,
|
|
11
|
+
* and makes it fairly simple to create Trait classes.
|
|
12
|
+
*
|
|
13
|
+
* @alias module:@lumjs/core/traits.Trait
|
|
14
|
+
*/
|
|
15
|
+
class CoreTrait
|
|
16
|
+
{
|
|
17
|
+
/**
|
|
18
|
+
* Extend another class or object instance with the methods and
|
|
19
|
+
* getter/setter properties from a Trait.
|
|
20
|
+
*
|
|
21
|
+
* The sub-class of Trait this static method is called on will always
|
|
22
|
+
* be the `source` argument.
|
|
23
|
+
*
|
|
24
|
+
* @param {(function|object)} target - Target class or instance.
|
|
25
|
+
*
|
|
26
|
+
* @param {object} [protoOpts] Options for `compose()` function.
|
|
27
|
+
*
|
|
28
|
+
* If this is not specified, or is any value other than an `object`,
|
|
29
|
+
* we will look for defaults in a `composeOptions` static property:
|
|
30
|
+
*
|
|
31
|
+
* ```js
|
|
32
|
+
* static get composeOptions() { return {your: default, options: here}; }
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @param {(object|true)} [staticOpts] Static options.
|
|
36
|
+
*
|
|
37
|
+
* If this is set we'll use `composeFully()` instead of using `compose()`.
|
|
38
|
+
*
|
|
39
|
+
* If this value is an `object` it will be used as the `staticOpts`.
|
|
40
|
+
*
|
|
41
|
+
* If this is the special value `true`, then we will look for the options
|
|
42
|
+
* in a `staticOptions` static property:
|
|
43
|
+
*
|
|
44
|
+
* ```js
|
|
45
|
+
* static get staticOptions() { return {your: static, options: here}; }
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* If this any value other than an `object` or `true`, it will be ignored
|
|
49
|
+
* entirely, and the regular `compose()` call will be used.
|
|
50
|
+
*
|
|
51
|
+
* @returns {object} Return value from the `setupTrait()` static method.
|
|
52
|
+
*
|
|
53
|
+
*/
|
|
54
|
+
static composeInto(target, protoOpts, staticOpts)
|
|
55
|
+
{
|
|
56
|
+
if (!isObj(protoOpts))
|
|
57
|
+
{
|
|
58
|
+
protoOpts = this.composeOptions ?? {};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (staticOpts === true)
|
|
62
|
+
{
|
|
63
|
+
staticOpts = this.staticOptions ?? {};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let composed;
|
|
67
|
+
|
|
68
|
+
if (isObj(staticOpts))
|
|
69
|
+
{
|
|
70
|
+
composed = composeFully(target, this, protoOpts, staticOpts);
|
|
71
|
+
}
|
|
72
|
+
else
|
|
73
|
+
{
|
|
74
|
+
composed = compose(target, this, protoOpts);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return this.setupTrait({target, protoOpts, staticOpts, composed});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* A static method called by `composeInto()`
|
|
82
|
+
* _after_ composing the trait properties into the target.
|
|
83
|
+
*
|
|
84
|
+
* @param {object} info - Metadata from `composeInto()`
|
|
85
|
+
* @param {(function|object)} info.target - The `target` argument
|
|
86
|
+
* @param {object} info.protoOpts - The `protoOpts` used
|
|
87
|
+
* @param {object} [info.staticOpts] The `staticOpts` if used
|
|
88
|
+
* @param {module:@lumjs/core/traits~Composed} info.composed
|
|
89
|
+
* The return value from `compose()` or `composeFully()`.
|
|
90
|
+
*
|
|
91
|
+
* @returns {object} The `info` object, with any changes made
|
|
92
|
+
* by an overridden `setupTrait()` method in the sub-class.
|
|
93
|
+
*
|
|
94
|
+
* The default implementation is a placeholder that returns the
|
|
95
|
+
* `info` object without making any changes.
|
|
96
|
+
*
|
|
97
|
+
*/
|
|
98
|
+
static setupTrait(info)
|
|
99
|
+
{
|
|
100
|
+
if (this.debug)
|
|
101
|
+
{
|
|
102
|
+
console.debug(this.name, "setupTrait()", info, this);
|
|
103
|
+
}
|
|
104
|
+
return info;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* A method wrapping {@link module:@lumjs/core/traits.decompose}
|
|
109
|
+
* where the `source` is always the Trait sub-class constructor.
|
|
110
|
+
*
|
|
111
|
+
* See the `decompose()` docs for descriptions of the other arguments.
|
|
112
|
+
*
|
|
113
|
+
* @param {(function|object)} target
|
|
114
|
+
* @param {object} [opts]
|
|
115
|
+
* @returns {object} Return value from the `removedTrait()` static method.
|
|
116
|
+
*/
|
|
117
|
+
static decomposeFrom(target, opts)
|
|
118
|
+
{
|
|
119
|
+
const info = {target, ok:true};
|
|
120
|
+
info.composed = this.getComposed(target);
|
|
121
|
+
this.removeTrait(info);
|
|
122
|
+
|
|
123
|
+
if (info.ok)
|
|
124
|
+
{
|
|
125
|
+
info.count = decompose(target, this, opts);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return this.removedTrait(info);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* A static method called by `decomposeFrom()`
|
|
133
|
+
* _before_ decomposing the trait properties from the target.
|
|
134
|
+
*
|
|
135
|
+
* @param {object} info - Metadata from `decomposeFrom()`
|
|
136
|
+
* @param {(function|object)} info.target - The `target` argument
|
|
137
|
+
* @param {module:@lumjs/core/traits~Composed} info.composed
|
|
138
|
+
* The property map that was previously composed.
|
|
139
|
+
* @param {boolean} info.ok - Will always be `true` initially.
|
|
140
|
+
*
|
|
141
|
+
* If an overridden `removeTrait()` method sets this to `false`,
|
|
142
|
+
* then the decomposeFrom() operation will skip the step of
|
|
143
|
+
* actually decomposing the trait.
|
|
144
|
+
*
|
|
145
|
+
* @returns {*} Return value is not used.
|
|
146
|
+
*/
|
|
147
|
+
static removeTrait(info)
|
|
148
|
+
{
|
|
149
|
+
if (this.debug)
|
|
150
|
+
{
|
|
151
|
+
console.debug(this.name, "removeTrait()", info, this);
|
|
152
|
+
}
|
|
153
|
+
return info;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* A static method called by `decomposeFrom()`
|
|
158
|
+
* _after_ decomposing the trait properties from the target.
|
|
159
|
+
*
|
|
160
|
+
* @param {object} info - The same as `removeTrait()`, plus:
|
|
161
|
+
* @param {number} info.count - The number of properties decomposed;
|
|
162
|
+
*
|
|
163
|
+
* @returns {object} The `info` object, with any changes made
|
|
164
|
+
* by `removeTrait()` and `removedTrait()` methods in the
|
|
165
|
+
* sub-class.
|
|
166
|
+
*
|
|
167
|
+
* The default implementation is a placeholder that returns the
|
|
168
|
+
* `info` object without making any changes.
|
|
169
|
+
*
|
|
170
|
+
*/
|
|
171
|
+
static removedTrait(info)
|
|
172
|
+
{
|
|
173
|
+
if (this.debug)
|
|
174
|
+
{
|
|
175
|
+
console.debug(this.name, "removedTrait()", info, this);
|
|
176
|
+
}
|
|
177
|
+
return info;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* A method wrapping {@link module:@lumjs/core/traits.getComposed}
|
|
182
|
+
* where the `source` is always the Trait sub-class constructor.
|
|
183
|
+
*
|
|
184
|
+
* @param {(function|object)} target
|
|
185
|
+
* @returns {mixed} Return value from `getComposed()`
|
|
186
|
+
*/
|
|
187
|
+
static getComposed(target)
|
|
188
|
+
{
|
|
189
|
+
return getComposed(target, this);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
} // CoreTrait class
|
|
193
|
+
|
|
194
|
+
module.exports = CoreTrait;
|