ForgeTotal.js 5
de

Total.js 5

Geschäftsregeln als Text mit NEWMACRO()

Total.js 5 · total5@0.0.19 macrosrulesscripting Mit Total.js 5 ausgeführt

NEWMACRO(text, [nocompile], [isasync]) macht aus ein paar Zeilen einer kleinen Sprache eine Funktion function(model, helpers). Es ist für Regeln gedacht, die sich häufiger ändern als der Code: ein Rabatt, ein Preis, eine Berechtigungsprüfung, eine Validierung, die jemand ohne JavaScript-Kenntnisse in einer Datei oder einer Datenbank pflegt.

Die Sprache, wie sie geprüft wurde:

  • IF condition, ELSE IF condition, ELSE und FI (das Ende der Bedingung) funktionieren wie in einer textbasierten Regel. Bedingungen verwenden >, <, >=, <=, = und AND / OR.
  • RETURN value beendet das Makro mit einem Wert. YES und NO (auch TRUE, FALSE, OK) sind Booleans, Text in Anführungszeichen ist Text und Zahlen sind Zahlen.
  • Ein Wort ist eine Eigenschaft des Modells, in Kleinbuchstaben geschrieben: age liest model.age, und total = price * qty schreibt model.total.
  • NAME(arguments) ruft einen Helper auf, die Funktion mit diesem Namen (in Kleinbuchstaben) im übergebenen Objekt helpers; darin ist this das Modell. Mit isasync auf true ist das Makro eine async-Funktion und wartet auf die Helper.
  • #name ist eine temporäre Variable des Laufs: #temp = num + 1 und RETURN #temp.
  • Verwende Namen aus mehreren Buchstaben: Eine Eigenschaft oder Variable aus einem Buchstaben wurde bei den Prüfungen nicht erkannt.

NEWMACRO() kompiliert den Text mit new Function, ein Makro ist also Code: Kompiliere nur Text, den du selbst geschrieben hast oder den eine vertrauenswürdige Person pflegt, niemals Text, den ein Besucher gesendet hat. Mit nocompile auf true gibt es statt der Funktion das erzeugte JavaScript zurück, was nützlich ist, um zu sehen, was eine Regel tut.

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));
}