refactor: gathering

This commit is contained in:
cha0s 2024-01-28 11:09:34 -06:00
parent 97536387e6
commit 4d17a99c1b
8 changed files with 150 additions and 78 deletions

View File

@ -48,7 +48,6 @@ program
action,
args = [],
description,
help,
name = keys[i],
options = [],
} = commands[keys[i]];

View File

@ -40,9 +40,6 @@ const capitalize = (string) => string.substring(0, 1).toUpperCase() + string.sub
*/
const camelCase = (string) => string.split(/[_-]/).map(capitalize).join('');
// Track gathered for HMR.
const hotGathered = new Map();
// Wrap classes to expose their flecks ID and type.
const wrapGathered = (Class, id, idProperty, type, typeProperty) => {
class Subclass extends Class {
@ -67,6 +64,8 @@ exports.Flecks = class Flecks {
flecks = {};
$$gathered = {};
hooks = {};
/**
@ -184,7 +183,9 @@ exports.Flecks = class Flecks {
* Destroy this instance.
*/
destroy() {
this.$$expandedFlecksCache = {};
this.config = {};
this.$$gathered = {};
this.hooks = {};
this.flecks = {};
}
@ -366,7 +367,9 @@ exports.Flecks = class Flecks {
.map(([path, {mixin}]) => [path, mixin]).filter(([, mixin]) => mixin);
debugSilly('mixins: %O', mixinDescription.map(([path]) => path));
const Flecks = compose(...mixinDescription.map(([, mixin]) => mixin))(this);
return new Flecks(runtime);
const instance = new Flecks(runtime);
await instance.gatherHooks();
return instance;
}
/**
@ -414,20 +417,35 @@ exports.Flecks = class Flecks {
[exports.ById]: ids,
[exports.ByType]: types,
};
// Register for HMR?
hotGathered.set(
hook,
{
check,
idProperty,
typeProperty,
gathered,
},
);
this.$$gathered[hook] = {
check,
idProperty,
typeProperty,
gathered,
};
debug("gathered '%s': %O", hook, Object.keys(gathered[exports.ByType]));
return gathered;
}
gathered(type) {
return this.$$gathered[type]?.gathered;
}
async gatherHooks() {
const gathering = await this.invokeAsync('@flecks/core.gathered');
await Promise.all(
Object.entries(gathering)
.map(([fleck, gathering]) => (
Promise.all(
Object.entries(gathering)
.map(([type, options]) => (
this.gather(`${fleck}.${type}`, options)
)),
)
)),
);
}
/**
* Get a configuration value.
*
@ -814,48 +832,46 @@ exports.Flecks = class Flecks {
*
* @param {string} fleck
*/
refreshGathered(fleck) {
const it = hotGathered.entries();
for (let current = it.next(); current.done !== true; current = it.next()) {
const {
value: [
hook,
{
check,
idProperty,
gathered,
typeProperty,
},
],
} = current;
let raw;
// If decorating, gather all again
if (this.fleckImplementation(fleck, `${hook}.decorate`)) {
raw = this.invokeMerge(hook);
debugSilly('%s implements %s.decorate', fleck, hook);
}
// If only implementing, gather and decorate.
else if (this.fleckImplementation(fleck, hook)) {
raw = this.invokeFleck(hook, fleck);
debugSilly('%s implements %s', fleck, hook);
}
if (raw) {
const decorated = this.checkAndDecorateRawGathered(hook, raw, check);
debug('updating gathered %s from %s... %O', hook, fleck, decorated);
const entries = Object.entries(decorated);
entries.forEach(([type, Class]) => {
const {[type]: {[idProperty]: id}} = gathered;
const Subclass = wrapGathered(Class, id, idProperty, type, typeProperty);
// eslint-disable-next-line no-multi-assign
gathered[type] = Subclass;
gathered[id] = Subclass;
gathered[exports.ById][id] = Subclass;
gathered[exports.ByType][type] = Subclass;
this.invoke('@flecks/core.hmr.gathered.class', Subclass, hook);
});
this.invoke('@flecks/core.hmr.gathered', gathered, hook);
}
}
async refreshGathered(fleck) {
Object.entries(this.$$gathered)
.forEach(([
hook,
{
check,
idProperty,
gathered,
typeProperty,
},
]) => {
let raw;
// If decorating, gather all again
if (this.fleckImplementation(fleck, `${hook}.decorate`)) {
raw = this.invokeMergeAsync(hook);
debugSilly('%s implements %s.decorate', fleck, hook);
}
// If only implementing, gather and decorate.
else if (this.fleckImplementation(fleck, hook)) {
raw = this.invokeFleck(hook, fleck);
debugSilly('%s implements %s', fleck, hook);
}
if (raw) {
const decorated = this.checkAndDecorateRawGathered(hook, raw, check);
debug('updating gathered %s from %s...', hook, fleck);
debugSilly('%O', decorated);
const entries = Object.entries(decorated);
entries.forEach(([type, Class]) => {
const {[type]: {[idProperty]: id}} = gathered;
const Subclass = wrapGathered(Class, id, idProperty, type, typeProperty);
// eslint-disable-next-line no-multi-assign
gathered[type] = Subclass;
gathered[id] = Subclass;
gathered[exports.ById][id] = Subclass;
gathered[exports.ByType][type] = Subclass;
this.invoke('@flecks/core.hmr.gathered.class', Subclass, hook);
});
this.invoke('@flecks/core.hmr.gathered', gathered, hook);
}
});
}
/**

View File

@ -44,14 +44,14 @@ export const hooks = {
*/
username: undefined,
}),
'@flecks/core.gathered': () => ({
models: {typeProperty: 'name'},
}),
'@flecks/core.hmr.gathered': (gathered, hook, flecks) => {
if ('@flecks/db/server.models' === hook) {
register(gathered, flecks.db.sequelize);
}
},
'@flecks/core.starting': (flecks) => {
flecks.db.Models = flecks.gather('@flecks/db/server.models', {typeProperty: 'name'});
},
'@flecks/docker.containers': containers,
'@flecks/server.up': Flecks.priority(
async (flecks) => {
@ -63,17 +63,26 @@ export const hooks = {
export const mixin = (Flecks) => class FlecksWithDb extends Flecks {
db = {
Models: {},
$$sequelize: undefined,
get sequelize() {
return this.$$sequelize;
},
set sequelize(sequelize) {
this.$$sequelize = sequelize;
this.transaction = sequelize.transaction.bind(sequelize);
},
transaction: () => {},
};
constructor(runtime) {
super(runtime);
if (!this.db) {
this.db = {};
}
Object.defineProperty(this.db, 'Models', {get: () => this.gathered('@flecks/db/server.models')});
let $$sequelize;
let $$transaction = (fn) => fn();
Object.defineProperty(
this.db,
'sequelize',
{
get: () => $$sequelize,
set: (sequelize) => {
$$sequelize = sequelize;
$$transaction = sequelize.transaction.bind(sequelize);
},
},
);
Object.defineProperty(this.db, 'transaction', {get: () => $$transaction});
}
};

View File

@ -0,0 +1,14 @@
import {Flecks} from '@flecks/core';
import {expect} from 'chai';
it('automatically gathers models', async () => {
const flecks = await Flecks.from({
flecks: {
'@flecks/core': await import('@flecks/core'),
'@flecks/db/server': await import('../../src/server'),
'@test/thing': await import('./thing'),
},
});
expect(flecks.db.Models.Foo)
.to.not.be.undefined;
});

View File

@ -0,0 +1,7 @@
import Model from '../../../src/model';
export const hooks = {
'@flecks/db/server.models': () => ({
Foo: class extends Model {},
}),
};

View File

@ -8,9 +8,11 @@ export * from './hooks';
export {Packet, Packer, ValidationError} from './packet';
export const hooks = {
'@flecks/core.starting': (flecks) => {
flecks.socket.Packets = flecks.gather('@flecks/socket.packets', {check: badPacketsCheck});
},
'@flecks/core.gathered': () => ({
packets: {
check: badPacketsCheck,
},
}),
'@flecks/socket.packets': (flecks) => ({
Bundle: Bundle(flecks),
Redirect,
@ -26,12 +28,16 @@ export const hooks = {
export const mixin = (Flecks) => class FlecksWithSocket extends Flecks {
constructor(...args) {
super(...args);
constructor(runtime) {
super(runtime);
if (!this.socket) {
this.socket = {};
}
this.socket.Packets = {};
Object.defineProperty(
this.socket,
'Packets',
{get: () => this.gathered('@flecks/socket.packets')},
);
}
};

View File

@ -0,0 +1,14 @@
import {Flecks} from '@flecks/core';
import {expect} from 'chai';
it('automatically gathers packets', async () => {
const flecks = await Flecks.from({
flecks: {
'@flecks/core': await import('@flecks/core'),
'@flecks/socket': await import('../../src'),
'@test/thing': await import('./thing'),
},
});
expect(flecks.socket.Packets.Foo)
.to.not.be.undefined;
});

View File

@ -0,0 +1,7 @@
import Packet from '../../../src/packet/packet';
export const hooks = {
'@flecks/socket.packets': () => ({
Foo: class extends Packet {},
}),
};