Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,23 @@ class LiquidoPontoEntrada {
initial: 1
});

const perguntaLinguagemDeBackEnd = await prompts({
type: 'select',
name: 'linguagemBackEnd',
message: 'Selecione a linguagem de programação',
choices: [
{
title: 'Delégua',
value: 'delegua'
},
{
title: 'Pituguês',
value: 'pitugues'
}
],
initial: 1
});

const perguntaInicializarRepositorioGit = await prompts({
type: 'confirm',
message: 'Deseja inicializar um repositório Git?',
Expand Down Expand Up @@ -176,13 +193,16 @@ class LiquidoPontoEntrada {
const gerenciadorDePacotes =
perguntaQualGerenciadorDePacotesQuerUsar.gerenciadorDePacotes;

const linguagemSelecionada = perguntaLinguagemDeBackEnd.linguagemBackEnd;

await detectarGerenciadorDePacotes(
gerenciadorDePacotes,
diretorioCompleto
);

await copiarArquivosDeExemploParaNovoProjeto(
nomeProjeto,
linguagemSelecionada,
perguntaTipoProjeto.tipoProjeto,
diretorioCompleto
);
Expand Down
86 changes: 86 additions & 0 deletions infraestrutura/avaliador-sintatico-liquido.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { AvaliadorSintaticoComImportacao } from '@designliquido/delegua-node';
import { AvaliadorSintaticoPituguesComImportacao } from '@designliquido/delegua-node/avaliador-sintatico/dialetos/avaliador-sintatico-pitugues-com-importacao';
import { Importador } from '@designliquido/delegua-node/importador';
import tiposDeSimbolos from '@designliquido/delegua/tipos-de-simbolos/pitugues';
import { Decorador } from '@designliquido/delegua';

export class AvaliadorSintaticoDeleguaLiquido extends AvaliadorSintaticoComImportacao {
constructor(importador: Importador) {
super(importador);
}
}

export class AvaliadorSintaticoPituguesLiquido extends AvaliadorSintaticoPituguesComImportacao {
tiposDeFerramentasExternas: {
[nomeFerramenta: string]: {
[nomeTipo: string]: string;
};
};

constructor(importador: Importador) {
super(importador);
}

protected async resolverDecoradores(): Promise<void> {
while (this.verificarTipoSimboloAtual(tiposDeSimbolos.ARROBA)) {
this.avancarEDevolverAnterior();

let nomeDecorador = '';
let linha: number;
const atributos: { [key: string]: any } = {};

const primeiraParteNomeDecorador = this.consumir(
tiposDeSimbolos.IDENTIFICADOR,
'Esperado nome de decorador após "@".'
);

linha = Number(primeiraParteNomeDecorador.linha);
nomeDecorador += primeiraParteNomeDecorador.lexema;

while (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.PONTO)) {
const parteNomeDecorador = this.consumir(
tiposDeSimbolos.IDENTIFICADOR,
'Esperado nome de decorador após "."'
);

nomeDecorador += '.' + parteNomeDecorador.lexema;
}

if (
this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.PARENTESE_ESQUERDO)
) {
if (
!this.verificarTipoSimboloAtual(tiposDeSimbolos.PARENTESE_DIREITO)
) {
let indexArgumento = 0;

do {
const valorExpressao = await this.expressao();

atributos[indexArgumento] = valorExpressao;

if (indexArgumento === 0) {
atributos['caminho'] = valorExpressao;
}

indexArgumento++;
} while (this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.VIRGULA));
}

this.consumir(
tiposDeSimbolos.PARENTESE_DIREITO,
'Esperado ")" após argumentos do decorador.'
);
}

this.pilhaDecoradores.push(
new Decorador(
this.hashArquivo,
linha,
nomeDecorador,
atributos
)
);
}
}
}
3 changes: 2 additions & 1 deletion infraestrutura/centro-configuracoes/configuracao-liquido.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { ConfiguracaoDados } from "./configuracao-dados";
import { ConfiguracaoRoteador } from "./configuracao-roteador";

export class ConfiguracaoLiquido extends ConfiguracaoComum {
arquetipo?: 'rest' | 'mvc';
arquetipo: 'rest' | 'mvc';
linguagem: 'delegua' | 'pitugues';
aplicacao: ConfiguracaoAplicacao;
autenticacao: ConfiguracaoAutenticacao;
dados: ConfiguracaoDados;
Expand Down
8 changes: 7 additions & 1 deletion infraestrutura/interpretador-liquido.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { InterpretadorComImportacao } from "@designliquido/delegua-node/interpretador";
import { InterpretadorPituguesComImportacao } from "@designliquido/delegua-node/interpretador/dialetos/interpretador-pitugues-com-importacao";


/**
* A única função deste interpretador é resolver bugs que precisam ser repassados
* para o núcleo de Delégua.
*/
export class InterpretadorLiquido extends InterpretadorComImportacao {


}

export class InterpretadorLiquidoPitugues extends InterpretadorPituguesComImportacao {

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Configuração da aplicação em si.
liquido.arquetipo = 'rest'
liquido.linguagem = 'delégua'
liquido.aplicacao.nome = 'Minha aplicação'
liquido.aplicacao.versao = '0.0.0'
liquido.aplicacao.descricao = 'A minha aplicação é uma API REST.'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Configuração da aplicação em si.
liquido.arquetipo = 'mvc'
liquido.linguagem = 'delégua'
liquido.aplicacao.nome = 'Minha aplicação'
liquido.aplicacao.versao = '0.0.0'
liquido.aplicacao.descricao = 'A minha aplicação é um MVC.'
Expand Down
180 changes: 180 additions & 0 deletions interface-linha-comando/exemplos/pitugues/api-rest/LEIAME.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# API REST

Este padrão de projeto implementa o que chamamos de API (_Application Programming Interface_, ou Interface de Programação de Aplicação) REST (_Representational State Transfer_, ou Transferência de Estado Representacional).

## Não li e nem lerei

Se você não tem paciência para ler o documento inteiro, colocamos em `rotas/inicial.pitu` um exemplo funcional de rotas trabalhando com JSON. Há um outro documento `LEIAME.md` dentro do diretório `rotas` que pode ajudar.

Se tem, boa leitura.

## Siglas API e REST

É importante entender o que cada uma dessas siglas significa separadamente.

**APIs** normalmente expõem componentes que são usados dentro de uma aplicação, como métodos, propriedades e classes, e são tipicamente distribuídas por pessoas ou empresas para uso por terceiros (ou consumidores). Por definição, uma interface não expõe como esses métodos, propriedades e classes são implementados. A maioria das interfaces possui uma documentação, e esta documentação orienta os consumidores sobre como utilizar cada componente da interface.

**REST** é um dos vários protocolos da Internet. Em REST, uma aplicação expõe uma série de recursos. Cada recurso é acessível através de um conjunto de endereços e métodos.

Cada endereço segue uma convenção que chamamos de URL (_Universal Resource Locator_, ou Localizador Universal de Recurso). _Sites_, ou sítios da internet, usualmente são acessíveis por um endereço que indica qual protocolo de transferência deve ser utilizando (HTTP e FTP são os mais populares, mas há muitos outros), seguido de `://` (dois-pontos e duas barras), um localizador DNS (_Domain Name Server_, ou Servidor de Nomes de Domínio) e um caminho. Por exemplo, `http://designliquido.com.br`. O protocolo é HTTP, e o endereço DNS da empresa que construiu Líquido é `designliquido.com.br` (`.com` quer dizer que é um sítio comercial, e `.br` quer dizer que fica no Brasil).

Ao acessar o sítio da Design Líquido no seu navegador de internet, o navegador assume um método (ou verbo) padrão. Por padrão, toda e qualquer requisição cujo método não esteja especificado usa o método `GET` (obter). Este método indica que queremos ler o conteúdo correspondente ao endereço. O servidor da Design Líquido irá receber esta requisição, montar uma página em HTML e devolver.

Todo sítio da internet é, por definição, uma API REST, que normalmente nos devolve HTML como retorno, mas nada nos impediria de retornar qualquer outra coisa serializável. HTML, XML e JSON são exemplos de formatos serializáveis.

APIs REST se tornaram muito populares com a criação de _smartphones_. Antes dos _smartphones_, um outro padrão de APIs dominava a internet, chamado SOAP. SOAP se parece muito com REST, mas suas APIs trabalhavam apenas com um método (`POST`) e suas respostas são bastante longas e verbosas, o que oneravam sobremaneira e desnecessariamente o processamento em um _smartphone_ da época, bem mais lento e limitado que o _smartphone_ mais barato hoje. Surgiu a necessidade de não apenas simplificar as APIs, como também deixar as respostas menores.

# Serialização

Serialização é um processo de estruturação de dados. Essa estruturação pode ser legível a seres humanos (por exemplo, JSON, XML, YAML) ou não (Protobuf, binário, etc.), sendo os formatos menos legíveis os mais otimizados para uso por máquinas. A serialização é feita por serializadores, e o processo de desestruturação desses dados é chamado de desserialização. Serializadores e desserializadores seguem a especificação do formato que implementam.

Existem centenas de formatos de serialização, que servem a diferentes propósitos. JSON e XML, por exemplo, são muito bons para estruturar dados aninhados, em uma enorme quantidade de detalhes.

A melhor forma de explicar serialização é por exemplos. Vamos supor que queremos construir uma API REST para um blog. O primeiro recurso que queremos implementar é o de leitura de artigos, e vamos supor que temos três artigos já escritos:

- Primeiro artigo
- Título: O que é REST?
- Texto: REST significa "Transferência de Estado Representacional".
- Autor: Leonel
- Segundo artigo
- Título: O que é API?
- Texto: API significa "Interface de Programação de Aplicação".
- Autor: Leonel
- Terceiro artigo
- Título: Bem-vindos!
- Texto: Este é meu blog.
- Autor: Leonel

Se queremos serializar o primeiro artigo em JSON, podemos fazê-lo da seguinte forma:

```json
{
"titulo": "O que é REST?",
"texto": "REST significa 'Transferência de Estado Representacional'.",
"autor": "Leonel"
}
```

Chaves, aspas duplas, dois-pontos e vírgulas são delimitadores. São usados para definir onde uma porção de informação começa e/ou termina, dependendo do caso. A vírgula, por exemplo, é usada para separar porções de dados. Dois-pontos é usado para separar uma chave de um valor. `"titulo"`, `"texto"` e `"autor"` são, portanto, chaves. O que vem após o sinal de dois-pontos é o valor correspondente a cada chave. Em JSON, todas as chaves são porções de dados delimitadas por aspas duplas, mas valores podem ser delimitados por aspas duplas, chaves, e até mesmo colchetes. Em JSON, o nome "chave" não é à toa: Não é possível um objeto ter duas chaves idênticas.

Colchetes são usados para definir listas de valores. Por exemplo, se queremos definir a lista de artigos numa representação JSON, podemos fazer algo assim:

```json
[
{
"titulo": "O que é REST?",
"texto": "REST significa 'Transferência de Estado Representacional'.",
"autor": "Leonel"
},
{
"titulo": "O que é API?",
"texto": "API significa 'Interface de Programação de Aplicação'.",
"autor": "Leonel"
},
{
"titulo": "Bem-vindos!",
"texto": "Este é meu blog.",
"autor": "Leonel"
}
]
```

Ou seja, temos três artigos, sendo cada artigo delimitado por chaves e separado por vírgulas. Cada artigo possui três pares chave-valor, separados por vírgulas. Os três artigos aparecem entre colchetes, indicando uma lista de artigos.

JSON é exatamente poderoso pela recombinação de diversos elementos de dados. Por exemplo, poderíamos fazer nosso recurso de listagem de artigos devolver não apenas uma lista de artigos, mas informações adicionais sobre eles. Por exemplo:

```json
{
"pagina": "Meu blog",
"totalArtigos": 3,
"artigos": [
{
"titulo": "O que é REST?",
"texto": "REST significa 'Transferência de Estado Representacional'.",
"autor": "Leonel"
},
{
"titulo": "O que é API?",
"texto": "API significa 'Interface de Programação de Aplicação'.",
"autor": "Leonel"
},
{
"titulo": "Bem-vindos!",
"texto": "Este é meu blog.",
"autor": "Leonel"
}
]
}
```

Ou seja, aninhamos os artigos como valor de uma chave `"artigos"`, dentro de um outro objeto, com outras chaves, que contém valores de diferentes tipos. Números, por exemplo, não requerem delimitadores.

## Serialização para JSON em Líquido

O método `.json()` do objeto `resposta` serializa um dicionário em Pituguês para a representação JSON. Dicionários em Pituguês são muito parecidos com objetos JSON, com algumas diferenças:

- Dicionários em Pituguês permitem chaves como números. Objetos JSON permitem apenas chaves delimitadas por aspas duplas.

Para usar, basta passar qualquer dicionário, seja literal ou variável, como argumento de `resposta.json()`:

```js
@liquido.rotaGet("/")
funcao minha_rota(requisicao, resposta):
resposta.json([{
"id": 1,
"titulo": "teste 1",
"descricao": "descricao 1"
}])
```

## Auto-documentação

Para projetos REST, Liquido possui capacidades de auto-documentação, ou seja, gerar uma série de documentos que explicam como a API REST que você está escrevendo irá funcionar.

Uma boa parte dos elementos são depreendidos pelo método de rota usado, o tipo de retorno usado para a resposta, e assim por diante. Outros podem ser adicionados por decoradores.

Do exemplo anterior:

```js
@liquido.rotaGet("/")
funcao minha_rota(requisicao, resposta):
resposta.json([{
"id": 1,
"titulo": "teste 1",
"descricao": "descricao 1"
}])
```

- Sabemos a rota pela posição do arquivo controlador na estrutura de diretórios;
- Sabemos que a rota responde pelo método `GET`;
- Sabemos que o tipo da resposta é feito por `resposta.json()`, portanto, um conteúdo JSON.

Nosso modelo de auto-documentação é o [OpenAPI 3.1.0](https://swagger.io/specification/). A geração dessa documentação pode ser feita de duas maneiras:

- Na inicialização do servidor;
- Por linha de comando.

### Decoradores para auto-documentação

Os decoradores suportados atualmente estão como no exemplo abaixo:

```js
@rest.documentacao(
sumario = "Um exemplo de rota GET.",
descricao = "Uma descrição mais detalhada sobre como a rota GET funciona.",
idOperacao = "lerArtigos",
etiquetas = ["artigos"]
)
@rest.resposta(
codigo = 200,
descricao = "Devolvido com sucesso",
formatos = ["application/json", "application/xml"]
)
@liquido.rotaGet("/")
funcao minha_rota(requisicao, resposta):
resposta.json([{
"id": 1,
"titulo": "teste 1",
"descricao": "descricao 1"
}])
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Configuração da aplicação em si.
liquido.arquetipo = 'rest'
liquido.linguagem = 'pituguês'
liquido.aplicacao.nome = 'Minha aplicação'
liquido.aplicacao.versao = '0.0.0'
liquido.aplicacao.descricao = 'A minha aplicação é uma API REST.'
liquido.aplicacao.licenca.nome = 'MIT'
liquido.aplicacao.licenca.url = 'https://github.com/DesignLiquido/liquido/LICENSE'

// Configuração de arquivos estáticos
liquido.roteador.diretorioEstatico = 'publico'

// Configuração do roteador.
liquido.roteador.cors = verdadeiro
liquido.roteador.bodyParser = verdadeiro
liquido.roteador.morgan = verdadeiro
liquido.roteador.cookieParser = verdadeiro
liquido.roteador.passport = falso
liquido.roteador.json = verdadeiro
liquido.roteador.helmet = verdadeiro

// Configuração de bases de dados
liquido.dados.lincones.tecnologia = 'sqlite'
liquido.dados.lincones.caminho = ':memory:'
Loading
Loading