# Komponensek folyamának építése a Flow.load()-dal és futtatása az exec()-kel

> Írjon le egy FlowStreamet komponensekként és a köztük lévő kapcsolatokként, töltse be az alkalmazásba, küldjön rajta át egy üzenetet és olvassa a választ, és módosítsa egy komponens konfigurációját futás közben.

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

A **FlowStream** egy kis program, amely **komponensekből** (bemenetekkel és kimenetekkel rendelkező dobozokból) és a köztük lévő **kapcsolatokból** áll. A Total.js az alkalmazásán belül futtatja: egy üzenet belép egy komponensbe, a komponens eredményeket küld a kimeneteire, a kapcsolatok pedig a következő komponensekhez viszik őket. Ugyanezt a sémát rajzolja a Total.js Flow-tervezője; itt kézzel van megírva.

A `Flow.load(schema, [callback])` elindít egy FlowStreamet, és a callbacknek `(err, instance)` értéket ad. A séma egy objektum:

- `id` és `name`: az id a kulcs a `Flow.instances`-ben (`Flow.instances.demo`); id nélkül véletlenszerűt hoz létre.
- `components`: egy objektum az egyes komponensek **forráskódjával**, szövegként. A szöveg egy szkript, `<script total>...</script>` (az a forma, amelyet a tervező ment).
- `design`: a komponensek példányai. Mindegyiknek van `id`-ja, a használt `component`, a saját `config` és `connections` értéke, egy objektum, amelynek kulcsai a **kimenetek id-jai**, értékei pedig `{ id, index }` listák: az a példány és **bemenet-id**, amely megkapja, amit arra a kimenetre küldenek.
- `variables`: értékek, amelyeket a komponensek olvashatnak.

A komponens egy modul: `exports.id`, `exports.name`, `exports.inputs` és `exports.outputs` (`{ id, name }` listák), `exports.config` (az alapértelmezések) és `exports.make = function(instance, config)`. A `make()`-ben a komponens beállítja az `instance.message = function($) {}` függvényt, amely minden üzenetet megkap: a `$.data` az adat, a `$.send(outputid, data)` egy kimenetre küldi, a `$.destroy()` pedig befejez egy üzenetet, amely nem megy tovább.

A folyamat kódból való használatához az `instance.exec({ id, data, callback })` elküldi a `data`-t az `id` példánynak, és meghívja a `callback(err, message)` függvényt, amikor az üzenet véget ér; a lánc utolsó üzenete a `message` (így a `message.data` az, amit az utolsó komponens kapott). Az `instance.reconfigure(id, config)` megváltoztatja egy futó példány konfigurációját, a `Flow.remove(id)` pedig leállítja a FlowStreamet és elfelejti. A `FlowStream` ugyanaz az objektum, mint a `Flow`: két név a kezelőre.

**A komponens olyan kód, amely az alkalmazás összes jogával fut** (ugyanabban a szálban, hacsak a séma nem mondja, hogy `worker`). Soha ne töltsön be olyan sémát vagy komponenst, amelyet egy látogató írhat.

```js definitions/flow.js
const upper = `<script total>
exports.id = 'upper';
exports.name = 'Upper case';
exports.inputs = [{ id: 'input', name: 'Input' }];
exports.outputs = [{ id: 'output', name: 'Output' }];
exports.config = { prefix: '' };
exports.make = function(instance, config) {
	instance.message = function($) {
		$.send('output', config.prefix + String($.data).toUpperCase());
	};
};
</script>`;

const collect = `<script total>
exports.id = 'collect';
exports.name = 'Collect';
exports.inputs = [{ id: 'input', name: 'Input' }];
exports.make = function(instance) {
	instance.message = function($) {
		MAIN.collected.push($.data);
		$.destroy();
	};
};
</script>`;

MAIN.collected = [];

MAIN.flow = null;

Flow.load({
	id: 'demo',
	name: 'Demo',
	components: { upper: upper, collect: collect },
	design: {
		a: { id: 'a', component: 'upper', config: { prefix: '> ' }, connections: { output: [{ id: 'b', index: 'input' }] } },
		b: { id: 'b', component: 'collect', config: {}, connections: {} }
	},
	variables: {}
}, function(err, instance) {
	MAIN.flow = instance;
});
```

```js controllers/flow.js
exports.install = function() {
	ROUTE('GET /shout/', shout);
	ROUTE('POST /prefix/ @json', prefix);
	ROUTE('GET /collected/', collected);
	ROUTE('GET /info/', info);
	ROUTE('POST /stop/', stop);
};

function shout($) {
	if (!MAIN.flow)
		return $.invalid('The flow is not running');
	MAIN.flow.exec({ id: 'a', data: $.query.text, callback: (err, message) => $.json({ result: message ? message.data : null }) });
}

function prefix($) {
	MAIN.flow.reconfigure('a', { prefix: $.body.prefix });
	$.json({ ok: true });
}

function collected($) {
	$.json(MAIN.collected);
}

function info($) {
	$.json({ flows: Object.keys(Flow.instances), instances: Object.keys(MAIN.flow.flow.meta.flow), same: Flow === FlowStream });
}

function stop($) {
	Flow.remove('demo');
	MAIN.flow = null;
	$.json({ flows: Object.keys(Flow.instances) });
}
```
