Skip to content

Commit

Permalink
extract schema definitions to its own structure. #75
Browse files Browse the repository at this point in the history
  • Loading branch information
endel committed Jun 8, 2020
1 parent 8e6ef64 commit 573e11e
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 37 deletions.
23 changes: 9 additions & 14 deletions src/Schema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SWITCH_TO_STRUCTURE, NIL, INDEX_CHANGE, TYPE_ID, OPERATION } from './spec';
import { Definition, FilterCallback, Client, PrimitiveType, Context } from "./annotations";
import { SWITCH_TO_STRUCTURE, TYPE_ID, OPERATION } from './spec';
import { Definition, FilterCallback, Client, PrimitiveType, Context, SchemaDefinition } from "./annotations";

import * as encode from "./encoding/encode";
import * as decode from "./encoding/decode";
Expand Down Expand Up @@ -97,12 +97,7 @@ export abstract class Schema {
static _typeid: number;
static _context: Context;

static _schema: Definition;
static _indexes: {[field: string]: number};
static _fieldsByIndex: {[index: number]: string};
static _filters: {[field: string]: FilterCallback};
static _deprecated: {[field: string]: boolean};
static _descriptors: PropertyDescriptorMap & ThisType<any>;
static _definition: SchemaDefinition;

static onError(e) {
console.error(e);
Expand Down Expand Up @@ -156,12 +151,12 @@ export abstract class Schema {
return this;
}

protected get _schema () { return (this.constructor as typeof Schema)._schema; }
protected get _descriptors () { return (this.constructor as typeof Schema)._descriptors; }
protected get _indexes () { return (this.constructor as typeof Schema)._indexes; }
protected get _fieldsByIndex() { return (this.constructor as typeof Schema)._fieldsByIndex; }
protected get _filters () { return (this.constructor as typeof Schema)._filters; }
protected get _deprecated () { return (this.constructor as typeof Schema)._deprecated; }
protected get _schema () { return (this.constructor as typeof Schema)._definition.schema; }
protected get _descriptors () { return (this.constructor as typeof Schema)._definition.descriptors; }
protected get _indexes () { return (this.constructor as typeof Schema)._definition.indexes; }
protected get _fieldsByIndex() { return (this.constructor as typeof Schema)._definition.fieldsByIndex; }
protected get _filters () { return (this.constructor as typeof Schema)._definition.filters; }
protected get _deprecated () { return (this.constructor as typeof Schema)._definition.deprecated; }

public listen <K extends NonFunctionPropNames<this>>(attr: K, callback: (value: this[K], previousValue: this[K]) => void) {
if (!this.$listeners[attr as string]) {
Expand Down
74 changes: 51 additions & 23 deletions src/annotations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,30 @@ export type FilterCallback<
R extends Schema = any
> = (this: T, client: Client, value: V, root?: R) => boolean;

export class SchemaDefinition {
schema: Definition;
indexes: { [field: string]: number };
fieldsByIndex: { [index: number]: string };
filters: { [field: string]: FilterCallback };
deprecated: { [field: string]: boolean };
descriptors: PropertyDescriptorMap & ThisType<any>;

static extend(parent: SchemaDefinition) {
const extended = new SchemaDefinition();

// support inheritance
if (parent) {
extended.schema = Object.assign({}, parent.schema || {});
extended.indexes = Object.assign({}, parent.indexes || {});
extended.fieldsByIndex = Object.assign({}, parent.fieldsByIndex || {});
extended.descriptors = Object.assign({}, parent.descriptors || {});
extended.deprecated = Object.assign({}, parent.deprecated || {});
}

return extended;
}
}

// Colyseus integration
export type Client = { sessionId: string } & any;

Expand Down Expand Up @@ -69,39 +93,40 @@ export function type (type: DefinitionType, context: Context = globalContext): P
if (!context.has(constructor)) {
context.add(constructor);

// TODO: move all this stuff to its own holder object.

// support inheritance
constructor._schema = Object.assign({}, constructor._schema || {});
constructor._indexes = Object.assign({}, constructor._indexes || {});
constructor._fieldsByIndex = Object.assign({}, constructor._fieldsByIndex || {});
constructor._descriptors = Object.assign({}, constructor._descriptors || {});
constructor._deprecated = Object.assign({}, constructor._deprecated || {});
if (constructor._definition) {
constructor._definition = SchemaDefinition.extend(constructor._definition);

} else {
constructor._definition = new SchemaDefinition()
}
}

const index = Object.keys(constructor._schema).length;
constructor._fieldsByIndex[index] = field;
constructor._indexes[field] = index;
constructor._schema[field] = type;
const definition = constructor._definition;

const index = Object.keys(definition.schema).length;
definition.fieldsByIndex[index] = field;
definition.indexes[field] = index;
definition.schema[field] = type;

/**
* skip if descriptor already exists for this field (`@deprecated()`)
*/
if (constructor._descriptors[field]) { return; }
if (definition.descriptors[field]) { return; }

const isArray = ArraySchema.is(type);
const isMap = !isArray && MapSchema.is(type);
const isSchema = Schema.is(type);

const fieldCached = `_${field}`;

constructor._descriptors[fieldCached] = {
definition.descriptors[fieldCached] = {
enumerable: false,
configurable: false,
writable: true,
};

constructor._descriptors[field] = {
definition.descriptors[field] = {
get: function () {
return this[fieldCached];
},
Expand Down Expand Up @@ -199,7 +224,7 @@ export function type (type: DefinitionType, context: Context = globalContext): P
value.$changes.setParent(
this.$changes,
$root,
constructor._schema[field],
definition.schema[field],
);
// value.$changes = new ChangeTree(value, {}, this.$changes, $root);

Expand All @@ -214,13 +239,13 @@ export function type (type: DefinitionType, context: Context = globalContext): P
}

} else if (isMap) {
console.log("DIRECTLY ASSIGNING A MAP, type =>", (constructor._schema[field] as any).map);
console.log("DIRECTLY ASSIGNING A MAP, type =>", (definition.schema[field] as any).map);

// directly assigning a map
value.$changes.setParent(
this.$changes,
$root,
(constructor._schema[field] as any).map,
(definition.schema[field] as any).map,
);

this.$changes.change(field);
Expand All @@ -247,7 +272,7 @@ export function type (type: DefinitionType, context: Context = globalContext): P
value.$changes.setParent(
this.$changes,
$root,
// constructor._schema[field],
// definition.schema[field],
);
}

Expand All @@ -270,15 +295,16 @@ export function type (type: DefinitionType, context: Context = globalContext): P
export function filter<T extends Schema, V, R extends Schema>(cb: FilterCallback<T, V, R>): PropertyDecorator {
return function (target: any, field: string) {
const constructor = target.constructor as typeof Schema;
const definition = constructor._definition;

/*
* static filters
*/
if (!constructor._filters) {
constructor._filters = {};
if (!definition.filters) {
definition.filters = {};
}

constructor._filters[field] = cb;
definition.filters[field] = cb;
}
}

Expand All @@ -290,10 +316,12 @@ export function filter<T extends Schema, V, R extends Schema>(cb: FilterCallback
export function deprecated(throws: boolean = true, context: Context = globalContext): PropertyDecorator {
return function (target: typeof Schema, field: string) {
const constructor = target.constructor as typeof Schema;
constructor._deprecated[field] = true;
const definition = constructor._definition;

definition.deprecated[field] = true;

if (throws) {
constructor._descriptors[field] = {
definition.descriptors[field] = {
get: function () { throw new Error(`${field} is deprecated.`); },
set: function (this: Schema, value: any) { /* throw new Error(`${field} is deprecated.`); */ },
enumerable: false,
Expand Down

0 comments on commit 573e11e

Please sign in to comment.