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

> Compilez de petites règles écrites dans un langage simple (IF, ELSE, AND, OR, RETURN, appels de helpers) en une fonction que vous exécutez avec un modèle, afin que les règles puissent vivre dans un fichier ou une base de données.

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

`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.

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