macroable
Version:
A simple ES6 class that can be extended to provide macros and getters functionality
116 lines (115 loc) • 3.46 kB
JavaScript
;
/*
* @poppinss/macroable
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Macroable = void 0;
/**
* Macroable is an abstract class to add ability to extend your class
* prototype using better syntax.
*
* Macroable has handful of benefits over using traditional `prototype` approach.
*
* 1. Methods or properties added dynamically to the class can be removed using `hydrate` method.
* 2. Can define singleton getters.
*/
class Macroable {
constructor() {
if (!this.constructor['macros'] || !this.constructor['getters']) {
throw new Error('Set static properties "macros = {}" and "getters = {}" on the class for the macroable to work.');
}
}
/**
* Add a macro to the class. This method is a better to manually adding
* to `class.prototype.method`.
*
* Also macros added using `Macroable.macro` can be cleared anytime
*
* @example
* ```js
* Macroable.macro('getUsername', function () {
* return 'virk'
* })
* ```
*/
static macro(name, callback) {
const self = this;
self.macros[name] = callback;
this.prototype[name] = callback;
}
/**
* Return the existing macro or null if it doesn't exists
*/
static getMacro(name) {
return this.macros[name];
}
/**
* Returns a boolean telling if a macro exists
*/
static hasMacro(name) {
return !!this.getMacro(name);
}
/**
* Define a getter, which is invoked everytime the value is accessed. This method
* also allows adding single getters, whose value is cached after first time
*
* @example
* ```js
* Macroable.getter('time', function () {
* return new Date().getTime()
* })
*
* console.log(new Macroable().time)
*
* // Singletons
* Macroable.getter('time', function () {
* return new Date().getTime()
* }, true)
*
* console.log(new Macroable().time)
* ```
*/
static getter(name, callback, singleton = false) {
const wrappedCallback = singleton
? function wrappedCallback() {
const value = callback.bind(this)();
Object.defineProperty(this, name, { value, configurable: true });
return value;
}
: callback;
const self = this;
self.getters[name] = wrappedCallback;
Object.defineProperty(this.prototype, name, {
get: wrappedCallback,
configurable: true,
enumerable: true,
});
}
/**
* Return the existing getter or null if it doesn't exists
*/
static getGetter(name) {
return this.getters[name];
}
/**
* Returns a boolean telling if a getter exists
*/
static hasGetter(name) {
return !!this.getGetter(name);
}
/**
* Cleanup getters and macros from the class
*/
static hydrate() {
Object.keys(this.macros).forEach((key) => Reflect.deleteProperty(this.prototype, key));
Object.keys(this.getters).forEach((key) => Reflect.deleteProperty(this.prototype, key));
this.macros = {};
this.getters = {};
}
}
exports.Macroable = Macroable;