# Construa um fluxo de componentes com Flow.load() e execute-o com exec()

> Descreva um FlowStream como componentes e as ligações entre eles, carregue-o na aplicação, envie uma mensagem através dele e leia a resposta, e altere a configuração de um componente enquanto corre.

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

Um **FlowStream** é um pequeno programa feito de **componentes** (caixas com entradas e saídas) e **ligações** entre eles. O Total.js executa-o dentro da sua aplicação: uma mensagem entra num componente, o componente envia resultados para as suas saídas e as ligações levam-nos aos componentes seguintes. O mesmo esquema é o que o designer de Flow do Total.js desenha; aqui está escrito à mão.

`Flow.load(schema, [callback])` inicia um FlowStream e dá ao callback `(err, instance)`. O esquema é um objeto:

- `id` e `name`: o id é a chave em `Flow.instances` (`Flow.instances.demo`); sem id é criado um aleatório.
- `components`: um objeto com o **código-fonte** de cada componente, como texto. O texto é um script, `<script total>...</script>` (a forma que o designer guarda).
- `design`: as instâncias dos componentes. Cada uma tem um `id`, o `component` que usa, a sua `config` e `connections`, um objeto cujas chaves são **ids de saída** e cujos valores são listas de `{ id, index }`: a instância e o **id de entrada** que recebem o que é enviado para essa saída.
- `variables`: valores que os componentes podem ler.

Um componente é um módulo: `exports.id`, `exports.name`, `exports.inputs` e `exports.outputs` (listas de `{ id, name }`), `exports.config` (os valores por omissão) e `exports.make = function(instance, config)`. Em `make()` o componente define `instance.message = function($) {}`, que recebe cada mensagem: `$.data` são os dados, `$.send(outputid, data)` envia-os para uma saída e `$.destroy()` termina uma mensagem que não vai mais longe.

Para usar o fluxo a partir do seu código, `instance.exec({ id, data, callback })` envia `data` para a instância `id` e chama `callback(err, message)` quando a mensagem termina; a última mensagem da cadeia é `message` (pelo que `message.data` é o que o último componente recebeu). `instance.reconfigure(id, config)` altera a configuração de uma instância em execução e `Flow.remove(id)` para o FlowStream e esquece-o. `FlowStream` é o mesmo objeto que `Flow`: dois nomes para o gestor.

**Um componente é código que corre com todos os direitos da aplicação** (no mesmo thread, a menos que o esquema diga `worker`). Nunca carregue um esquema ou um componente que um visitante possa escrever.

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