# Bedrijfsregels als tekst met NEWMACRO()

> Compileer kleine regels in een eenvoudige taal (IF, ELSE, AND, OR, RETURN, helper-aanroepen) tot een functie die je met een model uitvoert, zodat de regels in een bestand of een database kunnen leven.

Framework: total5
Product: Total.js 5 (total5@0.0.19)
Language: nl
Content-SHA256: d60743c35c1e6e96adb0a608e701631001aac22b36022f3774d60617cc728b3f
Snapshot: c0700e321579725c5cf4f5ace1ad7d2866d1b4f93c24a63c0b41b6b7998e976c
Sources: total5-installed@total5@0.0.19
Review: not-recorded

`NEWMACRO(text, [nocompile], [isasync])` maakt van enkele regels van een kleine taal een functie `function(model, helpers)`. Het is bedoeld voor regels die vaker veranderen dan de code: een korting, een prijs, een rechtencontrole, een validatie die iemand die geen JavaScript schrijft in een bestand of database onderhoudt.

De taal, zoals die is gecontroleerd:

- `IF condition`, `ELSE IF condition`, `ELSE` en `FI` (het einde van de voorwaarde) werken zoals in een tekstgebaseerde regel. Voorwaarden gebruiken `>`, `<`, `>=`, `<=`, `=` en `AND` / `OR`.
- `RETURN value` beëindigt de macro met een waarde. `YES` en `NO` (ook `TRUE`, `FALSE`, `OK`) zijn booleans, tekst tussen aanhalingstekens is tekst en getallen zijn getallen.
- Een woord is een **eigenschap van het model**, in kleine letters geschreven: `age` leest `model.age`, en `total = price * qty` schrijft `model.total`.
- `NAME(arguments)` roept een **helper** aan, de functie met die naam (in kleine letters) in het `helpers`-object dat je meegeeft; daarin is `this` het model. Met `isasync` op `true` is de macro een async-functie en wacht hij op de helpers.
- `#name` is een tijdelijke variabele van de run: `#temp = num + 1` en `RETURN #temp`.
- Gebruik namen van **meerdere letters**: een eigenschap of variabele van één letter werd bij de controles niet herkend.

`NEWMACRO()` compileert de tekst met `new Function`, dus een macro is code: compileer alleen tekst die je zelf hebt geschreven of die een vertrouwde persoon onderhoudt, nooit tekst die een bezoeker heeft gestuurd. Met `nocompile` op `true` geeft het in plaats van de functie het JavaScript terug dat het genereert, handig om te zien wat een regel doet.

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