Pular para o conteúdo

Vitrine

Um programa de cima para baixo vira uma página web. Sem HTML, sem JavaScript, sem separar o que calcula do que mostra.

Vitrine é o framework de painéis e aplicações de dados do DataForge. Você escreve um programa de cima para baixo; ele vira uma página web.

dataforge
adopt Arcane.Vitrine as V

action painel():
    V.titulo("Vendas")
    regiao := V.escolha("Região", ["Sul", "Sudeste", "Norte"])
    V.metrica("Receita", "R$ 128.400", variacao := 12.5)
    V.grafico_barras(vendas_de(regiao), x := "mes")

V.rodar(painel, porta := 8501)

Isso é a aplicação inteira. Não há HTML, não há CSS, não há JavaScript, não há build, e não há separação entre o que calcula e o que mostra.

O modelo de execução#

A cada interação, o programa inteiro roda de novo — e o estado da sessão sobrevive.

text
clique  →  o programa roda do começo  →  a árvore vira HTML  →  a tela troca
              ↑                                                      │
              └──────────  o estado da sessão continua  ─────────────┘

Parece desperdício e é o contrário. Quem escreve nunca pensa em callback, em diffing, nem em qual pedaço da tela atualizar: a linha de cima sempre aconteceu antes da linha de baixo, como em qualquer programa.

É por isso que um componente devolve o que quem escreve precisa. V.botao(…) devolve yes ou no; V.entrada(…) devolve o texto digitado. A linha seguinte já usa o valor:

dataforge
given V.botao("Salvar"):
    salvar(V.entrada("Nome"))
    V.sucesso("Pronto.")

Vitrine ou Kiln?#

Os dois são frameworks web, e a escolha é clara:

KilnVitrine
Parasites e APIspainéis e aplicações de dados
Você escreverotas que devolvem o que quiserum programa de cima para baixo
A página éum template que você controlaa árvore que o programa montou
Clienteo que você puser lá~4 KB, prontos
Exemplouma loja, uma API RESTum dashboard, um formulário interno

A Vitrine roda sobre o Kiln: HTTP, rotas, arquivos estáticos, sessão, cabeçalhos de segurança e REST já existiam lá, testados. Reimplementá-los criaria duas implementações do mesmo protocolo para divergirem. V.montar() devolve o app Kiln por baixo, e nele você acrescenta rota, REST ou middleware.

Um painel completo#

dataforge
adopt Arcane.Vitrine as V

V.app("Business Intelligence", icone := "📊")

mark @V.cache
action vendas():
    yield Banco.consultar("SELECT mes, receita, meta FROM vendas")

action painel():
    lado := V.lateral()
    regiao := lado.escolha("Região", ["Sudeste", "Sul", "Norte"])

    V.titulo("Dashboard de Vendas", icone := "📊")

    colunas := V.colunas(4)
    colunas[0].metrica("Receita", "R$ 850.000", variacao := 18.0)
    colunas[1].metrica("Clientes", "12.450", variacao := 8.0)
    colunas[2].metrica("Pedidos", "32.500", variacao := 14.0)
    colunas[3].metrica("Conversão", "8.4%", variacao := 1.2)

    V.cabecalho("Evolução")
    V.grafico_linha(vendas(), x := "mes", y := ["receita", "meta"])

    V.cabecalho("Dados")
    V.frame(vendas())
    V.exportar_csv(vendas())

V.pagina("/", painel)
V.rodar(porta := 8501)

Começar#

bash
dataforge vitrine new meupainel
cd meupainel
dataforge vitrine dev        # http://127.0.0.1:8501

O projeto criado já tem página, dados, testes e um forge.toml — e passa nos próprios testes antes de você tocar em qualquer coisa.

O painel completo do exemplo está em examples/vitrine_dashboard.df, e ele também se testa sozinho:

bash
dataforge run examples/vitrine_dashboard.df              # os testes
dataforge run examples/vitrine_dashboard.df -- --servir  # no navegador

De onde vêm os dados#

De lugar nenhum especial. A Vitrine desenha; quem lê os dados é o módulo que você já usaria num script — e é por isso que não há um "conector" a aprender.

dataforge
adopt Arcane.Vitrine as V
adopt Arcane.Database as DB
adopt Arcane.Analytics as An
adopt Arcane.Cortex as ML

banco := DB.connect("vendas.db")

mark @V.cache
action vendas():
    yield DB.query(banco, "SELECT mes, numero, receita FROM v")

action prever(mes):
    modelo := ML.linear(vendas(), "receita", ["numero"])
    yield ML.prever(modelo, [{"numero": mes}])[0]

action painel():
    V.frame(vendas())                                    // do banco
    V.grafico_barras(An.from_records(vendas()), x := "mes")
    V.vault(An.describe(An.from_records(vendas())))      // estatística
    V.metrica("Previsão", round(prever(4), 1))           // modelo
ParaUseE passe para
banco relacional`Arcane.Database` · Arcane.ForgeV.frame, V.tabela, qualquer gráfico
CSV, JSON, Excel`Arcane.IO` · `Arcane.Excel`o mesmo
estatística e DataFrame`Arcane.Analytics`um Frame entra direto
Parquet e Data Lake`Arcane.Lago`o mesmo
streaming`Arcane.Stream`com V.atualizar_a_cada(n)
aprendizado de máquina`Arcane.Cortex`V.metrica, V.grafico_dispersao
uma API de fora`Arcane.Http`sob mark @V.cache, sempre

A mesma aplicação serve uma API#

V.montar() devolve o app Kiln por baixo. Acrescente rotas nele e o painel e a API vivem no mesmo processo, na mesma porta, lendo das mesmas ações em cache:

dataforge
adopt Kiln

kiln := V.montar()

Kiln.get(kiln, "/api/vendas", lambda req: {"itens": vendas()})
Kiln.use(kiln, Kiln.cors())
Kiln.use(kiln, Kiln.rate_limit(120))

V.subir(porta := 8501)

Vale para tudo do Kiln: CORS, limite de taxa, CSRF, cabeçalhos, validação de esquema, Kiln.resource com as sete rotas RESTful. Ver Middleware do Kiln — e `Arcane.API`, que exporta essas rotas para o Insomnia e o Postman.

Zero dependência, inclusive no navegador#

O gráfico é SVG escrito no servidor. O cliente são ~4 KB de JavaScript sem build e sem CDN — ele manda de volta o que o usuário fez e troca o miolo da página.

Não é purismo: uma biblioteca de gráficos vinda de CDN quebra qualquer aplicação que rode em rede fechada, que é exatamente onde painel de dados costuma rodar. E SVG imprime, escala e é legível por leitor de tela.

Onde continuar#