Regole di business come testo con NEWMACRO()
NEWMACRO(text, [nocompile], [isasync]) trasforma poche righe di un piccolo linguaggio in una funzione function(model, helpers). È pensato per regole che cambiano più spesso del codice: uno sconto, un prezzo, un controllo dei permessi, una validazione che qualcuno che non scrive JavaScript mantiene in un file o in un database.
Il linguaggio, come è stato verificato:
IF condition,ELSE IF condition,ELSEeFI(la fine della condizione) funzionano come in una regola testuale. Le condizioni usano>,<,>=,<=,=eAND/OR.RETURN valuetermina la macro con un valore.YESeNO(ancheTRUE,FALSE,OK) sono booleani, il testo tra virgolette è testo e i numeri sono numeri.- Una parola è una proprietà del modello, scritta in minuscolo:
ageleggemodel.age, etotal = price * qtyscrivemodel.total. NAME(arguments)chiama un helper, la funzione con quel nome (in minuscolo) nell'oggettohelpersche passi; al suo internothisè il modello. Conisasyncimpostato sutruela macro è una funzione async e attende gli helper.#nameè una variabile temporanea dell'esecuzione:#temp = num + 1eRETURN #temp.- Usa nomi di più lettere: una proprietà o una variabile di una lettera non è stata riconosciuta nei controlli.
NEWMACRO() compila il testo con new Function, quindi una macro è codice: compila solo testo che hai scritto tu o che mantiene una persona di fiducia, mai testo inviato da un visitatore. Con nocompile impostato su true restituisce il JavaScript che genera invece della funzione, utile per vedere cosa fa una regola.
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));
}