ForgeTotal.js 5
fr

Total.js 5

Des règles métier sous forme de texte avec NEWMACRO()

Total.js 5 · total5@0.0.19 macrosrulesscripting Exécutée avec Total.js 5

NEWMACRO(text, [nocompile], [isasync]) transforme quelques lignes d'un petit langage en une fonction function(model, helpers). Il est conçu pour des règles qui changent plus souvent que le code : une remise, un prix, un contrôle de permission, une validation que quelqu'un qui n'écrit pas de JavaScript maintient dans un fichier ou une base de données.

Le langage, tel qu'il a été vérifié :

  • IF condition, ELSE IF condition, ELSE et FI (la fin de la condition) fonctionnent comme dans une règle textuelle. Les conditions utilisent >, <, >=, <=, = et AND / OR.
  • RETURN value termine la macro avec une valeur. YES et NO (aussi TRUE, FALSE, OK) sont des booléens, le texte entre guillemets est du texte et les nombres sont des nombres.
  • Un mot est une propriété du modèle, écrite en minuscules : age lit model.age, et total = price * qty écrit model.total.
  • NAME(arguments) appelle un helper, la fonction portant ce nom (en minuscules) dans l'objet helpers que vous passez ; à l'intérieur, this est le modèle. Avec isasync à true, la macro est une fonction async et attend les helpers.
  • #name est une variable temporaire de l'exécution : #temp = num + 1 et RETURN #temp.
  • Utilisez des noms de plusieurs lettres : une propriété ou une variable d'une seule lettre n'a pas été reconnue lors des vérifications.

NEWMACRO() compile le texte avec new Function, donc une macro est du code : ne compilez que du texte que vous avez écrit ou qu'une personne de confiance maintient, jamais du texte envoyé par un visiteur. Avec nocompile à true, elle renvoie le JavaScript qu'elle génère au lieu de la fonction, ce qui est utile pour voir ce que fait une règle.

controllers/rules.js
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));
}