import { bindModelPrototype, construct } from "./construction";
import { ModelKeys } from "../utils/constants";
import { Model } from "./Model";
import { metadata } from "@decaf-ts/reflection";
import { Decoration, DecorationKeys, Metadata } from "@decaf-ts/decoration";
export function modelBaseDecorator(original: any) {
// the new constructor behaviour
const newConstructor: any = function (...args: any[]) {
const instance: ReturnType<typeof original> = construct(original, ...args);
bindModelPrototype(instance);
// run a builder function if defined with the first argument (The ModelArg)
const builder = Model.getBuilder();
if (builder) builder(instance, args.length ? args[0] : undefined);
metadata(Model.key(ModelKeys.MODEL), original.name)(instance.constructor);
return instance;
};
// copy prototype so instanceof operator still works
newConstructor.prototype = original.prototype;
metadata(Model.key(ModelKeys.MODEL), original.name)(original);
Reflect.getMetadataKeys(original).forEach((key) => {
Reflect.defineMetadata(
key,
Reflect.getMetadata(key, original),
newConstructor
);
});
// Sets the proper constructor name for type verification
Object.defineProperty(newConstructor, "name", {
writable: false,
enumerable: true,
configurable: false,
value: original.prototype.constructor.name,
});
//
// anchors the original constructor for future reference
Object.defineProperty(newConstructor, ModelKeys.ANCHOR, {
writable: false,
enumerable: false,
configurable: false,
value: original,
});
Metadata.set(newConstructor, DecorationKeys.CONSTRUCTOR, original);
//
// // anchors the new constructor for future reference
// Object.defineProperty(original, ModelKeys.ANCHOR, {
// writable: false,
// enumerable: true,
// configurable: false,
// value: newConstructor,
// });
Model.register(newConstructor, original.name);
// return new constructor (will override original)
return newConstructor;
}
/**
* @summary Defines a class as a Model class
* @description
*
* - Registers the class under the model registry so it can be easily rebuilt;
* - Overrides the class constructor;
* - Runs the global {@link ModelBuilderFunction} if defined;
*
* @function model
*
* @category Class Decorators
*/
export function model() {
const key = Model.key(ModelKeys.MODEL);
return Decoration.for(key).define(modelBaseDecorator).apply();
}
/**
* @summary Defines the hashing algorithm to use on the model
* @description
*
* - Registers the class under the model registry so it can be easily rebuilt;
* - Overrides the class constructor;
* - Runs the global {@link ModelBuilderFunction} if defined;
*
* @param {string} algorithm the algorithm to use
*
* @function hashedBy
*
* @category Class Decorators
*/
export function hashedBy(algorithm: string, ...args: any[]) {
return metadata(Model.key(ModelKeys.HASHING), {
algorithm: algorithm,
args: args,
});
}
/**
* @summary Defines the serialization algorithm to use on the model
*
* @param {string} serializer the algorithm to use
*
* @function serializedBy
*
* @category Class Decorators
*/
export function serializedBy(serializer: string, ...args: any[]) {
return metadata(Model.key(ModelKeys.SERIALIZATION), {
serializer: serializer,
args: args,
});
}
/**
* @summary Applies descriptive metadata to a class, property or method
*
* @param {string} description the description to apply
*
* @function description
*
* @category Decorators
*/
export function description(description: string) {
return metadata(Model.key(ModelKeys.DESCRIPTION), description);
}
Source