Reglas de negocio como texto con NEWMACRO()
NEWMACRO(text, [nocompile], [isasync]) convierte unas pocas líneas de un lenguaje pequeño en una función function(model, helpers). Está pensado para reglas que cambian más a menudo que el código: un descuento, un precio, una comprobación de permisos, una validación que alguien que no escribe JavaScript mantiene en un archivo o en una base de datos.
El lenguaje, tal como se comprobó:
IF condition,ELSE IF condition,ELSEyFI(el final de la condición) funcionan como en una regla basada en texto. Las condiciones usan>,<,>=,<=,=yAND/OR.RETURN valuetermina la macro con un valor.YESyNO(tambiénTRUE,FALSE,OK) son booleanos, el texto entre comillas es texto y los números son números.- Una palabra es una propiedad del modelo, escrita en minúsculas:
ageleemodel.age, ytotal = price * qtyescribemodel.total. NAME(arguments)llama a un helper, la función con ese nombre (en minúsculas) en el objetohelpersque pasas; dentro de ellathises el modelo. Conisasyncentruela macro es una función async y espera a los helpers.#namees una variable temporal de la ejecución:#temp = num + 1yRETURN #temp.- Usa nombres de varias letras: una propiedad o una variable de una letra no se reconoció en las comprobaciones.
NEWMACRO() compila el texto con new Function, así que una macro es código: compila solo texto que hayas escrito tú o que mantenga una persona de confianza, nunca texto que envió un visitante. Con nocompile en true devuelve el JavaScript que genera en lugar de la función, lo que es útil para ver qué hace una regla.
const helpers = {
percent: function(value, rate) {
return value * rate / 100;
}
};
const age = NEWMACRO("IF age >= 18\n\tRETURN 'adult'\nELSE\n\tRETURN 'minor'\nFI");
const level = NEWMACRO("IF points > 100\n\tRETURN 'gold'\nELSE IF points > 50\n\tRETURN 'silver'\nELSE\n\tRETURN 'bronze'\nFI");
const access = NEWMACRO("IF active AND score >= 5\n\tRETURN yes\nFI\nRETURN no");
const total = NEWMACRO("total = price * qty\nIF qty >= 10\n\ttotal = total - PERCENT(total, 10)\nFI\nRETURN total");
const temporary = NEWMACRO("#next = count + 1\nRETURN #next");
const slow = NEWMACRO("RETURN LOOKUP(code)", false, true);
exports.install = function() {
ROUTE('POST /rules/', rules);
ROUTE('GET /source/', source);
};
async function rules($) {
const model = $.body;
$.json({
age: age(model, helpers),
level: level(model, helpers),
access: access(model, helpers),
total: total(model, helpers),
model_total: model.total,
temporary: temporary(model, helpers),
slow: await slow(model, { lookup: async code => 'found ' + code })
});
}
function source($) {
$.plain(NEWMACRO("IF points > 100\n\tRETURN 'gold'\nFI", true));
}