Skip to content

Commit b9c5951

Browse files
author
craigparra
committed
initial lifecycle complete - @craigparra
1 parent 74b23a2 commit b9c5951

File tree

3 files changed

+174
-18
lines changed

3 files changed

+174
-18
lines changed

Application.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ module.exports = class Application {
1212
if (applicationContext.constructor.name !== 'ApplicationContext') {
1313
applicationContext = new ApplicationContext(options);
1414
}
15-
// if (!applicationContext?.config) {
16-
// applicationContext.config = options.config;
17-
// }
1815
applicationContext.lifeCycle();
1916
return applicationContext;
2017
}

ApplicationContext.js

Lines changed: 173 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,31 @@ module.exports = class ApplicationContext {
88

99
static DEFAULT_CONTEXT_NAME = 'default';
1010
static DEFAULT_CONFIG_CONTEXT_PATH = 'context';
11+
12+
static getGlobalRef() {
13+
let $globalref = null;
14+
if (ApplicationContext.detectBrowser()) {
15+
$globalref = window;
16+
} else {
17+
$globalref = global;
18+
}
19+
return $globalref;
20+
}
21+
22+
static getGlobalRoot(key) {
23+
const $globalref = ApplicationContext.getGlobalRef();
24+
let $key = ($globalref && $globalref.boot);
25+
$key = $key && $key.contexts;
26+
$key = $key && $key.root;
27+
$key = $key && $key[`${key}`];
28+
return $key;
29+
}
30+
31+
static detectBrowser() {
32+
const browser = !(typeof window === 'undefined');
33+
return browser;
34+
}
35+
1136
constructor(options) {
1237
let contexts = options?.contexts || options;
1338
this.contexts = _.isArray(contexts) ? contexts: (contexts ? [contexts] : []);
@@ -27,12 +52,14 @@ module.exports = class ApplicationContext {
2752
}
2853
}
2954

30-
lifeCycle() {
55+
async lifeCycle() {
3156
logger.verbose(`ApplicationContext (${this.name}) lifecycle started.`);
32-
this.parseContexts(this.contexts, this.components, this.profiles, this.name);
33-
this.createSingletons(this.components);
34-
this.injectSingletonDependencies(this.components);
35-
logger.verbose(`ApplicationContext (${this.name}) lifecycle completed.`);
57+
this.parseContexts();
58+
this.createSingletons();
59+
this.injectSingletonDependencies();
60+
this.initialiseSingletons();
61+
this.registerSingletonDestroyers();
62+
this.run();
3663
}
3764

3865
detectConfigContext(){
@@ -46,6 +73,39 @@ module.exports = class ApplicationContext {
4673
logger.verbose('Detecting config contexts completed.');
4774
}
4875

76+
detectGlobalContextComponents(){
77+
logger.verbose('Detecting global context components started.');
78+
79+
if (!this.components['config'] && ApplicationContext.getGlobalRoot('config')){
80+
this.deriveContextComponent({
81+
Reference:ApplicationContext.getGlobalRoot('config'),
82+
name:'config'})
83+
}
84+
if (!this.components['loggerFactory'] && ApplicationContext.getGlobalRoot('loggerFactory')){
85+
this.deriveContextComponent({
86+
Reference:ApplicationContext.getGlobalRoot('loggerFactory'),
87+
name:'loggerFactory'})
88+
}
89+
if (!this.components['loggerCategoryCache'] && ApplicationContext.getGlobalRoot('loggerCategoryCache')){
90+
this.deriveContextComponent({
91+
Reference:ApplicationContext.getGlobalRoot('loggerCategoryCache'),
92+
name:'loggerCategoryCache'})
93+
}
94+
if (!this.components['logger']){
95+
this.deriveContextComponent({
96+
scope : Scopes.PROTOTYPE,
97+
wireFactory:'loggerFactory',
98+
name:'logger'})
99+
}
100+
if (!this.components['fetch'] && ApplicationContext.getGlobalRoot('fetch')){
101+
this.deriveContextComponent({
102+
Reference:ApplicationContext.getGlobalRoot('fetch'),
103+
name:'fetch'})
104+
}
105+
106+
logger.verbose('Detecting global context components completed.');
107+
}
108+
49109
parseContexts() {
50110
logger.verbose('Parsing configured contexts started.');
51111
this.detectConfigContext();
@@ -62,6 +122,7 @@ module.exports = class ApplicationContext {
62122
throw msg;
63123
}
64124
}
125+
this.detectGlobalContextComponents();
65126
logger.verbose('Parsing configured contexts completed.');
66127
}
67128

@@ -78,6 +139,7 @@ module.exports = class ApplicationContext {
78139
}
79140
}
80141
}
142+
81143
parseContextComponents(context) {
82144
logger.verbose('Processing context components started');
83145
if (context.components) {
@@ -159,6 +221,12 @@ module.exports = class ApplicationContext {
159221
if (component.scope === Scopes.SINGLETON) {
160222
if (component.isClass) {
161223
component.instance = new component.Reference();
224+
} else if (typeof component.factory === 'function') {
225+
let args = component.factoryArgs;
226+
if (!Array.isArray(args)) {
227+
args = [args];
228+
}
229+
component.instance = new component.factory(...args);
162230
} else {
163231
component.instance = component.Reference;
164232
}
@@ -182,10 +250,10 @@ module.exports = class ApplicationContext {
182250
const property = instance[insKeys[j]];
183251
const autowire = property?.name === 'Autowired' || _.lowerCase(property) === 'autowired';
184252
if (autowire) {
185-
instance[insKeys[j]] = this.get(insKeys[j]);
253+
instance[insKeys[j]] = this.get(insKeys[j],undefined,component);
186254
logger.verbose(`Explicitly autowired component (${component.name}) property (${insKeys[j]}) from context.`);
187255
} else if (instance[insKeys[j]] == null) {
188-
instance[insKeys[j]] = this.get(insKeys[j], instance[insKeys[j]]);
256+
instance[insKeys[j]] = this.get(insKeys[j], instance[insKeys[j]], component);
189257
if (instance[insKeys[j]] != null){
190258
logger.verbose(`Implicitly autowired null component (${component.name}) property (${insKeys[j]}) from context.`);
191259
}
@@ -212,7 +280,7 @@ module.exports = class ApplicationContext {
212280
}
213281
if (typeof property.name === 'string') {
214282
if (typeof property.reference) {
215-
component.instance[property.name] = this.get(property.reference);
283+
component.instance[property.name] = this.get(property.reference, undefined, component);
216284
logger.verbose(`Explicitly wired component (${component.name}) property (${property.name}) with context reference (${property.reference}).`);
217285
}
218286
if (property.value) {
@@ -223,10 +291,6 @@ module.exports = class ApplicationContext {
223291
component.instance[property.name] = this.config.get(property.path, property.defaultValue);
224292
logger.verbose(`Explicitly wired component (${component.name}) property (${property.name}) from config path (${property.path}).`);
225293
}
226-
if (property.factory && property.function) {
227-
component.instance[property.name] = this.get(property.path)[property.method](property.args);
228-
logger.verbose(`Explicitly wired component (${component.name}) property (${property.name}) from context factory function (${property.function}.${factory.function}).`);
229-
}
230294
}
231295
}
232296

@@ -254,7 +318,71 @@ module.exports = class ApplicationContext {
254318
logger.verbose('Injecting singleton dependencies completed');
255319
}
256320

257-
get(reference, defaultValue) {
321+
initialiseSingletons(){
322+
logger.verbose('Initialising singletons started');
323+
const keys = Object.keys(this.components);
324+
for (let i = 0; i < keys.length; i++) {
325+
const component = this.components[keys[i]];
326+
if (component.scope === Scopes.SINGLETON) {
327+
if (typeof component.instance.init === 'function') {
328+
component.instance.init();
329+
} else if (typeof component.init === 'string') {
330+
component.instance[component.init]();
331+
}
332+
logger.verbose(`Initialised singleton (${component.name})`);
333+
}
334+
}
335+
logger.verbose('Initialising singletons completed');
336+
}
337+
338+
registerDestroyer(){
339+
process.on('exit', destroyer.bind());
340+
//catches ctrl+c event
341+
process.on('SIGINT', destroyer.bind());
342+
// catches "kill pid" (for example: nodemon restart)
343+
process.on('SIGUSR1', destroyer.bind());
344+
process.on('SIGUSR2', destroyer.bind());
345+
//catches uncaught exceptions
346+
process.on('uncaughtException', destroyer.bind());
347+
}
348+
async registerSingletonDestroyers(){
349+
logger.verbose('Registering singleton destroyers started');
350+
const keys = Object.keys(this.components);
351+
for (let i = 0; i < keys.length; i++) {
352+
const component = this.components[keys[i]];
353+
if (component.scope === Scopes.SINGLETON) {
354+
let destroyer = null;
355+
if (typeof component.instance.destroy === 'function') {
356+
destroyer = component.instance.destroy;
357+
} else if (typeof component.destroy === 'string') {
358+
destroyer = component.instance[component.destroy]();
359+
}
360+
this.registerDestroyer(destroyer);
361+
this.registerDestroyer(() => {
362+
logger.verbose(`ApplicationContext (${this.name}) lifecycle completed.`);
363+
});
364+
365+
logger.verbose(`Registering singleton (${component.name}) destroyer`);
366+
}
367+
}
368+
369+
logger.verbose('Registering singleton destroyers completed');
370+
}
371+
async run (){
372+
const keys = Object.keys(this.components);
373+
for (let i = 0; i < keys.length; i++) {
374+
const component = this.components[keys[i]];
375+
if (component.scope === Scopes.SINGLETON) {
376+
if (typeof component.instance.run === 'function') {
377+
component.instance.run();
378+
} else if (typeof component.run === 'string') {
379+
component.instance[component.run]();
380+
}
381+
}
382+
}
383+
logger.verbose('Application context started');
384+
}
385+
get(reference, defaultValue, targetArgs) {
258386
if (this.components[reference]) {
259387
logger.verbose(`Found component (${reference})`);
260388
if (this.components[reference].scope === Scopes.SINGLETON) {
@@ -265,7 +393,38 @@ module.exports = class ApplicationContext {
265393
if (this.components[reference].isClass) {
266394
logger.verbose(`Component (${reference}) is scoped as (${Scopes.PROTOTYPE}), returning new instance.`);
267395
prototype = new this.components[reference].Reference();
268-
} else {
396+
} else if (typeof this.components[reference] === 'function') {
397+
let args = targetArgs || this.components[reference].factoryArgs;
398+
if (!Array.isArray(args)){
399+
args = [args];
400+
}
401+
prototype = new this.components[reference](...args);
402+
} else if (typeof this.components['factory'] === 'function') {
403+
let args = this.components[reference].factoryArgs;
404+
if (!Array.isArray(args)) {
405+
args = [args];
406+
}
407+
prototype = this.components[reference](...args);
408+
} else if (typeof this.components['factory'] === 'string' && typeof this.components['factoryFunction'] === 'string') {
409+
let args = this.components[reference]['factoryArgs'];
410+
if (!Array.isArray(args)) {
411+
args = [args];
412+
}
413+
prototype = this.get(this.components['factory'])[this.components['factoryFunction']](...args);
414+
} else if (typeof this.components['wireFactory'] === 'function') {
415+
let args = targetArgs;
416+
if (!Array.isArray(args)) {
417+
args = [args];
418+
}
419+
prototype = this.components['wireFactory'](...args);
420+
} else if (typeof this.components['wireFactory'] === 'string') {
421+
let args = targetArgs;
422+
if (!Array.isArray(args)) {
423+
args = [args];
424+
}
425+
prototype = this.get(this.components['wireFactory'])(...args);
426+
}
427+
else {
269428
logger.verbose(`Component (${reference}) is scoped as (${Scopes.PROTOTYPE}), returning deep clone.`);
270429
prototype = _.cloneDeep(this.components[reference].Reference);
271430
}

config/local-development.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"^^^ set a format to json for JSON output" : "doco",
55
"level": {
66
"/" : "info",
7-
"@alt-javascript/contexts" : "info",
7+
"@alt-javascript/contexts" : "verbose",
88
"^^^ set a path level to enable log lines at that folder, down to file/module" : "doco"
99
},
1010
"test" : {

0 commit comments

Comments
 (0)