9 | `;
10 |
11 | let novo = document.createElement("div");
12 | novo.innerHTML = html;
13 | $msgs.appendChild(novo);
14 | });
15 | }
16 |
17 | get_messages()
18 | .then(function () {
19 | render();
20 | });
21 |
--------------------------------------------------------------------------------
/tst/sockets/hello/README.md:
--------------------------------------------------------------------------------
1 | # Servidor _Hello, World!_
2 |
3 | > Importante: Este código deve ser executado usando **Python 3**.
4 |
5 | Para executar o servidor, digite:
6 |
7 | ```
8 | $ python3 hello.py
9 | ```
10 |
11 | Deixe o servidor executando e, em seguida, em outro terminal
12 | conecte-se ao servidor, usando `netcat` (`nc` no mac). Use o
13 | seguinte comando:
14 |
15 | ```
16 | $ netcat localhost 9090
17 | ```
18 |
19 | Observe que `9090` é a porta default usada pelo servidor. Se você
20 | quiser (ou precisar) rodar o servidor em outra porta, adicione ao
21 | comando de execução do servidor um último argumento, indicando a
22 | porta a ser usada.
23 |
--------------------------------------------------------------------------------
/06.web_apps/3-frontend_mural/mural-v9/app.js:
--------------------------------------------------------------------------------
1 | import {get_messages, messages} from './messages.js';
2 | import './mural.js';
3 |
4 | function render() {
5 | let $msgs = document.getElementById("msgs");
6 | $msgs.innerHTML = '';
7 | messages.forEach(function (message) {
8 | let novo = document.createElement("ps-message");
9 | novo.setAttribute('message', message.message);
10 | novo.setAttribute('author', message.author);
11 | novo.setAttribute('at', message.at);
12 | $msgs.appendChild(novo);
13 | });
14 | }
15 |
16 | async function init() {
17 | await get_messages();
18 | render()
19 | }
20 |
21 | init();
22 |
--------------------------------------------------------------------------------
/06.web_apps/2-imc_padrao_mvc/imc_v4/imc.js:
--------------------------------------------------------------------------------
1 | const $altura = document.querySelector("#altura");
2 | const $peso = document.querySelector("#peso");
3 | const $imc = document.querySelector("#imc");
4 |
5 | const model = {
6 | peso: null,
7 | altura: null,
8 | imc: function imc() {
9 | return this.peso / this.altura ** 2;
10 | }
11 | };
12 |
13 | function calcule_imc() {
14 | model.peso = Number($peso.value);
15 | model.altura = Number($altura.value);
16 | atualize_imc(model.imc());
17 | };
18 |
19 | function atualize_imc(imc) {
20 | $imc.innerText = "imc = " + imc.toFixed(2);
21 | }
22 |
23 | $peso.onkeyup = calcule_imc;
24 | $altura.onkeyup = calcule_imc;
25 |
--------------------------------------------------------------------------------
/tst/sockets/hello/hello.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import sys
3 |
4 | # porta default 9090 (ou o que vier na linha de comando)
5 | porta = int(sys.argv[1] if len(sys.argv) > 1 else 9090)
6 |
7 | # cria o socket
8 | s = socket.socket()
9 | s.bind(('localhost', porta))
10 |
11 | # faz o socket ouvir
12 | # … é isto que o caracteriza como um servidor
13 | s.listen()
14 |
15 | # aguarda uma conexão
16 | print('Aguardando conexões na porta %s...' % porta)
17 | conexao, endereco = s.accept()
18 |
19 | # agora já temos uma conexão
20 | print('Conexão estabelecida de %s:%s' % endereco)
21 | conexao.send('Hello, World!\n'.encode('utf-8'))
22 |
23 | # é importante fechar os sockets
24 | s.close()
25 | conexao.close()
26 |
--------------------------------------------------------------------------------
/tst/threads/mthreads2.py:
--------------------------------------------------------------------------------
1 | from threading import Thread
2 | import time
3 |
4 | class Contador(Thread):
5 | def __init__(self, nome, segundos, intervalo):
6 | super(Contador, self).__init__()
7 | self.nome = nome
8 | self.segundos = segundos
9 | self.intervalo = intervalo
10 |
11 | def run(self):
12 | while self.segundos:
13 | print("%s: %s" % (self.nome, self.segundos))
14 | time.sleep(self.intervalo / 1000.0)
15 | self.segundos -= 1
16 |
17 | print('Iniciando contador 1 ("5 Segundos")')
18 | Contador("5 Segundos", 5, 1000).start()
19 | print('Iniciando contador 2 ("A")')
20 | Contador("A", 15, 300).start()
21 | print('Fim')
22 |
--------------------------------------------------------------------------------
/01.como_funciona_a_web/servidor/hello.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import sys
3 |
4 | # porta default 9090 (ou o que vier na linha de comando)
5 | porta = int(sys.argv[1] if len(sys.argv) > 1 else 9090)
6 |
7 | # cria o socket
8 | s = socket.socket()
9 | s.bind(('localhost', porta))
10 |
11 | # faz o socket ouvir
12 | # … é isto que o caracteriza como um servidor
13 | s.listen()
14 |
15 | # aguarda uma conexão
16 | print('Aguardando conexões na porta %s...' % porta)
17 | conexao, endereco = s.accept()
18 |
19 | # agora já temos uma conexão
20 | print('Conexão estabelecida de %s:%s' % endereco)
21 | conexao.send('Hello, World!\n'.encode('utf-8'))
22 |
23 | # é importante fechar os sockets
24 | s.close()
25 | conexao.close()
26 |
--------------------------------------------------------------------------------
/tst/sockets/echo/echo.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import sys
3 |
4 | # porta default 9090 (ou o que vier na linha de comando)
5 | porta = int(sys.argv[1] if len(sys.argv) > 1 else 9090)
6 |
7 | with socket.socket() as s:
8 | s.bind(('localhost', porta))
9 | s.listen()
10 |
11 | print('Aguardando conexões na porta %s...' % porta)
12 | conexao, endereco = s.accept()
13 | with conexao:
14 | print('Conexão estabelecida de %s:%s' % endereco)
15 | while True:
16 | print('Aguardando mensagem de %s:%s' % endereco)
17 | mensagem = conexao.recv(4096)
18 | print('Mensagem: %s' % mensagem)
19 | if not mensagem: break
20 | conexao.sendall(mensagem)
21 |
--------------------------------------------------------------------------------
/01.como_funciona_a_web/servidor/echo.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import sys
3 |
4 | # porta default 9090 (ou o que vier na linha de comando)
5 | porta = int(sys.argv[1] if len(sys.argv) > 1 else 9090)
6 |
7 | with socket.socket() as s:
8 | s.bind(('localhost', porta))
9 | s.listen()
10 |
11 | print('Aguardando conexões na porta %s...' % porta)
12 | conexao, endereco = s.accept()
13 | with conexao:
14 | print('Conexão estabelecida de %s:%s' % endereco)
15 | while True:
16 | print('Aguardando mensagem de %s:%s' % endereco)
17 | mensagem = conexao.recv(4096)
18 | print('Mensagem: %s' % mensagem)
19 | if not mensagem: break
20 | conexao.sendall(mensagem)
21 |
--------------------------------------------------------------------------------
/01.como_funciona_a_web/servidor/mthreads2.py:
--------------------------------------------------------------------------------
1 | from threading import Thread
2 | import time
3 |
4 | class Contador(Thread):
5 | def __init__(self, nome, segundos, intervalo):
6 | super(Contador, self).__init__()
7 | self.nome = nome
8 | self.segundos = segundos
9 | self.intervalo = intervalo
10 |
11 | def run(self):
12 | while self.segundos:
13 | print("%s: %s" % (self.nome, self.segundos))
14 | time.sleep(self.intervalo / 1000.0)
15 | self.segundos -= 1
16 |
17 | print('Iniciando contador 1 ("5 Segundos")')
18 | Contador("5 Segundos", 5, 1000).start()
19 | print('Iniciando contador 2 ("A")')
20 | Contador("A", 15, 300).start()
21 | print('Contadores iniciados...')
22 |
--------------------------------------------------------------------------------
/06.web_apps/3-frontend_mural/mural-v5/app.js:
--------------------------------------------------------------------------------
1 | import {get_messages, messages} from './messages.js';
2 |
3 | function render() {
4 | let $msgs = document.getElementById("msgs");
5 | $msgs.innerHTML = '';
6 | messages.forEach(function (message) {
7 | let html = `
10 |
${message.message}
11 |
${message.author}
12 | `;
13 |
14 | let novo = document.createElement("div");
15 | novo.innerHTML = html;
16 | $msgs.appendChild(novo);
17 | });
18 | }
19 |
20 | get_messages()
21 | .then(function () {
22 | render();
23 | });
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Projeto de Software CC/UFCG 2019.1
2 |
3 | Este repositório reúne as notas de aula, os exercícios e os
4 | exemplos usados em sala de aula. Sugiro que cada aluno da
5 | disciplina se registre como _watcher_ do repositório para que
6 | receba notificações de adições/modificações dos arquivos.
7 |
8 | > Importante. Os arquivos aqui registrados só mostram o arquivo
9 | > final de um exemplo e não os estágios intermediários pelos
10 | > quais passamos em sala de aula. Isso significa que algum
11 | > aspecto importante possa não ficar evidente nos arquivos aqui
12 | > armazenados. Em alguns casos, procuro criar versões numeradas
13 | > dos arquivos para refletir a evolução do exemplo.
14 |
15 | ## Conteúdo
16 |
17 | - [Web Apps](06.web_apps/text.md)
18 |
--------------------------------------------------------------------------------
/06.web_apps/3-frontend_mural/mural-v1/app.js:
--------------------------------------------------------------------------------
1 | window.messages = [];
2 |
3 | function render() {
4 | let $msgs = document.getElementById("msgs");
5 | $msgs.innerHTML = '';
6 | messages.forEach(function (message) {
7 | let html = `
34 | `;
35 |
36 | let novo = document.createElement("div");
37 | let shadow = novo.attachShadow({"mode": "open"});
38 | shadow.innerHTML = html;
39 | $msgs.appendChild(novo);
40 | });
41 | }
42 |
43 | get_messages()
44 | .then(function () {
45 | render();
46 | });
47 |
--------------------------------------------------------------------------------
/01.como_funciona_a_web/servidor_http.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Um mini servidor HTTP
3 | ---
4 | # Um minúsculo servidor HTTP
5 |
6 | Neste roteiro, escreveremos um pequeno servidor HTTP que servirá
7 | alguns dados estáticos lidos do sistema de arvivos, bem como
8 | alguns dados dinâmicos. O servidor terá poucos recursos e
9 | reconhecerá pouquíssimos verbos em cada um deles. Mas deve ser o
10 | suficiente, para melhorar seu entendimento do funcionamento do
11 | protocolo e dos dois principais componentes da web: clientes e
12 | servidores.
13 |
14 | ## Servidor Base
15 |
16 | O nosso primeiro servidor deve apenas atender a um `GET` no
17 | recurso base `/`. A resposta será uma mensagem simples em texto
18 | pleno (mime type `text/plain`). Além disso, nesta primeira
19 | versão, ele não será sequer multi-threaded. O servidor deve
20 | apenas atender a uma conexão TCP e aceitar um _request_ mínimo no
21 | seguinte formato:
22 |
23 | ```
24 | GET / HTTP/1.1
25 | Host: www.example.com
26 | Connection: close
27 |
28 | ```
29 |
30 | Observe dois detalhes do _request_ acima: i) ao final de cada
31 | linha, o protocolo http exige o uso de `\r\n`, mas nosso servidor
32 | irá tolerar o uso de apenas `\n`, embora tenha que aceitar o
33 | `\r\n` também (veja que Python facilita sua vida nesse sentido);
34 | ii) ao final dos _headers_ é obrigatória a presença de uma linha
35 | em branco (ou seja, dois grupos de `\r\n` seguidos). Depois
36 | disso, tudo o que se seguir é o corpo da mensagem.
37 |
38 | Escreva o código do servidor que receba uma conexão HTTP e que
39 | dado um _request_ qualquer separe a mensagem em todas as partes
40 | que compõem o _request_: i) verbo, recurso, protocolo (os três
41 | obtidos da primeira linha da mensagem e representados em Python
42 | por strings); ii) os _headers_ representados por um dicionário
43 | Python em que chaves são os _nomes_ e os valores são os _valores_
44 | dos _headers_; e iii) o corpo da mensagem, representado também
45 | como uma string Python. Todos os dados devem ser estruturados em
46 | um objeto ou em um simples dicionário Python. Escreva o código
47 | que faz essa decomposição em uma função chamada
48 | `parse_request(mensagem)`que recebe a mensagem recebida via
49 | socket. Se a função não “conseguir” fazer o parsing, deve
50 | retornar `None` ou lançar uma exceção, de forma que o código
51 | cliente saiba que a mensagem está mal formada ou não é HTTP.
52 |
53 | O código principal deve fazer o _parsing_ da mensagem usando a
54 | função acima descrita. Com o objeto retornado, o código principal
55 | deve verificar se está tudo ok. Primeiro, o verbo deve ser `GET`,
56 | segundo o recurso deve ser `/` (o protocolo deve ser `HTTP/1.1`,
57 | mas nós vamos simplesmente ignorá-lo). Para casos diferentes
58 | disso, o servidor deve gerar _responses_ de erro. O conteúdo da
59 | mensagem, contudo, pode variar. Se o método não for `GET`, o
60 | _response_ deve ter [_status
61 | code_](https://www.restapitutorial.com/httpstatuscodes.html) 405,
62 | para indicar que aquele método não é permitido. E se o recurso
63 | não for `/` o _response_ deve ter _status code_ 404, indicando
64 | que o recurso não existe ali. Se, contudo, esses dados estiverem
65 | corretos, vamos produzir um _response_ com _status code_ 200,
66 | para indicar que o recurso existe naquele servidor e que tudo
67 | ocorreu bem. O corpo do _response_ no caso de tudo estar ok deve
68 | ser simplesmente uma mensagem do tipo
69 |
70 | ```
71 | Este é o conteúdo do recurso "/" neste servidor.
72 | ```
73 |
74 | Observe que esse texto contém caracteres acentuados. Logo, é
75 | importante adicionar o _header_ apropriado na resposta. Neste
76 | caso, trata-se do _header_ `Content-type` que deve ter o valor
77 | `text/html; charset=utf-8`. Observe que o valor indica tanto o
78 | mime type (`text/html`), quanto o conjunto de caracteres que,
79 | neste caso, é `utf-8`.
80 |
81 | ### Testes
82 |
83 | Para testar esse servidor, faça a conexão inicialmente pelo
84 | `netcat` (ou `nc` no Mac). Em seguida, conecte-se usando um
85 | _browser_. Lembre de colocar o endereço, incluindo a porta. Mude
86 | a url e veja se o servidor responde corretamente quando um
87 | recurso inexistente é pedido. Também veja se os caracteres são
88 | devidamente mostrados pelo _browser_, quando o único recurso
89 | existente é visualizado. Lembre-se, isso depende da presença
90 | correta do _header_ acima mencionado.
91 |
92 |
93 | ## Servidor de arquivos
94 |
95 | Evolua o servidor para que ele busque arquivos armazenados no
96 | diretório em que foi executado com o mesmo nome do recurso. Se o
97 | arquivo existir, leia o arquivo e retorne seu conteúdo no corpo
98 | do _response_. Se não existir, retorne um 404. Para testar,
99 | inclua alguns arquivos de texto simples no diretório e veja se
100 | consegue visualizar seu conteúdo pelo _browser_. Teste também com
101 | arquivos com extensão `.html`. Provavelmente, funcionará, mas não
102 | da forma que você espera... o html pode não ser devidamente
103 | interpretado pelo _browser_ por causa do _header_ `Content-type`.
104 |
105 | Evolua novamente seu servidor. Agora, se o arquivo enviado tiver
106 | extensão `.html`, faça com que o `Content-type` seja apropriado
107 | para html (faça uma pesquisa na internet para descobrir o mim
108 | type apropriado).
109 |
--------------------------------------------------------------------------------
/03.html/exercicios_1/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Exercícios ProjSW
8 |
14 |
15 |
18 |
19 |
20 |
21 |
Exercícios ProjSW
22 |
23 |
Introdução a HTML, CSS & JavaScript
24 |
Exercícios
25 |
26 |
Crie uma página pessoal no servidor Apache no LCC. Para isso, siga as instruções a seguir:
27 |
28 |
crie um diretório public_html dentro de seu diretório home de sua conta no LCC; é possível que um diretório já tenha sido criado pela administração, quando o Apache foi instalado;
29 |
certifique-se que as permissões de acesso ao diretório autorizem outros usuários a “entrar” e a ler o conteúdo do diretório; se não tiver certeza, digite o comando chmod 755 public_html (você deve estar no seu diretório home);
30 |
faça o mesmo com o seu diretório home; para que o servidor Apache possa ler seus dados, é necessário que ele tenha permissão de entrar no seu diretório public_html e isso inclui passar pelo seu diretório home; não se preocupe, a permissão é apenas para passar e ler, não para escrever; digite o comando chmod 755 ~;
31 |
se tudo estiver certo, você já deve conseguir acessar o conteúdo de seu diretório através de qualquer browser, através da internet; para testar, abra um browser e coloque a url de sua página que terá a forma http://kirk.lcc.ufcg.edu.br/~dalton/; você só deve trocar o nome do meu login no LCC, dalton, pelo seu próprio login; você deve ver apenas uma página gerada automaticamente pelo Apache bem ao estilo do que vimos em sala de aula quando usamos o mini servidor de Python que é uma espécie de índice criado automaticamente pelo Apache, com base no conteúdo do diretório;
32 |
33 |
Adicione alguns arquivos ao diretório public_html (nenhum deles deve ser nomeado index.html, deixe isso pros próximos passos). Mas cuidado, lembre-se que esses arquivos ficarão publicamente disponíveis na internet para qualquer usuário.
34 |
Crie também um subdiretório. Escolha o nome você mesmo, mas evite nomes com espaços ou acentuação. Observe que você pode acessar esse diretório simplesmente adicionando uma barra e o nome do diretório ao final da url de sua página. Observe também que o Apache irá criar uma página índice para esse diretório também, tal como ocorreu com o diretório base.
35 |
Crie um arquivo index.html (vazio) no diretório public_html (você pode usar o comando touch index.html para criar esse arquivo vazio). Observe que ao fazer isso, mesmo que o arquivo seja vazio, o Apache deixa de criar o índice automático que você viu nos passos anteriores. Agora o índice da página pode ser controlado por você, editando o arquivo index.html da forma que você achar mais conveniente.
36 |
37 |
Observe que ao usarmos a url dos subdiretórios criados no passo anterior, o Apache continuará funcionando como antes, criando um índice automático. Isso ocorre porque esse é o comportamento default do Apache. Para mudar o conteúdo de um subdiretório, simplesmente adicione outro arquivo index.html dentro do subdiretório.
38 |
O mesmo ocorre com os arquivos que estiverem em public_html. Eles continuam acessíveis pelo browser. A diferença é que agora você precisa saber a url para poder acessá-los, já que não há um índice com o link.
39 |
40 |
Edite o arquivo index.html para fazer uma simples página pessoal. Esse arquivo deve ser um simples arquivo HTML5 (estritamente válido) que defina em sua página as partes clássicas de uma página web: cabeçalho, rodapé, área de navegação e uma área para o conteúdo principal. Coloque o que quiser dentro de cada uma delas, mas observe o bom uso das tags semânticas de HTML5 (se decidir usar figuras, siga também as convenções de HTML semântico). No corpo principal, adicione conteúdo que identifique você pessoalmente e seus interesses. Pesquise sobre CSS e faça um arquivo de estilos para sua página.
41 |
Crie o subdiretório imc. Dentro dele, recrie o mini app que fizemos em sala de aula que calcula o IMC de uma pessoa. Contudo, desta vez, adicione um comportamento. O app deve informar se a pessoa se encontra abaixo ou acima do peso sugerido pelas recomendações de saúde. Adicione um css à página.
Crie uma página pessoal no servidor Apache no LCC. Para isso, siga as instruções a seguir:
27 |
28 |
crie um diretório public_html dentro de seu diretório home de sua conta no LCC; é possível que um diretório já tenha sido criado pela administração, quando o Apache foi instalado;
29 |
certifique-se que as permissões de acesso ao diretório autorizem outros usuários a “entrar” e a ler o conteúdo do diretório; se não tiver certeza, digite o comando chmod 755 public_html (você deve estar no seu diretório home);
30 |
faça o mesmo com o seu diretório home; para que o servidor Apache possa ler seus dados, é necessário que ele tenha permissão de entrar no seu diretório public_html e isso inclui passar pelo seu diretório home; não se preocupe, a permissão é apenas para passar e ler, não para escrever; digite o comando chmod 755 ~;
31 |
se tudo estiver certo, você já deve conseguir acessar o conteúdo de seu diretório através de qualquer browser, através da internet; para testar, abra um browser e coloque a url de sua página que terá a forma http://kirk.lcc.ufcg.edu.br/~dalton/; você só deve trocar o nome do meu login no LCC, dalton, pelo seu próprio login; você deve ver apenas uma página gerada automaticamente pelo Apache bem ao estilo do que vimos em sala de aula quando usamos o mini servidor de Python que é uma espécie de índice criado automaticamente pelo Apache, com base no conteúdo do diretório;
32 |
33 |
Adicione alguns arquivos ao diretório public_html (nenhum deles deve ser nomeado index.html, deixe isso pros próximos passos). Mas cuidado, lembre-se que esses arquivos ficarão publicamente disponíveis na internet para qualquer usuário.
34 |
Crie também um subdiretório. Escolha o nome você mesmo, mas evite nomes com espaços ou acentuação. Observe que você pode acessar esse diretório simplesmente adicionando uma barra e o nome do diretório ao final da url de sua página. Observe também que o Apache irá criar uma página índice para esse diretório também, tal como ocorreu com o diretório base.
35 |
Crie um arquivo index.html (vazio) no diretório public_html (você pode usar o comando touch index.html para criar esse arquivo vazio). Observe que ao fazer isso, mesmo que o arquivo seja vazio, o Apache deixa de criar o índice automático que você viu nos passos anteriores. Agora o índice da página pode ser controlado por você, editando o arquivo index.html da forma que você achar mais conveniente.
36 |
37 |
Observe que ao usarmos a url dos subdiretórios criados no passo anterior, o Apache continuará funcionando como antes, criando um índice automático. Isso ocorre porque esse é o comportamento default do Apache. Para mudar o conteúdo de um subdiretório, simplesmente adicione outro arquivo index.html dentro do subdiretório.
38 |
O mesmo ocorre com os arquivos que estiverem em public_html. Eles continuam acessíveis pelo browser. A diferença é que agora você precisa saber a url para poder acessá-los, já que não há um índice com o link.
39 |
40 |
Edite o arquivo index.html para fazer uma simples página pessoal. Esse arquivo deve ser um simples arquivo HTML5 (estritamente válido) que defina em sua página as partes clássicas de uma página web: cabeçalho, rodapé, área de navegação e uma área para o conteúdo principal. Coloque o que quiser dentro de cada uma delas, mas observe o bom uso das tags semânticas de HTML5 (se decidir usar figuras, siga também as convenções de HTML semântico). No corpo principal, adicione conteúdo que identifique você pessoalmente e seus interesses. Pesquise sobre CSS e faça um arquivo de estilos para sua página.
41 |
Crie o subdiretório imc. Dentro dele, recrie o mini app que fizemos em sala de aula que calcula o IMC de uma pessoa. Contudo, desta vez, adicione um comportamento. O app deve informar se a pessoa se encontra abaixo ou acima do peso sugerido pelas recomendações de saúde. Adicione um css à página.
Neste roteiro, escreveremos um pequeno servidor HTTP que servirá alguns dados estáticos lidos do sistema de arvivos, bem como alguns dados dinâmicos. O servidor terá poucos recursos e reconhecerá pouquíssimos verbos em cada um deles. Mas deve ser o suficiente, para melhorar seu entendimento do funcionamento do protocolo e dos dois principais componentes da web: clientes e servidores.
25 |
Servidor Base
26 |
O nosso primeiro servidor deve apenas atender a um GET no recurso base /. A resposta será uma mensagem simples em texto pleno (mime type text/plain). Além disso, nesta primeira versão, ele não será sequer multi-threaded. O servidor deve apenas atender a uma conexão TCP e aceitar um request mínimo no seguinte formato:
27 |
GET / HTTP/1.1
28 | Host: www.example.com
29 | Connection: close
30 |
31 |
Observe dois detalhes do request acima: i) ao final de cada linha, o protocolo http exige o uso de \r\n, mas nosso servidor irá tolerar o uso de apenas \n, embora tenha que aceitar o \r\n também (veja que Python facilita sua vida nesse sentido); ii) ao final dos headers é obrigatória a presença de uma linha em branco (ou seja, dois grupos de \r\n seguidos). Depois disso, tudo o que se seguir é o corpo da mensagem.
32 |
Escreva o código do servidor que receba uma conexão HTTP e que dado um request qualquer separe a mensagem em todas as partes que compõem o request: i) verbo, recurso, protocolo (os três obtidos da primeira linha da mensagem e representados em Python por strings); ii) os headers representados por um dicionário Python em que chaves são os nomes e os valores são os valores dos headers; e iii) o corpo da mensagem, representado também como uma string Python. Todos os dados devem ser estruturados em um objeto ou em um simples dicionário Python. Escreva o código que faz essa decomposição em uma função chamada parse_request(mensagem)que recebe a mensagem recebida via socket. Se a função não “conseguir” fazer o parsing, deve retornar None ou lançar uma exceção, de forma que o código cliente saiba que a mensagem está mal formada ou não é HTTP.
33 |
O código principal deve fazer o parsing da mensagem usando a função acima descrita. Com o objeto retornado, o código principal deve verificar se está tudo ok. Primeiro, o verbo deve ser GET, segundo o recurso deve ser / (o protocolo deve ser HTTP/1.1, mas nós vamos simplesmente ignorá-lo). Para casos diferentes disso, o servidor deve gerar responses de erro. O conteúdo da mensagem, contudo, pode variar. Se o método não for GET, o response deve ter status code 405, para indicar que aquele método não é permitido. E se o recurso não for / o response deve ter status code 404, indicando que o recurso não existe ali. Se, contudo, esses dados estiverem corretos, vamos produzir um response com status code 200, para indicar que o recurso existe naquele servidor e que tudo ocorreu bem. O corpo do response no caso de tudo estar ok deve ser simplesmente uma mensagem do tipo
34 |
Este é o conteúdo do recurso "/" neste servidor.
35 |
Observe que esse texto contém caracteres acentuados. Logo, é importante adicionar o header apropriado na resposta. Neste caso, trata-se do headerContent-type que deve ter o valor text/html; charset=utf-8. Observe que o valor indica tanto o mime type (text/html), quanto o conjunto de caracteres que, neste caso, é utf-8.
36 |
Testes
37 |
Para testar esse servidor, faça a conexão inicialmente pelo netcat (ou nc no Mac). Em seguida, conecte-se usando um browser. Lembre de colocar o endereço, incluindo a porta. Mude a url e veja se o servidor responde corretamente quando um recurso inexistente é pedido. Também veja se os caracteres são devidamente mostrados pelo browser, quando o único recurso existente é visualizado. Lembre-se, isso depende da presença correta do header acima mencionado.
38 |
Servidor de arquivos
39 |
Evolua o servidor para que ele busque arquivos armazenados no diretório em que foi executado com o mesmo nome do recurso. Se o arquivo existir, leia o arquivo e retorne seu conteúdo no corpo do response. Se não existir, retorne um 404. Para testar, inclua alguns arquivos de texto simples no diretório e veja se consegue visualizar seu conteúdo pelo browser. Teste também com arquivos com extensão .html. Provavelmente, funcionará, mas não da forma que você espera… o html pode não ser devidamente interpretado pelo browser por causa do headerContent-type.
40 |
Evolua novamente seu servidor. Agora, se o arquivo enviado tiver extensão .html, faça com que o Content-type seja apropriado para html (faça uma pesquisa na internet para descobrir o mim type apropriado).
41 |
42 |
43 |
--------------------------------------------------------------------------------
/06.web_apps/1-padrao_mvc/text.md:
--------------------------------------------------------------------------------
1 | # MVC
2 |
3 | ## O Padrão Arquitetural MVC
4 |
5 | MVC (_Model_, _View_, _Controller_) é um padrão arquitetural
6 | usado para o desenvolvimento de aplicações e suas interfaces com
7 | o usuário. Compreender o padrão MVC é fundamental para qualquer
8 | desenvolvedor, dado que não apenas aplicações _web_, mas também
9 | aplicações _mobile_ e _desktop_ têm sido historicamente
10 | desenvolvidas com base nesse padrão.
11 |
12 | O padrão consiste em decompor a aplicação em três componentes,
13 | chamados _model_, _view_ e _controller_, e em disciplinar a
14 | interação entre eles. Seu objetivo é promover o desacoplamento
15 | das partes envolvidas e permitir seu desenvolvimento de forma
16 | independente e paralela.
17 |
18 | Neste roteiro, estudaremos o padrão arquitetural MVC e sua
19 | aplicação no desenvolvimento de _frontends web_, através de um
20 | exemplo prático. Nesta primeira parte veremos os conceitos de
21 | MVC. Na segunda, você será conduzido na construção de um exemplo
22 | de aplicação baseada no padrão MVC.
23 |
24 | ### Padrões arquiteturais X padrões de _design_
25 |
26 | Antes de iniciarmos, é importante destacarmos a diferença entre
27 | padrões arquiteturais e padrões de _design_. Em padrões
28 | arquiteturais, o foco não é no nível de classes ou funções, como
29 | ocorre nos padrões de _design_ e, sim, nos grandes componentes ou
30 | módulos do sistema. Um componente é um conjunto de funções,
31 | classes, dados e até mesmo processos do sistema. Os padrões
32 | arquiteturais, portanto, não expressam detalhes de como classes e
33 | objetos serão interconectados, mas como o sistema é entendido e
34 | decomposto em grandes partes e como essas partes se relacionam.
35 | Padrões arquiteturais operam em um nível de abstração bem maior
36 | que padrões de _design_. Por um lado isso permite que os padrões
37 | se apliquem a um número maior de linguagens e tecnologias. Por
38 | outro, implica que sua concretização é muito mais aberta a
39 | interpretações e variações do que padrões de _design_.
40 |
41 | > Observação. As linguagens que usamos não provêem mecanismos
42 | > explícitos para a decomposição em componentes no sentido
43 | > arquitetural da palavra. Por esse motivo, nem sempre há
44 | > registro explícito e absoluto do mapeamento dos elementos aos
45 | > componentes a que pertencem. Nem mesmo a decomposição física
46 | > (em arquivos e diretórios) pode ser tomada como registro do
47 | > mapeamento arquitetural de um sistema. São diversos os casos em
48 | > que elementos colocados nos mesmos arquivos ou diretórios
49 | > pertencem a diferentes componentes arquiteturais. Bem como há
50 | > casos em que elementos de um mesmo componente são colocados em
51 | > diferentes arquivos e/ou diretórios. Nesses casos, é importante
52 | > que o mapeamento arquitetural seja registrado/mantido de alguma
53 | > outra forma nos artefatos do sistema (padrões de nomeação,
54 | > tags, tipos de serviços fornecidos, etc) ou em documentação
55 | > complementar. Na prática, infelizmente, é comum que não haja
56 | > qualquer registro formal.
57 |
58 | ### Os componentes do MVC
59 |
60 | Os três componentes de MVC são: o _model_, a _view_ e o
61 | _controller_. A ideia central do padrão é que a lógica central do
62 | domínio da aplicação seja implementada pelo _model_ e que a
63 | _view_ e o _controller_ implementem a interface com o usuário.
64 | Vejamos como cada um dos componentes pode ser definido e como
65 | deve ser sua interação com os outros dois.
66 |
67 | > Observação. Quando tratamos do tema de forma conceitual, como
68 | > é o caso aqui, é comum nos referirmos a cada um dos componentes
69 | > no singular: o _model_, a _view_, o _controller_. Na prática,
70 | > contudo, não é raro que cada um dos componentes do MVC consista
71 | > em um conjunto de elementos que podem ser, independentemente,
72 | > chamados de _models_, _views_ e _controllers_.
73 |
74 | *O _model_* é o componente principal da aplicação. Ele reúne
75 | todos os elementos necessários para criar e gerenciar os dados e
76 | toda a lógica _de negócio_ da aplicação. O _model_ deve ser
77 | completamente independente da interface com o usuário. Isso
78 | permite que o componente seja utilizado em diferentes aplicações
79 | que explorem a mesma lógica, mas que tenham diferentes interfaces
80 | com o usuário.
81 |
82 | *A _view_* é o componente que provê a representação visual da
83 | informação para o usuário. São parte da _view_ todos os elementos
84 | que produzem ou manipulam visualizações.
85 |
86 | *O _Controller_* é o componente responsável por interpretar
87 | comandos do usuário e transformá-los em comandos para a _view_
88 | e/ou para o _model_. O _controller_ cria e manipula os dados da
89 | aplicação através dos serviços providos pelo _model_ e controla a
90 | _view_ da aplicação, através dos serviços providos pela _view_.
91 |
92 | ### Regras de interação dos componentes MVC
93 |
94 | As regras de interação entre os componentes no padrão MVC são,
95 | tipicamente, expressas através de uma figura abaixo como essa
96 | abaixo. Nela, as setas cheias representam _dependências
97 | permitidas_ de um componente em relação a outro. Elas indicam que
98 | o componente de origem da seta _pode depender_ do componente no
99 | destino da seta, seja instanciando objetos, invocando funções ou
100 | usando serviços. As setas tracejadas, por outro lado, indicam que
101 | o componente de origem _notifica_ o componente de destino da
102 | seta, em certas condições ou na ocorrência de eventos.
103 | Tipicamente, essa relação é implementada através de padrões de
104 | _design_ semelhantes ao _publish-subscribe_.
105 |
106 | 
107 |
108 | Analise a figura e perceba que ela ilustra que o _controller_
109 | pode depender tanto do _model_, quanto da _view_ (confira as
110 | setas cheias que saem do _controller_). E que a _view_ pode
111 | notificar o _controller_ sobre eventos e/ou mudanças que ocorram
112 | nela (veja a seta tracejada da _view_ para o _controller_).
113 | Tipicamente, os eventos que ocorrem na _view_ são os produzidos
114 | pelo usuário em sua interação com a interface. A figura mostra
115 | ainda que tanto o _controller_ quanto a _view_ podem depender do
116 | _model_ e, finalmente, que o _model_ não deve depender de nenhum
117 | dos dois (não há setas cheias saindo do _model_). Finalmente,
118 | vê-se que o _model_ também pode notificar a _view_ sobre eventos
119 | e/ou condições ocorridas dentro do próprio _model_.
120 |
121 | ### MVC e aplicações _web_
122 |
123 | Aplicações _web_ popularizaram o uso do padrão MVC. O padrão,
124 | contudo, já era usado para desenvolver aplicações _desktop_,
125 | aplicações _cliente-servidor_, aplicações _mobile_ e até mesmo
126 | aplicações de linha de comando. A popularização da _web_,
127 | contudo, colocou o padrão em um novo patamar de relevância e
128 | popularidade. Praticamente todos os sites com conteúdo dinâmico
129 | existentes usam, em última instância, o padrão MVC ou alguma
130 | variação dele.
131 |
132 | Perceba, por exemplo, que em um _frontend web_, o _DOM_ e sua
133 | renderização pelo _browser_ podem ser vistos como a _view_ da
134 | aplicação. Perceba ainda que os elementos interativos de html
135 | renderizados na _view_ permitem que o usuário interaja com a
136 | interface, seja movendo ou clicando o _mouse_ nos elementos ou
137 | digitando em algum _input_. E que o _browser_ captura esses
138 | eventos e os repassa para funções JavaScript que atuam como o
139 | _controller_ da aplicação. Essas funções, por sua vez, alteram
140 | valores de objetos na memória que são o _model_ da aplicação.
141 | Fechando o círculo, o _DOM_ (a _view_) é construída a partir dos
142 | objetos na memória (do _model_) da aplicação. A aplicação
143 | explícita do padrão MVC ajuda a melhor compreender o papel de
144 | cada parte de um _frontend_.
145 |
146 | Do lado _backend_ (servidor) também é fácil perceber o uso do
147 | padrão MVC. O típico _backend_ moderno é implementado como o que
148 | chamamos de uma API REST JSON. Na prática, trata-se de um
149 | conjunto de _handlers_ que recebem requisições HTTP e que
150 | produzem uma _visualização_ JSON de algum _modelo_ armazenado no
151 | servidor (tipicamente, em um banco de dados). Em última
152 | instância, os _handlers_ podem ser vistos como _controllers_ e os
153 | dados armazenados como _models_. Para completar a figura, observe
154 | que muitos servidores modernos mantêm conexões HTTP entre cliente
155 | e servidor para que o servidor possa notificar o cliente sobre
156 | mudanças de estado e/ou ocorrências de eventos, tal como previsto
157 | pelo padrão MVC.
158 |
159 | > Se você achar tudo isso abstrato, não se preocupe. De fato, o
160 | > padrão é abstrato. Nesse caso, tente lembrar de uma única regra
161 | > do padrão MVC: que _o model não depende nem da view, nem do
162 | > controller_. Na prática, isso significa que o _model_ *deve*
163 | > ser escrito sem nenhuma dependência estrutural dos outros dois
164 | > módulos. Consegue lembrar de uma segunda regra? Então lembre
165 | > desta: regras de negócio jamais devem ser colocadas na
166 | > interface do usuário (entenda-se, no _controller_ e na _view_).
167 | > Somente no _model_.
168 |
169 | ### Responda
170 |
171 | Depois de ler ao texto acima, você deveria saber responder às
172 | perguntas abaixo. Caso você não se sinta confortável, faça uma
173 | pesquisa sobre o tema antes de respondê-las.
174 |
175 | 1. O que significam as letras do acrônimo MVC?
176 |
177 | 2. O que é o _model_?
178 |
179 | 3. O que é a _view_?
180 |
181 | 4. O que é o _controller_?
182 |
183 | 5. Qual a diferença entre um padrão de _design_ e um padrão
184 | _arquitetural_?
185 |
186 | 6. Quais componentes do MVC formam a _interface com o usuário_ da
187 | aplicação?
188 |
189 | 7. Quais os benefícios de o _model_ não depender da _view_ e do
190 | _controller_?
191 |
192 | 8. Em alguns desenhos do padrão MVC, se coloca uma seta entre o
193 | _model_ e o _view_. Em outros também se coloca uma seta entre
194 | o _model_ e o _controller_. No desenho apresentado aqui,
195 | optamos por desenhar essas setas de forma tracejada. O que
196 | essas relações indicam? Podemos dizer que nesses desenhos se
197 | está dizendo que o _model_ depende da _view_ e do
198 | _controller_?
199 |
200 | 9. Podemos dizer que o padrão MVC promove a colocação de lógica
201 | de negócio no _model_? Por quê?
202 |
203 | ## Próxima atividade
204 |
205 | [Parte 2: IMC Padrao MVC](../2-imc_padrao_mvc/text.md)
206 |
--------------------------------------------------------------------------------
/06.web_apps/2-imc_padrao_mvc/text.md:
--------------------------------------------------------------------------------
1 | ## O aplicativo IMC à luz do padrão MVC
2 |
3 | Relembre o pequeno aplicativo que calcula o IMC. Mesmo um
4 | aplicativo tão simples pode se beneficiar da aplicação do padrão
5 | MVC. Nesta parte faremos um refatoramento de nossa minúscula
6 | aplicação para introduzir pouco a pouco os conceitos de MVC.
7 |
8 | ### Versão inicial
9 |
10 | Relembre que em nossa primeira versão do app IMC, construída em
11 | sala de aula, a função `calcule_imc()` era definida como abaixo.
12 |
13 | ```javascript
14 | const $altura = document.querySelector("#altura");
15 | const $peso = document.querySelector("#peso");
16 | const $imc = document.querySelector("#imc");
17 |
18 | function calcule_imc() {
19 | const peso = Number($peso.value);
20 | const altura = Number($altura.value);
21 | $imc.innerText = "imc = " + (peso / altura ** 2).toFixed(2);
22 | }
23 |
24 | $peso.onkeyup = calcule_imc;
25 | $altura.onkeyup = calcule_imc;
26 | ```
27 |
28 | A função `calcule_imc` acima claramente viola a decomposição
29 | segundo o padrão MVC. Tente, por exemplo, mapear a função para um
30 | dos componentes do padrão. Em sua opinião ela seria parte do
31 | _model_, da _view_ ou do _controller_? É difícil mapear essa
32 | função apropriadamente porque ela opera responsabilidades dos
33 | três componentes. Observe. Nela codificamos a fórmula do IMC e,
34 | por esse motivo, deveria ser considerada parte do _model_, afinal
35 | a fórmula é parte da lógica de negócio, que só pode estar
36 | localizada no _model_. Por outro lado, a função é usada como
37 | _handler_ de eventos `onkeyup` produzidos pelo usuário e,
38 | portanto, deveria ser considerada parte do _controller_.
39 | Finalmente, observe que a função redefine o valor de
40 | `$imc.innerText`, que é uma forma de atualizar a representação
41 | visual apresentada ao usuário e, por isso, deveria ser
42 | considerada parte da _view_. Podemos concluir que, se for escrito
43 | dessa forma, esse mini app não atende apropriadamente ao padrão
44 | MVC.
45 |
46 | Embora a função não atenda ao padrão MVC, ela está correta e
47 | atende ao propósito do app. Naturalmente, em um app ridiculamente
48 | pequeno como esse não há motivos para termos maiores
49 | preocupações. A título de exercício, contudo, vale a pena
50 | repensarmos o app para adaptá-lo ao padrão.
51 |
52 | ### Versão 2
53 |
54 | A segunda solução de nosso app, mostrada abaixo, evolui um pouco.
55 |
56 | ```javascript
57 | const $altura = document.querySelector("#altura");
58 | const $peso = document.querySelector("#peso");
59 | const $imc = document.querySelector("#imc");
60 |
61 | function calcule_imc() {
62 | const peso = Number($peso.value);
63 | const altura = Number($altura.value);
64 | const imc = valor_imc(peso, altura);
65 | $imc.innerText = "imc = " + valor_imc(peso, altura);
66 | }
67 |
68 | function valor_imc(peso, altura) {
69 | return peso / altura ** 2;
70 | }
71 |
72 | $peso.onkeyup = calcule_imc;
73 | $altura.onkeyup = calcule_imc;
74 | ```
75 |
76 | Nesta versão, a função `calcule_imc` não contém mais a fórmula de
77 | cálculo do IMC. A fórmula foi isolada na função `valor_imc()` de
78 | forma que a função `calcule_imc()` passa a atuar exclusivamente
79 | na interface, lendo e interpretando os dados digitados e fazendo
80 | a atualização do valor do IMC. Assim, podemos afirmar com
81 | bastante segurança que a função `valor_imc()` deve ser
82 | considerada parte do modelo e que a função `calcule_imc` é parte
83 | da interface com o usuário, muito embora ainda seja difícil ser
84 | mapeada para o _controller_ ou para a _view_, dado que ela ainda
85 | faz muitas coisas: lê a _view_, invoca uma função do _model_ e
86 | altera a _view_.
87 |
88 |
89 | ### Versão 3
90 |
91 | A terceira versão, abaixo, avança na mudança, isolando um pouco
92 | mais o código pertencente a cada componente em diferentes
93 | funções.
94 |
95 | ```javascript
96 | const $altura = document.querySelector("#altura");
97 | const $peso = document.querySelector("#peso");
98 | const $imc = document.querySelector("#imc");
99 |
100 | function calcule_imc() {
101 | const peso = Number($peso.value);
102 | const altura = Number($altura.value);
103 | atualize_imc(valor_imc(peso, altura));
104 | }
105 |
106 | function atualize_imc(imc) {
107 | $imc.innerText = "imc = " + imc;
108 | }
109 |
110 | function valor_imc(peso, altura) {
111 | return peso / altura ** 2;
112 | }
113 |
114 | $peso.onkeyup = calcule_imc;
115 | $altura.onkeyup = calcule_imc;
116 | ```
117 |
118 | Nesta versão, introduzimos a função `atualize_imc()` cujo papel é
119 | alterar a _view_. A função `calcule_imc()` por sua vez, agora
120 | precisa invocar `atualize_imc()` para alterar a _view_. Isso nos
121 | permite caracterizar esta função como sendo parte da _view_. Na
122 | prática, contudo, não é raro que ainda seja considerada parte do
123 | código do _controller_ e que a _view_ seja considerada
124 | exclusivamente o DOM. De qualquer forma, a separação em
125 | diferentes funções para isolar cada tipo de comportamento é
126 | adequado.
127 |
128 | Há, contudo, um problema que ainda não tratamos que é central em
129 | MVC: dados. Observe que em todas as versões acima, os dados
130 | centrais de que trata o app são mantidos pela _view_. Confira.
131 | Não há nenhuma variável em nosso código que mantenha o tempo todo
132 | o valor do peso e da altura. Quando as funções executam esses
133 | dados são trazidos do DOM para serem usados. Claramente, isso
134 | também viola os princípios básicos de MVC.
135 |
136 | ## Revisão 4
137 |
138 | Nesta quarta versão damos mais um passo nessa separação dos componentes
139 | do MVC. Nela, introduzimos um objeto JavaScript que faz o papel
140 | explícito de modelo em nossa aplicação. Com isso, podemos reunir
141 | no _model_ os dados centrais do app e a função que calcula o imc
142 | (por simplicidade, renomeei `valor_imc` para `imc`).
143 |
144 | ```javascript
145 | const $altura = document.querySelector("#altura");
146 | const $peso = document.querySelector("#peso");
147 | const $imc = document.querySelector("#imc");
148 |
149 | const model = {
150 | peso: null,
151 | altura: null,
152 | imc: function imc() {
153 | return this.peso / this.altura ** 2;
154 | }
155 | };
156 |
157 | function calcule_imc() {
158 | model.peso = Number($peso.value);
159 | model.altura = Number($altura.value);
160 | atualize_imc(model.imc());
161 | };
162 |
163 | function atualize_imc(imc) {
164 | $imc.innerText = "imc = " + imc.toFixed(2);
165 | }
166 |
167 | $peso.onkeyup = calcule_imc;
168 | $altura.onkeyup = calcule_imc;
169 | ```
170 |
171 | Nesta versão temos um _model_ explícito, implementado como um
172 | objeto JavaScript. Nele, estão reunidos os dados centrais da
173 | aplicação (peso, altura) e a função que calcula o imc. Perceba
174 | que esse modelo não guarda qualquer dependência do restante do
175 | sistema, seja da _view_ (html, dom), seja do controller (o
176 | restante do código). De fato, nada impede que esse mesmo modelo,
177 | se for apropriado, seja reutilizado em outras aplicações.
178 |
179 | ### Revisão 5
180 |
181 | Esta é a quinta revisão de nosso código. Nela, separamos ainda
182 | mais o código do _model_ do restante da aplicação (_controller_ e
183 | _view_). Para isso, precisamos fazer uma pequena modificação no
184 | código do html para que dois arquivos JavaScript sejam lidos pelo
185 | _browser_ para compor a aplicação. Segue o trecho de html em que
186 | fazemos a leitura dos arquivos.
187 |
188 | ```html
189 |
190 |
191 | ...
192 |
193 |
194 |
195 |
196 | ```
197 |
198 | Neste caso específico, não importa a ordem em que os arquivos
199 | sejam lidos. Em casos maiores, contudo, a ordem pode importar.
200 |
201 | Vejamos agora o código do arquivo `app.js`.
202 |
203 | ```javascript
204 | const $altura = document.querySelector("#altura");
205 | const $peso = document.querySelector("#peso");
206 | const $imc = document.querySelector("#imc");
207 |
208 | function calcule_imc() {
209 | model.peso = Number($peso.value);
210 | model.altura = Number($altura.value);
211 | atualize_imc(model.imc());
212 | };
213 |
214 | function atualize_imc(imc) {
215 | $imc.innerText = "imc = " + imc.toFixed(2);
216 | }
217 |
218 | $peso.onkeyup = calcule_imc;
219 | $altura.onkeyup = calcule_imc;
220 | ```
221 |
222 | E agora, o código de `imc.js`.
223 |
224 | ```javascript
225 | const model = {
226 | peso: null,
227 | altura: null,
228 | imc: function imc() {
229 | return this.peso / this.altura ** 2;
230 | }
231 | };
232 | ```
233 |
234 | Observe que os dois arquivos são apenas a decomposição do arquivo
235 | original da versão anterior. Simplesmente mudamos a definição de
236 | `model` para o arquivo `imc.js` e mantivemos as demais definições
237 | em `app.js`. Observe ainda que a variável `model` é referenciada
238 | no arquivo `app.js` o que é bastante _peculiar_, pra dizer o
239 | mínimo.
240 |
241 | O problema é que nosso app está tirando proveito de um problema
242 | de JavaScript: que scripts compartilham um único _namespace_
243 | global. Assim, a variável `model` definida em `imc.js` pode ser
244 | lida (e poderia ser alterada) pelo código em `app.js`. Tal forma
245 | de codificação, dependente de variávels globais é um grande _bad
246 | smell_. Felizmente, nas versões modernas de JavaScript, isso é
247 | fácil de resolver.
248 |
249 | ### Revisão 6
250 |
251 | O que faremos é passar a usar _módulos_. Um _módulo JavaScript_ é
252 | simplesmente um arquivo de código que o _browser_ isola
253 | completamente dos demais módulos e do _namespace_ global. Isso
254 | permite escrever código com muito mais segurança. Para isso, a
255 | principal mudança necessária é no html. É necessário mudar a
256 | forma de ler os arquivos, indicando explicitamente para o
257 | _browser_ que se trata de módulos. Abaixo segue o novo código
258 | para fazer isso.
259 |
260 | ```html
261 |
262 |
263 | ...
264 |
265 |
266 |
267 |
268 | ```
269 |
270 | Se só fizermos essa única mudança, nosso código não irá
271 | funcionar. A razão para isso é que a variável `model` em `app.js`
272 | deixa de estar definida quando as funções forem executadas.
273 |
274 | Para fazer o código voltar a funcionar, é necessário que nosso
275 | módulo `imc.js` _exporte_ (disponibilize e torne acessível para
276 | fora do módulo) o objeto `model` contido nele. Da mesma forma,
277 | o módulo `app.js` deve _importar_ (criar uma referência no
278 | _namespace_ para um objeto criado em outro módulo) o objeto
279 | `model`. Para isso, vamos modificar muito pouco cada um dos
280 | arquivos `imc.js` e `app.js`. De fato, cada um dos arquivos
281 | receberá apenas uma linha adicional.
282 |
283 | Primeiro, vejamos como fica o arquivo `imc.js`.
284 |
285 | ```javascript
286 | const model = {
287 | peso: null,
288 | altura: null,
289 | imc: function imc() {
290 | return this.peso / this.altura ** 2;
291 | }
292 | };
293 |
294 | export default model;
295 | ```
296 |
297 | Atente para a última linha. Nela, indicamos que esse módulo
298 | exporta o objeto `model` (atente para a palavra-chave `default`
299 | que é usada para indicar que esse objeto é o _export_ padrão
300 | daquele módulo. Isso permite simplificar a sintaxe usada no
301 | _import_.
302 |
303 | > Observação. O sistema de módulos e de _imports_ e _exports_ de
304 | > JavaScript é bastante poderoso, mas às vezes confuso. Leia
305 | > sobre o assunto nas páginas
306 | > [Import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import)
307 | > e [Export](https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export)
308 | > do wiki do Mozilla Developer.
309 |
310 | Vejamos como fica o código de `app.js` e o respectivo _import_.
311 |
312 | ```javascript
313 | import model from './imc.js';
314 |
315 | const $altura = document.querySelector("#altura");
316 | const $peso = document.querySelector("#peso");
317 | const $imc = document.querySelector("#imc");
318 |
319 | function handle_user_input() {
320 | model.peso = Number($peso.value);
321 | model.altura = Number($altura.value);
322 | atualize_imc(model.imc());
323 | };
324 |
325 | function atualize_imc(imc) {
326 | $imc.innerText = "imc = " + imc.toFixed(2);
327 | }
328 |
329 | $peso.onkeyup = handle_user_input;
330 | $altura.onkeyup = handle_user_input;
331 | ```
332 |
333 | Atente para a primeira linha do código acima. É ela que faz o
334 | _import_ do objeto `model` exportado pelo módulo `imc.js`. No
335 | restante do código, há apenas uma mudança. O nome da função antes
336 | chamada `calcule_imc()` foi alterado para `handle_user_input()`, dado
337 | que dessa forma reflete mais claramente seu verdadeiro propósito
338 | que é tratar eventos de entrada de dados do usuário.
339 |
340 | ### Revisão 7
341 |
342 | Uma última e mínima mudança no nosso código ainda é possível,
343 | graças ao sistema de módulos de JavaScript. Observe que o módulo
344 | `app.js` é adicionado duas vezes: uma primeira vez através da tag
345 | `script` no código html e uma segunda vez, através do `import` no
346 | código de `app.js`. De fato, apenas este último é necessário.
347 | Isso é possível porque o sistema de módulos de JavaScript permite
348 | que um módulo referencie outro e isso implique em sua carga a
349 | partir do servidor (ou de qualquer origem que tenha). Assim, o
350 | `index.html` fica menos poluído e o comportamente é exatamente o
351 | que se espera. Eis o novo código do final do `body` do arquivo
352 | `index.html`.
353 |
354 | ```html
355 | ...
356 |
357 |
358 |