├── tst ├── threads │ ├── threads.md │ ├── tst.yaml │ ├── ex.py │ ├── mthreads.py │ ├── mthreads2.py │ └── README.md └── sockets │ ├── sockets.md │ ├── .tst.yaml.un~ │ ├── .sockets.md.un~ │ ├── hello │ ├── tst.yaml │ ├── hello2.py │ ├── README.md │ └── hello.py │ ├── tst.yaml~ │ ├── tst.yaml │ └── echo │ ├── echo.py │ └── README.md ├── 05.javascript └── programacao_assincrona │ ├── outro.json │ ├── maisum.json │ ├── dados.json │ ├── ex.js │ ├── index.html │ ├── criando.js │ ├── microtarefas.js │ ├── asaw.js │ ├── f.js │ ├── fa.js │ ├── pro.txt │ ├── README.md │ └── criando-promise.js ├── notas_de_aula.pdf ├── 01.como_funciona_a_web ├── servidor │ ├── req.txt │ ├── cliente2.py │ ├── cliente.py │ ├── mthreads.py │ ├── hello2.py │ ├── servidor1.py │ ├── hello.py │ ├── echo.py │ ├── mthreads2.py │ ├── servidor2.py │ └── servidor3.py ├── README.md ├── estilo.css ├── index.html ├── projsw.css ├── servidor_http.md ├── servidor_http.html ├── fundamentos.md └── fundamentos.html ├── 06.web_apps ├── 3-frontend_mural │ ├── shadow_dom.png │ ├── mural-v2 │ │ ├── messages.js │ │ ├── mensagens.json │ │ ├── index.html │ │ └── app.js │ ├── mural-v3 │ │ ├── messages.js │ │ ├── estilo.css │ │ ├── mensagens.json │ │ ├── index.html │ │ └── app.js │ ├── mural-v4 │ │ ├── messages.js │ │ ├── estilo.css │ │ ├── mensagens.json │ │ ├── index.html │ │ └── app.js │ ├── mural-v5 │ │ ├── messages.js │ │ ├── estilo.css │ │ ├── mensagens.json │ │ ├── index.html │ │ └── app.js │ ├── mural-v6 │ │ ├── messages.js │ │ ├── estilo.css │ │ ├── mensagens.json │ │ ├── index.html │ │ └── app.js │ ├── mural-v7 │ │ ├── messages.js │ │ ├── estilo.css │ │ ├── mensagens.json │ │ ├── index.html │ │ └── app.js │ ├── mural-v8 │ │ ├── messages.js │ │ ├── estilo.css │ │ ├── mensagens.json │ │ ├── index.html │ │ ├── message.css │ │ └── app.js │ ├── mural-v9 │ │ ├── messages.js │ │ ├── estilo.css │ │ ├── mensagens.json │ │ ├── index.html │ │ ├── message.css │ │ ├── app.js │ │ └── mural.js │ ├── mural-v1 │ │ ├── mensagens.json │ │ ├── index.html │ │ └── app.js │ └── text.md ├── 2-imc_padrao_mvc │ ├── imc_v7 │ │ ├── .app.js.swp │ │ ├── imc.js │ │ ├── index.html │ │ ├── app.js │ │ └── estilo.css │ ├── imc_v5 │ │ ├── imc.js │ │ ├── app.js │ │ ├── index.html │ │ └── estilo.css │ ├── imc_v6 │ │ ├── imc.js │ │ ├── app.js │ │ ├── index.html │ │ └── estilo.css │ ├── imc_v1 │ │ ├── imc.js │ │ ├── index.html │ │ └── estilo.css │ ├── imc_v2 │ │ ├── index.html │ │ ├── imc.js │ │ └── estilo.css │ ├── imc_v3 │ │ ├── index.html │ │ ├── imc.js │ │ └── estilo.css │ ├── imc_v4 │ │ ├── index.html │ │ ├── imc.js │ │ └── estilo.css │ └── text.md ├── text.md └── 1-padrao_mvc │ └── text.md ├── README.md └── 03.html ├── exercicios_1 ├── estilo2.css └── index.html └── exercicios_2 ├── estilo.css └── index.html /tst/threads/threads.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tst/sockets/sockets.md: -------------------------------------------------------------------------------- 1 | Roteiro sobre sockets 2 | -------------------------------------------------------------------------------- /05.javascript/programacao_assincrona/outro.json: -------------------------------------------------------------------------------- 1 | [100000] 2 | -------------------------------------------------------------------------------- /05.javascript/programacao_assincrona/maisum.json: -------------------------------------------------------------------------------- 1 | { 2 | "seila": "projsw" 3 | } 4 | -------------------------------------------------------------------------------- /notas_de_aula.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daltonserey/projsw-20191/HEAD/notas_de_aula.pdf -------------------------------------------------------------------------------- /tst/sockets/.tst.yaml.un~: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daltonserey/projsw-20191/HEAD/tst/sockets/.tst.yaml.un~ -------------------------------------------------------------------------------- /tst/sockets/.sockets.md.un~: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daltonserey/projsw-20191/HEAD/tst/sockets/.sockets.md.un~ -------------------------------------------------------------------------------- /01.como_funciona_a_web/servidor/req.txt: -------------------------------------------------------------------------------- 1 | GET /~dalton/projsw-20181/hello/backend/servidor.js HTTP/1.1 2 | Host: www.dsc.ufg.edu.br 3 | 4 | 5 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/shadow_dom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daltonserey/projsw-20191/HEAD/06.web_apps/3-frontend_mural/shadow_dom.png -------------------------------------------------------------------------------- /tst/sockets/hello/tst.yaml: -------------------------------------------------------------------------------- 1 | name: hello 2 | label: Hello World! 3 | text: hello.md 4 | include: 5 | - hello.py, public, rw 6 | - hello2.py, public, rw 7 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v7/.app.js.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daltonserey/projsw-20191/HEAD/06.web_apps/2-imc_padrao_mvc/imc_v7/.app.js.swp -------------------------------------------------------------------------------- /05.javascript/programacao_assincrona/dados.json: -------------------------------------------------------------------------------- 1 | { 2 | "exemplo": "testando", 3 | "exemplo2": "testando", 4 | "sites": [ 5 | "maisum.json", 6 | "outro.json" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /05.javascript/programacao_assincrona/ex.js: -------------------------------------------------------------------------------- 1 | function minhafuncao(msg) { 2 | console.log(msg); 3 | } 4 | 5 | setTimeout(function () { minhafuncao("oiiii!"); }, 4000); 6 | console.log("terminei"); 7 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v5/imc.js: -------------------------------------------------------------------------------- 1 | const model = { 2 | peso: null, 3 | altura: null, 4 | imc: function imc() { 5 | return this.peso / this.altura ** 2; 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /tst/threads/tst.yaml: -------------------------------------------------------------------------------- 1 | name: threads 2 | label: Introdução a Threads com Python 3 | text: threads.md 4 | include: 5 | - README.md, public, rw 6 | - mthreads.py, public, rw 7 | - mthreads2.py, public, rw 8 | -------------------------------------------------------------------------------- /tst/sockets/tst.yaml~: -------------------------------------------------------------------------------- 1 | name: sockets 2 | label: Sockets 3 | include: 4 | - hello/README.md, public, rw 5 | - hello/hello.py, public, rw 6 | - hello/hello2.py, public, rw 7 | - echo/README.md, public, rw 8 | - echo/echo.py, public, rw 9 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v6/imc.js: -------------------------------------------------------------------------------- 1 | const model = { 2 | peso: null, 3 | altura: null, 4 | imc: function imc() { 5 | return this.peso / this.altura ** 2; 6 | } 7 | }; 8 | 9 | export default model; 10 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v7/imc.js: -------------------------------------------------------------------------------- 1 | const model = { 2 | peso: null, 3 | altura: null, 4 | imc: function imc() { 5 | return this.peso / this.altura ** 2; 6 | } 7 | }; 8 | 9 | export default model; 10 | -------------------------------------------------------------------------------- /05.javascript/programacao_assincrona/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tst/sockets/tst.yaml: -------------------------------------------------------------------------------- 1 | name: sockets 2 | label: Sockets 3 | text: sockets.md 4 | include: 5 | - hello/README.md, public, rw 6 | - hello/hello.py, public, rw 7 | - hello/hello2.py, public, rw 8 | - echo/README.md, public, rw 9 | - echo/echo.py, public, rw 10 | -------------------------------------------------------------------------------- /01.como_funciona_a_web/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "projsw: Como funciona a web?" 3 | --- 4 | - aula prática 5 | - local: lcc3 6 | - tema: Como funciona a web? 7 | - Roteiro 1: [Fundamentos](fundamentos.html) 8 | - Roteiro 2: [Um mini servidor http](servidor_http.html) 9 | -------------------------------------------------------------------------------- /05.javascript/programacao_assincrona/criando.js: -------------------------------------------------------------------------------- 1 | function seila(callback) { 2 | setTimeout(function () { 3 | callback("oi"); 4 | }, 1000) 5 | } 6 | 7 | function recebedor(dado) { 8 | console.log("callback chamado: " + dado); 9 | } 10 | 11 | seila(recebedor); 12 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v2/messages.js: -------------------------------------------------------------------------------- 1 | const messages = []; 2 | 3 | async function get_messages() { 4 | let response = await fetch('mensagens.json'); 5 | let data = await response.json(); 6 | messages.push(...data); 7 | } 8 | 9 | export {get_messages, messages}; 10 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v3/messages.js: -------------------------------------------------------------------------------- 1 | const messages = []; 2 | 3 | async function get_messages() { 4 | let response = await fetch('mensagens.json'); 5 | let data = await response.json(); 6 | messages.push(...data); 7 | } 8 | 9 | export {get_messages, messages}; 10 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v4/messages.js: -------------------------------------------------------------------------------- 1 | const messages = []; 2 | 3 | async function get_messages() { 4 | let response = await fetch('mensagens.json'); 5 | let data = await response.json(); 6 | messages.push(...data); 7 | } 8 | 9 | export {get_messages, messages}; 10 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v5/messages.js: -------------------------------------------------------------------------------- 1 | const messages = []; 2 | 3 | async function get_messages() { 4 | let response = await fetch('mensagens.json'); 5 | let data = await response.json(); 6 | messages.push(...data); 7 | } 8 | 9 | export {get_messages, messages}; 10 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v6/messages.js: -------------------------------------------------------------------------------- 1 | const messages = []; 2 | 3 | async function get_messages() { 4 | let response = await fetch('mensagens.json'); 5 | let data = await response.json(); 6 | messages.push(...data); 7 | } 8 | 9 | export {get_messages, messages}; 10 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v7/messages.js: -------------------------------------------------------------------------------- 1 | const messages = []; 2 | 3 | async function get_messages() { 4 | let response = await fetch('mensagens.json'); 5 | let data = await response.json(); 6 | messages.push(...data); 7 | } 8 | 9 | export {get_messages, messages}; 10 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v8/messages.js: -------------------------------------------------------------------------------- 1 | const messages = []; 2 | 3 | async function get_messages() { 4 | let response = await fetch('mensagens.json'); 5 | let data = await response.json(); 6 | messages.push(...data); 7 | } 8 | 9 | export {get_messages, messages}; 10 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v9/messages.js: -------------------------------------------------------------------------------- 1 | const messages = []; 2 | 3 | async function get_messages() { 4 | let response = await fetch('mensagens.json'); 5 | let data = await response.json(); 6 | messages.push(...data); 7 | } 8 | 9 | export {get_messages, messages}; 10 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v3/estilo.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | background: gray; 5 | } 6 | 7 | body { 8 | margin: 20px; 9 | background: white; 10 | padding: 20px; 11 | } 12 | 13 | p { 14 | color: #77c; 15 | font-family: arial; 16 | } 17 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v5/estilo.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | background: gray; 5 | } 6 | 7 | body { 8 | margin: 20px; 9 | background: white; 10 | padding: 20px; 11 | } 12 | 13 | p { 14 | color: #77c; 15 | font-family: arial; 16 | } 17 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v6/estilo.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | background: gray; 5 | } 6 | 7 | body { 8 | margin: 20px; 9 | background: white; 10 | padding: 20px; 11 | } 12 | 13 | p { 14 | color: #77c; 15 | font-family: arial; 16 | } 17 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v7/estilo.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | background: gray; 5 | } 6 | 7 | body { 8 | margin: 20px; 9 | background: white; 10 | padding: 20px; 11 | } 12 | 13 | p { 14 | color: #77c; 15 | font-family: arial; 16 | } 17 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v8/estilo.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | background: gray; 5 | } 6 | 7 | body { 8 | margin: 20px; 9 | background: white; 10 | padding: 20px; 11 | } 12 | 13 | p { 14 | color: #77c; 15 | font-family: arial; 16 | } 17 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v9/estilo.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | background: gray; 5 | } 6 | 7 | body { 8 | margin: 20px; 9 | background: white; 10 | padding: 20px; 11 | } 12 | 13 | p { 14 | color: #77c; 15 | font-family: arial; 16 | } 17 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v4/estilo.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | background: gray; 5 | } 6 | 7 | body { 8 | margin: 20px; 9 | background: white; 10 | padding: 20px; 11 | } 12 | 13 | main > p { 14 | color: #77c; 15 | font-family: arial; 16 | } 17 | -------------------------------------------------------------------------------- /05.javascript/programacao_assincrona/microtarefas.js: -------------------------------------------------------------------------------- 1 | setTimeout(() => console.log("setTimeout"), 0); 2 | 3 | Promise.resolve(1) 4 | .then(function (d) { 5 | console.log(d); 6 | return 10101010; 7 | }) 8 | .then(d => { 9 | console.log("dado: " + d); 10 | }) 11 | 12 | console.log("log direto do main"); 13 | -------------------------------------------------------------------------------- /06.web_apps/text.md: -------------------------------------------------------------------------------- 1 | # Web Apps 2 | 3 | Nesta parte do curso, vamos ver como usar o que aprendemos sobre 4 | HTML, CSS, JavaScript e das APIs do _browser_ para escrever 5 | aplicações web modernas. 6 | 7 | ## Conteúdo 8 | 9 | 1. [O Padrão MVC](1-padrao_mvc/text.md) 10 | 2. [Ajustando o mini app IMC a MVC](2-imc_padrao_mvc/text.md) 11 | -------------------------------------------------------------------------------- /tst/threads/ex.py: -------------------------------------------------------------------------------- 1 | import time 2 | import sys 3 | from threading import Thread 4 | 5 | def conta(): 6 | num = 0 7 | while num < 5: 8 | time.sleep(1) 9 | num += 1 10 | print "Sinto muito, tempo esgotado." 11 | sys.exit() 12 | 13 | Thread(target=conta).start() 14 | nome = raw_input("Qual seu nome?") 15 | print("Oi, %s" % nome) 16 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v1/mensagens.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "message": "Oi eu sou PROJSW!", 4 | "author": "projsw@gmail.com", 5 | "at": "15:50" 6 | }, 7 | { 8 | "message": "Oi eu sou Dalton.", 9 | "author": "daltonserey@gmail.com", 10 | "at": "15:10" 11 | }, 12 | { 13 | "message": "Oi eu sou Fulano.", 14 | "author": "fulano@gmail.com", 15 | "at": "15:15" 16 | } 17 | ] 18 | 19 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v2/mensagens.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "message": "Oi eu sou PROJSW!", 4 | "author": "projsw@gmail.com", 5 | "at": "15:50" 6 | }, 7 | { 8 | "message": "Oi eu sou Dalton.", 9 | "author": "daltonserey@gmail.com", 10 | "at": "15:10" 11 | }, 12 | { 13 | "message": "Oi eu sou Fulano.", 14 | "author": "fulano@gmail.com", 15 | "at": "15:15" 16 | } 17 | ] 18 | 19 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v3/mensagens.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "message": "Oi eu sou PROJSW!", 4 | "author": "projsw@gmail.com", 5 | "at": "15:50" 6 | }, 7 | { 8 | "message": "Oi eu sou Dalton.", 9 | "author": "daltonserey@gmail.com", 10 | "at": "15:10" 11 | }, 12 | { 13 | "message": "Oi eu sou Fulano.", 14 | "author": "fulano@gmail.com", 15 | "at": "15:15" 16 | } 17 | ] 18 | 19 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v4/mensagens.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "message": "Oi eu sou PROJSW!", 4 | "author": "projsw@gmail.com", 5 | "at": "15:50" 6 | }, 7 | { 8 | "message": "Oi eu sou Dalton.", 9 | "author": "daltonserey@gmail.com", 10 | "at": "15:10" 11 | }, 12 | { 13 | "message": "Oi eu sou Fulano.", 14 | "author": "fulano@gmail.com", 15 | "at": "15:15" 16 | } 17 | ] 18 | 19 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v5/mensagens.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "message": "Oi eu sou PROJSW!", 4 | "author": "projsw@gmail.com", 5 | "at": "15:50" 6 | }, 7 | { 8 | "message": "Oi eu sou Dalton.", 9 | "author": "daltonserey@gmail.com", 10 | "at": "15:10" 11 | }, 12 | { 13 | "message": "Oi eu sou Fulano.", 14 | "author": "fulano@gmail.com", 15 | "at": "15:15" 16 | } 17 | ] 18 | 19 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v6/mensagens.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "message": "Oi eu sou PROJSW!", 4 | "author": "projsw@gmail.com", 5 | "at": "15:50" 6 | }, 7 | { 8 | "message": "Oi eu sou Dalton.", 9 | "author": "daltonserey@gmail.com", 10 | "at": "15:10" 11 | }, 12 | { 13 | "message": "Oi eu sou Fulano.", 14 | "author": "fulano@gmail.com", 15 | "at": "15:15" 16 | } 17 | ] 18 | 19 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v7/mensagens.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "message": "Oi eu sou PROJSW!", 4 | "author": "projsw@gmail.com", 5 | "at": "15:50" 6 | }, 7 | { 8 | "message": "Oi eu sou Dalton.", 9 | "author": "daltonserey@gmail.com", 10 | "at": "15:10" 11 | }, 12 | { 13 | "message": "Oi eu sou Fulano.", 14 | "author": "fulano@gmail.com", 15 | "at": "15:15" 16 | } 17 | ] 18 | 19 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v8/mensagens.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "message": "Oi eu sou PROJSW!", 4 | "author": "projsw@gmail.com", 5 | "at": "15:50" 6 | }, 7 | { 8 | "message": "Oi eu sou Dalton.", 9 | "author": "daltonserey@gmail.com", 10 | "at": "15:10" 11 | }, 12 | { 13 | "message": "Oi eu sou Fulano.", 14 | "author": "fulano@gmail.com", 15 | "at": "15:15" 16 | } 17 | ] 18 | 19 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v9/mensagens.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "message": "Oi eu sou PROJSW!", 4 | "author": "projsw@gmail.com", 5 | "at": "15:50" 6 | }, 7 | { 8 | "message": "Oi eu sou Dalton.", 9 | "author": "daltonserey@gmail.com", 10 | "at": "15:10" 11 | }, 12 | { 13 | "message": "Oi eu sou Fulano.", 14 | "author": "fulano@gmail.com", 15 | "at": "15:15" 16 | } 17 | ] 18 | 19 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Sua mensagem:

11 |

Autor: 12 | 13 |

14 |
15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Sua mensagem:

11 |

Autor: 12 | 13 |

14 |
15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /05.javascript/programacao_assincrona/asaw.js: -------------------------------------------------------------------------------- 1 | async function teste() { 2 | return new Promise((res, rej) => { 3 | setTimeout(() => res("oi"), 1000); 4 | }); 5 | } 6 | 7 | function usuario() { 8 | console.log("iniciando"); 9 | let x = await teste(); 10 | console.log(x.toUpperCase()); 11 | console.log("terminando"); 12 | } 13 | 14 | console.log("INICIO PROGRAMA"); 15 | usuario(); 16 | console.log("FIM PROGRAMA"); 17 | 18 | -------------------------------------------------------------------------------- /05.javascript/programacao_assincrona/f.js: -------------------------------------------------------------------------------- 1 | let res; 2 | let url; 3 | console.log("iniciando"); 4 | let p1 = fetch('dados.json'); 5 | let p2 = p1.then(r => r.json()); 6 | let p3 = p2.then(dados => fetch(dados.sites[0])); 7 | let p4 = p3.then(r => r.json()); 8 | let p5 = p4.then(numeros => { console.log(numeros); }) 9 | p4.catch(function (err) { 10 | console.log("ops!"); 11 | console.log(err); 12 | }) 13 | console.log("finalizada a execução"); 14 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v1/imc.js: -------------------------------------------------------------------------------- 1 | const $altura = document.querySelector("#altura"); 2 | const $peso = document.querySelector("#peso"); 3 | const $imc = document.querySelector("#imc"); 4 | 5 | function calcule_imc() { 6 | const peso = Number($peso.value); 7 | const altura = Number($altura.value); 8 | $imc.innerText = "imc = " + (peso / altura ** 2).toFixed(2); 9 | } 10 | 11 | $peso.onkeyup = calcule_imc; 12 | $altura.onkeyup = calcule_imc; 13 | -------------------------------------------------------------------------------- /01.como_funciona_a_web/servidor/cliente2.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import socket 3 | 4 | server_address = ('127.0.0.1', 9090) 5 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 6 | server.connect(server_address) 7 | 8 | while True: 9 | request = sys.stdin.read() 10 | server.send(request) 11 | 12 | response = '' 13 | while True: 14 | recv = server.recv(1024) 15 | if not recv: break 16 | response += recv 17 | 18 | print response 19 | server.close() 20 | -------------------------------------------------------------------------------- /05.javascript/programacao_assincrona/fa.js: -------------------------------------------------------------------------------- 1 | async function get() { 2 | try { 3 | let response = await fetch('dados.json'); 4 | let dados = await response.json(); 5 | let url = dados.sites[0]; 6 | let response2 = await fetch(url); 7 | let resultado = await response2.json(); 8 | console.log(resultado); 9 | } catch (err) { 10 | console.log("ops!"); 11 | console.log(err); 12 | } 13 | } 14 | 15 | get(); 16 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Sua mensagem:

12 |

Autor: 13 | 14 |

15 |
16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Sua mensagem:

12 |

Autor: 13 | 14 |

15 |
16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Sua mensagem:

12 |

Autor: 13 | 14 |

15 |
16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v6/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Sua mensagem:

12 |

Autor: 13 | 14 |

15 |
16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v7/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Sua mensagem:

12 |

Autor: 13 | 14 |

15 |
16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v8/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Sua mensagem:

12 |

Autor: 13 | 14 |

15 |
16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v9/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Sua mensagem:

12 |

Autor: 13 | 14 |

15 |
16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tst/threads/mthreads.py: -------------------------------------------------------------------------------- 1 | from threading import Thread 2 | import time 3 | 4 | def contador(nome, segundos, intervalo): 5 | while segundos: 6 | print("%s: %s" % (nome, segundos)) 7 | time.sleep(intervalo / 1000) 8 | segundos -= 1 9 | 10 | print('Iniciando contador 1 ("5 Segundos")') 11 | Thread(target=contador, args=("5 Segundos", 5, 1000)).start() 12 | print('Iniciando contador 2 ("A")') 13 | Thread(target=contador, args=("A", 15, 300)).start() 14 | print('Fim') 15 | -------------------------------------------------------------------------------- /05.javascript/programacao_assincrona/pro.txt: -------------------------------------------------------------------------------- 1 | # O que é uma promise? 2 | 3 | - um objeto 4 | - que desacopla três partes envolvidas: 5 | 1. o código cliente (quem faz a requisição do serv) 6 | 2. o código provedor (quem tenta atender à requisição) 7 | 3. o código consumidor (quem vai processar o resultado) 8 | 9 | concretamente 10 | - um objeto promise provê: 11 | 1. um método then 12 | 2. um método catch 13 | 3. um método finally 14 | 4. todos os métodos acima retornam outra promise 15 | -------------------------------------------------------------------------------- /tst/sockets/hello/hello2.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 | conexao.send('Hello, World!\n'.encode('utf-8')) 16 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v5/app.js: -------------------------------------------------------------------------------- 1 | const $altura = document.querySelector("#altura"); 2 | const $peso = document.querySelector("#peso"); 3 | const $imc = document.querySelector("#imc"); 4 | 5 | function calcule_imc() { 6 | model.peso = Number($peso.value); 7 | model.altura = Number($altura.value); 8 | atualize_imc(model.imc()); 9 | }; 10 | 11 | function atualize_imc(imc) { 12 | $imc.innerText = "imc = " + imc.toFixed(2); 13 | } 14 | 15 | $peso.onkeyup = calcule_imc; 16 | $altura.onkeyup = calcule_imc; 17 | -------------------------------------------------------------------------------- /01.como_funciona_a_web/servidor/cliente.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import socket 3 | 4 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 5 | server_address = ('www.dsc.ufcg.edu.br', 80) 6 | s.connect(server_address) 7 | 8 | request = sys.stdin.read() 9 | equest = """ 10 | GET /~dalton/index.html HTTP/1.1 11 | Host: www.dsc.ufcg.edu.br 12 | 13 | """ 14 | s.send(request) 15 | 16 | response = '' 17 | while True: 18 | recv = s.recv(1024) 19 | if not recv: break 20 | response += recv 21 | 22 | print response 23 | s.close() 24 | -------------------------------------------------------------------------------- /01.como_funciona_a_web/servidor/mthreads.py: -------------------------------------------------------------------------------- 1 | from threading import Thread 2 | import time 3 | 4 | def contador(nome, segundos, intervalo): 5 | while segundos: 6 | print("%s: %s" % (nome, segundos)) 7 | time.sleep(intervalo / 1000) 8 | segundos -= 1 9 | 10 | print('Iniciando contador 1 ("5 Segundos")') 11 | Thread(target=contador, args=("5 Segundos", 5, 1000)).start() 12 | print('Iniciando contador 2 ("A")') 13 | Thread(target=contador, args=("A", 15, 300)).start() 14 | print('Contadores iniciados...') 15 | -------------------------------------------------------------------------------- /01.como_funciona_a_web/servidor/hello2.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 | conexao.send('Hello, World!\n'.encode('utf-8')) 16 | -------------------------------------------------------------------------------- /05.javascript/programacao_assincrona/README.md: -------------------------------------------------------------------------------- 1 | # programação assíncrona 2 | 3 | ## event loop 4 | 5 | - runtime JS 6 | - engine 7 | - APIs 8 | - seu código: single thread 9 | - código browser: múltiplas threads + processos 10 | 11 | 12 | ## callbacks 13 | 14 | - setTimeout 15 | - setInterval 16 | 17 | 18 | ## promises 19 | 20 | - fetch 21 | - then 22 | - catch 23 | - finally 24 | - Promise 25 | - Promise.resolve 26 | - Promise.reject 27 | 28 | 29 | ## modelo de execução 30 | 31 | - macro tarefas (eventos) 32 | - micro tarefas (promessas) 33 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Calculadora IMC 6 | 7 | 8 | 9 |

imc

10 | 11 |
12 |

altura

13 | 14 | 15 |

peso

16 | 17 | 18 |

19 |

20 |
21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Calculadora IMC 6 | 7 | 8 | 9 |

imc

10 | 11 |
12 |

altura

13 | 14 | 15 |

peso

16 | 17 | 18 |

19 |

20 |
21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Calculadora IMC 6 | 7 | 8 | 9 |

imc

10 | 11 |
12 |

altura

13 | 14 | 15 |

peso

16 | 17 | 18 |

19 |

20 |
21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Calculadora IMC 6 | 7 | 8 | 9 |

imc

10 | 11 |
12 |

altura

13 | 14 | 15 |

peso

16 | 17 | 18 |

19 |

20 |
21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v7/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Calculadora IMC 6 | 7 | 8 | 9 |

imc

10 | 11 |
12 |

altura

13 | 14 | 15 |

peso

16 | 17 | 18 |

19 |

20 |
21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v8/message.css: -------------------------------------------------------------------------------- 1 | :host { 2 | background: #bfb; 3 | padding: 12px; 4 | border-radius: 8px; 5 | margin-bottom: 2px; 6 | display: grid; 7 | grid-template-areas: 8 | "message message" 9 | "at author"; 10 | grid-gap: 10px; 11 | } 12 | p, time { 13 | margin: 0; 14 | color: gray; 15 | } 16 | p.message { 17 | grid-area: message; 18 | color: navy; 19 | } 20 | p.author { 21 | grid-area: author; 22 | justify-self: end; 23 | } 24 | time.at { 25 | grid-area: at; 26 | } 27 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v9/message.css: -------------------------------------------------------------------------------- 1 | :host { 2 | background: #bfb; 3 | padding: 12px; 4 | border-radius: 8px; 5 | margin-bottom: 2px; 6 | display: grid; 7 | grid-template-areas: 8 | "message message" 9 | "at author"; 10 | grid-gap: 10px; 11 | } 12 | p, time { 13 | margin: 0; 14 | color: gray; 15 | } 16 | p.message { 17 | grid-area: message; 18 | color: navy; 19 | } 20 | p.author { 21 | grid-area: author; 22 | justify-self: end; 23 | } 24 | time.at { 25 | grid-area: at; 26 | } 27 | -------------------------------------------------------------------------------- /01.como_funciona_a_web/servidor/servidor1.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import socket 3 | import sys 4 | 5 | porta = int(sys.argv[1] if len(sys.argv) > 1 else 9090) 6 | s = socket.socket() 7 | s.bind(('', porta)) 8 | s.listen() 9 | 10 | print('Esperando conexões na porta %s...' % porta) 11 | conexao, endereco = s.accept() 12 | print('Conexão de %s:%s' % endereco) 13 | 14 | while True: 15 | data = conexao.recv(1024).decode('utf-8') 16 | if data.strip() == 'bye': break 17 | print("> " + data.strip()) 18 | conexao.send(data.encode('utf-8')) 19 | 20 | conexao.close() 21 | -------------------------------------------------------------------------------- /05.javascript/programacao_assincrona/criando-promise.js: -------------------------------------------------------------------------------- 1 | function seila() { 2 | let promise = new Promise(function (resolve, reject) { 3 | setTimeout(() => { 4 | let deucerto = Math.random() < 0.9; 5 | if (deucerto) { 6 | resolve("oi"); 7 | } else { 8 | reject("erro!"); 9 | } 10 | }, 1000); 11 | }); 12 | return promise; 13 | } 14 | 15 | seila() 16 | .then(r => r.toUpperCase()) 17 | .then(d => console.log(d)) 18 | .catch(err => { 19 | console.log("ops! " + err); 20 | }); 21 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v6/app.js: -------------------------------------------------------------------------------- 1 | import model from './imc.js'; 2 | 3 | const $altura = document.querySelector("#altura"); 4 | const $peso = document.querySelector("#peso"); 5 | const $imc = document.querySelector("#imc"); 6 | 7 | function handle_user_input() { 8 | model.peso = Number($peso.value); 9 | model.altura = Number($altura.value); 10 | atualize_imc(model.imc()); 11 | }; 12 | 13 | function atualize_imc(imc) { 14 | $imc.innerText = "imc = " + imc.toFixed(2); 15 | } 16 | 17 | $peso.onkeyup = handle_user_input; 18 | $altura.onkeyup = handle_user_input; 19 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v7/app.js: -------------------------------------------------------------------------------- 1 | import model from './imc.js'; 2 | 3 | const $altura = document.querySelector("#altura"); 4 | const $peso = document.querySelector("#peso"); 5 | const $imc = document.querySelector("#imc"); 6 | 7 | function handle_user_input() { 8 | model.peso = Number($peso.value); 9 | model.altura = Number($altura.value); 10 | atualize_imc(model.imc()); 11 | }; 12 | 13 | function atualize_imc(imc) { 14 | $imc.innerText = "imc = " + imc.toFixed(2); 15 | } 16 | 17 | $peso.onkeyup = handle_user_input; 18 | $altura.onkeyup = handle_user_input; 19 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Calculadora IMC 6 | 7 | 8 | 9 |

imc

10 | 11 |
12 |

altura

13 | 14 | 15 |

peso

16 | 17 | 18 |

19 |

20 |
21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v2/imc.js: -------------------------------------------------------------------------------- 1 | const $altura = document.querySelector("#altura"); 2 | const $peso = document.querySelector("#peso"); 3 | const $imc = document.querySelector("#imc"); 4 | 5 | function calcule_imc() { 6 | const peso = Number($peso.value); 7 | const altura = Number($altura.value); 8 | const imc = valor_imc(peso, altura); 9 | $imc.innerText = "imc = " + valor_imc(peso, altura).toFixed(2); 10 | } 11 | 12 | function valor_imc(peso, altura) { 13 | return peso / altura ** 2; 14 | } 15 | 16 | $peso.onkeyup = calcule_imc; 17 | $altura.onkeyup = calcule_imc; 18 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v6/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Calculadora IMC 6 | 7 | 8 | 9 |

imc

10 | 11 |
12 |

altura

13 | 14 | 15 |

peso

16 | 17 | 18 |

19 |

20 |
21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v3/imc.js: -------------------------------------------------------------------------------- 1 | const $altura = document.querySelector("#altura"); 2 | const $peso = document.querySelector("#peso"); 3 | const $imc = document.querySelector("#imc"); 4 | 5 | function calcule_imc() { 6 | const peso = Number($peso.value); 7 | const altura = Number($altura.value); 8 | atualize_imc(valor_imc(peso, altura)); 9 | } 10 | 11 | function atualize_imc(imc) { 12 | $imc.innerText = "imc = " + imc.toFixed(2); 13 | } 14 | 15 | function valor_imc(peso, altura) { 16 | return peso / altura ** 2; 17 | } 18 | 19 | $peso.onkeyup = calcule_imc; 20 | $altura.onkeyup = calcule_imc; 21 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v2/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 = `

${message.message}

8 |

${message.author}

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 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v3/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 = `

${message.message}

8 |

${message.author}

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 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v4/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 = `

${message.message}

8 |

${message.author}

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 = `

${message.message}

8 |

${message.author}

9 | `; 10 | 11 | let novo = document.createElement("div"); 12 | novo.innerHTML = html; 13 | $msgs.appendChild(novo); 14 | }); 15 | } 16 | 17 | async function get_messages() { 18 | let response = await fetch('mensagens.json'); 19 | let data = await response.json(); 20 | window.messages = data; 21 | } 22 | 23 | get_messages() 24 | .then(function () { 25 | render(); 26 | }); 27 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v8/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 = ` 8 |

${message.message}

9 |

${message.author}

10 | `; 11 | 12 | let novo = document.createElement("div"); 13 | let shadow = novo.attachShadow({"mode": "open"}); 14 | shadow.innerHTML = html; 15 | $msgs.appendChild(novo); 16 | }); 17 | } 18 | 19 | get_messages() 20 | .then(function () { 21 | render(); 22 | }); 23 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v6/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 | let shadow = novo.attachShadow({"mode": "open"}); 16 | shadow.innerHTML = html; 17 | $msgs.appendChild(novo); 18 | }); 19 | } 20 | 21 | get_messages() 22 | .then(function () { 23 | render(); 24 | }); 25 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v9/mural.js: -------------------------------------------------------------------------------- 1 | class MuralMessage extends HTMLElement{ 2 | constructor() { 3 | super(); 4 | this.$shadow = this.attachShadow({"mode": "open"}); 5 | } 6 | 7 | connectedCallback() { 8 | this.message = this.getAttribute('message'); 9 | this.author = this.getAttribute('author'); 10 | this.at = this.getAttribute('at'); 11 | this.render(); 12 | } 13 | 14 | render() { 15 | this.$shadow.innerHTML = 16 | ` 17 |

${this.message}

18 |

${this.author}

19 | `; 20 | 21 | } 22 | } 23 | 24 | window.customElements.define('ps-message', MuralMessage) 25 | -------------------------------------------------------------------------------- /01.como_funciona_a_web/estilo.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html { 7 | background: gray; 8 | } 9 | 10 | body { 11 | background: white; 12 | margin: 20px; 13 | padding: 80px; 14 | font-family: sans-serif; 15 | font-size: 24px; 16 | } 17 | 18 | main { 19 | margin: 20px 0 20px 0; 20 | display: grid; 21 | grid-template-columns: min-content 1fr 1fr; 22 | grid-template-areas: 23 | "text1 input1 result" 24 | "text2 input2 msg"; 25 | grid-column-gap: 1em; 26 | grid-row-gap: 10px; 27 | } 28 | 29 | main input { 30 | background: #eef; 31 | font-size: 24px; 32 | } 33 | 34 | #imc { 35 | grid-area: result; 36 | justify-self: center; 37 | } 38 | 39 | #classificacao { 40 | grid-area: msg; 41 | justify-self: center; 42 | color: gray; 43 | font-size: 80%; 44 | } 45 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v1/estilo.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html { 7 | background: gray; 8 | } 9 | 10 | body { 11 | background: white; 12 | margin: 20px; 13 | padding: 80px; 14 | font-family: sans-serif; 15 | font-size: 24px; 16 | } 17 | 18 | main { 19 | margin: 20px 0 20px 0; 20 | display: grid; 21 | grid-template-columns: min-content 1fr 1fr; 22 | grid-template-areas: 23 | "text1 input1 result" 24 | "text2 input2 msg"; 25 | grid-column-gap: 1em; 26 | grid-row-gap: 10px; 27 | } 28 | 29 | main input { 30 | background: #eef; 31 | font-size: 24px; 32 | } 33 | 34 | #imc { 35 | grid-area: result; 36 | justify-self: center; 37 | } 38 | 39 | #classificacao { 40 | grid-area: msg; 41 | justify-self: center; 42 | color: gray; 43 | font-size: 80%; 44 | } 45 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v2/estilo.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html { 7 | background: gray; 8 | } 9 | 10 | body { 11 | background: white; 12 | margin: 20px; 13 | padding: 80px; 14 | font-family: sans-serif; 15 | font-size: 24px; 16 | } 17 | 18 | main { 19 | margin: 20px 0 20px 0; 20 | display: grid; 21 | grid-template-columns: min-content 1fr 1fr; 22 | grid-template-areas: 23 | "text1 input1 result" 24 | "text2 input2 msg"; 25 | grid-column-gap: 1em; 26 | grid-row-gap: 10px; 27 | } 28 | 29 | main input { 30 | background: #eef; 31 | font-size: 24px; 32 | } 33 | 34 | #imc { 35 | grid-area: result; 36 | justify-self: center; 37 | } 38 | 39 | #classificacao { 40 | grid-area: msg; 41 | justify-self: center; 42 | color: gray; 43 | font-size: 80%; 44 | } 45 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v3/estilo.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html { 7 | background: gray; 8 | } 9 | 10 | body { 11 | background: white; 12 | margin: 20px; 13 | padding: 80px; 14 | font-family: sans-serif; 15 | font-size: 24px; 16 | } 17 | 18 | main { 19 | margin: 20px 0 20px 0; 20 | display: grid; 21 | grid-template-columns: min-content 1fr 1fr; 22 | grid-template-areas: 23 | "text1 input1 result" 24 | "text2 input2 msg"; 25 | grid-column-gap: 1em; 26 | grid-row-gap: 10px; 27 | } 28 | 29 | main input { 30 | background: #eef; 31 | font-size: 24px; 32 | } 33 | 34 | #imc { 35 | grid-area: result; 36 | justify-self: center; 37 | } 38 | 39 | #classificacao { 40 | grid-area: msg; 41 | justify-self: center; 42 | color: gray; 43 | font-size: 80%; 44 | } 45 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v4/estilo.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html { 7 | background: gray; 8 | } 9 | 10 | body { 11 | background: white; 12 | margin: 20px; 13 | padding: 80px; 14 | font-family: sans-serif; 15 | font-size: 24px; 16 | } 17 | 18 | main { 19 | margin: 20px 0 20px 0; 20 | display: grid; 21 | grid-template-columns: min-content 1fr 1fr; 22 | grid-template-areas: 23 | "text1 input1 result" 24 | "text2 input2 msg"; 25 | grid-column-gap: 1em; 26 | grid-row-gap: 10px; 27 | } 28 | 29 | main input { 30 | background: #eef; 31 | font-size: 24px; 32 | } 33 | 34 | #imc { 35 | grid-area: result; 36 | justify-self: center; 37 | } 38 | 39 | #classificacao { 40 | grid-area: msg; 41 | justify-self: center; 42 | color: gray; 43 | font-size: 80%; 44 | } 45 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v5/estilo.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html { 7 | background: gray; 8 | } 9 | 10 | body { 11 | background: white; 12 | margin: 20px; 13 | padding: 80px; 14 | font-family: sans-serif; 15 | font-size: 24px; 16 | } 17 | 18 | main { 19 | margin: 20px 0 20px 0; 20 | display: grid; 21 | grid-template-columns: min-content 1fr 1fr; 22 | grid-template-areas: 23 | "text1 input1 result" 24 | "text2 input2 msg"; 25 | grid-column-gap: 1em; 26 | grid-row-gap: 10px; 27 | } 28 | 29 | main input { 30 | background: #eef; 31 | font-size: 24px; 32 | } 33 | 34 | #imc { 35 | grid-area: result; 36 | justify-self: center; 37 | } 38 | 39 | #classificacao { 40 | grid-area: msg; 41 | justify-self: center; 42 | color: gray; 43 | font-size: 80%; 44 | } 45 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v6/estilo.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html { 7 | background: gray; 8 | } 9 | 10 | body { 11 | background: white; 12 | margin: 20px; 13 | padding: 80px; 14 | font-family: sans-serif; 15 | font-size: 24px; 16 | } 17 | 18 | main { 19 | margin: 20px 0 20px 0; 20 | display: grid; 21 | grid-template-columns: min-content 1fr 1fr; 22 | grid-template-areas: 23 | "text1 input1 result" 24 | "text2 input2 msg"; 25 | grid-column-gap: 1em; 26 | grid-row-gap: 10px; 27 | } 28 | 29 | main input { 30 | background: #eef; 31 | font-size: 24px; 32 | } 33 | 34 | #imc { 35 | grid-area: result; 36 | justify-self: center; 37 | } 38 | 39 | #classificacao { 40 | grid-area: msg; 41 | justify-self: center; 42 | color: gray; 43 | font-size: 80%; 44 | } 45 | -------------------------------------------------------------------------------- /06.web_apps/2-imc_padrao_mvc/imc_v7/estilo.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html { 7 | background: gray; 8 | } 9 | 10 | body { 11 | background: white; 12 | margin: 20px; 13 | padding: 80px; 14 | font-family: sans-serif; 15 | font-size: 24px; 16 | } 17 | 18 | main { 19 | margin: 20px 0 20px 0; 20 | display: grid; 21 | grid-template-columns: min-content 1fr 1fr; 22 | grid-template-areas: 23 | "text1 input1 result" 24 | "text2 input2 msg"; 25 | grid-column-gap: 1em; 26 | grid-row-gap: 10px; 27 | } 28 | 29 | main input { 30 | background: #eef; 31 | font-size: 24px; 32 | } 33 | 34 | #imc { 35 | grid-area: result; 36 | justify-self: center; 37 | } 38 | 39 | #classificacao { 40 | grid-area: msg; 41 | justify-self: center; 42 | color: gray; 43 | font-size: 80%; 44 | } 45 | -------------------------------------------------------------------------------- /03.html/exercicios_1/estilo2.css: -------------------------------------------------------------------------------- 1 | html { 2 | background: gray; 3 | display: grid; 4 | align-items: center; 5 | justify-items: center; 6 | } 7 | 8 | body { 9 | background: white; 10 | margin: 20px; 11 | padding: 80px; 12 | font-family: sans-serif; 13 | font-size: 16px; 14 | width: 800px; 15 | } 16 | 17 | main { 18 | margin: 20px 0 20px 0; 19 | display: grid; 20 | grid-template-columns: min-content 1fr 1fr; 21 | grid-template-areas: 22 | "text1 input1 result" 23 | "text2 input2 msg"; 24 | grid-column-gap: 1em; 25 | grid-row-gap: 10px; 26 | } 27 | 28 | main input { 29 | background: #eef; 30 | font-size: 24px; 31 | } 32 | 33 | #imc { 34 | grid-area: result; 35 | justify-self: center; 36 | } 37 | 38 | #classificacao { 39 | grid-area: msg; 40 | justify-self: center; 41 | color: gray; 42 | font-size: 80%; 43 | } 44 | -------------------------------------------------------------------------------- /01.como_funciona_a_web/servidor/servidor2.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import socket 3 | import sys 4 | import thread 5 | 6 | def on_new_client(clientsocket, endereco): 7 | while True: 8 | msg = clientsocket.recv(1024) 9 | if msg.rstrip() == 'bye': 10 | print 'Cliente em %s:%s desconectado' % endereco 11 | break 12 | print "[%s:%s] %s" % (endereco[0], endereco[1], msg.rstrip()) 13 | clientsocket.send(msg) 14 | clientsocket.close() 15 | 16 | porta = int(sys.argv[1] if len(sys.argv) > 1 else 9090) 17 | s = socket.socket() 18 | s.bind(('', porta)) 19 | 20 | s.listen(5) 21 | 22 | while True: 23 | print 'Esperando conexões na porta %s...' % porta 24 | conexao, endereco = s.accept() 25 | print 'Cliente em %s:%s' % endereco 26 | thread.start_new_thread(on_new_client, (conexao, endereco)) 27 | 28 | s.close() 29 | -------------------------------------------------------------------------------- /01.como_funciona_a_web/servidor/servidor3.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import socket 3 | import sys 4 | import thread 5 | 6 | def on_new_client(clientsocket, endereco): 7 | while True: 8 | msg = clientsocket.recv(1024) 9 | if msg.rstrip() == 'bye': 10 | print 'Cliente em %s:%s desconectado' % endereco 11 | break 12 | print "[%s:%s] %s" % (endereco[0], endereco[1], msg.rstrip()) 13 | clientsocket.send(msg) 14 | clientsocket.close() 15 | 16 | porta = int(sys.argv[1] if len(sys.argv) > 1 else 9090) 17 | s = socket.socket() 18 | s.bind(('', porta)) 19 | 20 | s.listen(5) 21 | 22 | while True: 23 | print 'Esperando conexões na porta %s...' % porta 24 | conexao, endereco = s.accept() 25 | print 'Cliente em %s:%s' % endereco 26 | thread.start_new_thread(on_new_client, (conexao, endereco)) 27 | 28 | s.close() 29 | -------------------------------------------------------------------------------- /tst/sockets/echo/README.md: -------------------------------------------------------------------------------- 1 | # Servidor de eco 2 | 3 | > Importante: Este código deve ser executado usando **Python 3**. 4 | 5 | Para executar o servidor, digite: 6 | 7 | ``` 8 | $ python3 echo.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 | 24 | ## Comportamento esperado 25 | 26 | O servidor deve simplesmente ecoar tudo que receber pelo socket. 27 | Isso significa que, do lado cliente, você deve receber e ver uma 28 | cópia de cada mensagem enviada para o servidor. Para terminar, 29 | digite Ctrl-D (EOF). 30 | -------------------------------------------------------------------------------- /03.html/exercicios_2/estilo.css: -------------------------------------------------------------------------------- 1 | html { 2 | background: gray; 3 | display: grid; 4 | align-items: center; 5 | justify-items: center; 6 | height: 100%; 7 | padding: 0px; 8 | } 9 | 10 | body { 11 | background: white; 12 | padding: 80px; 13 | font-family: sans-serif; 14 | font-size: 16px; 15 | width: 720px; 16 | margin: 40px 0 80px 0; 17 | line-height: 24px; 18 | } 19 | 20 | main { 21 | margin: 0; 22 | display: grid; 23 | grid-template-columns: min-content 1fr 1fr; 24 | grid-template-areas: 25 | "text1 input1 result" 26 | "text2 input2 msg"; 27 | grid-column-gap: 1em; 28 | grid-row-gap: 10px; 29 | } 30 | 31 | main input { 32 | background: #eef; 33 | font-size: 24px; 34 | } 35 | 36 | #imc { 37 | grid-area: result; 38 | justify-self: center; 39 | } 40 | 41 | #classificacao { 42 | grid-area: msg; 43 | justify-self: center; 44 | color: gray; 45 | font-size: 80%; 46 | } 47 | -------------------------------------------------------------------------------- /tst/threads/README.md: -------------------------------------------------------------------------------- 1 | # Introdução a Threads com Python 2 | 3 | > Importante: Este código deve ser executado usando **Python 3**. 4 | 5 | O script `contadores.py` permite criar múltiplos contadores 6 | regressivos que funcionam “simultaneamente” e com intervalos 7 | próprios. Para executar o script, digite: 8 | 9 | ``` 10 | $ python3 mthreads.py 11 | ``` 12 | 13 | ## Comportamento esperado 14 | 15 | Observe que dois contadores diferentes estarão em funcionamento. 16 | Um nomeado `"5 Segundos"` conta de 5 abaixo a cada 1 segundo. E 17 | o segundo contador, nomeado simplesmente de `"A"` (para facilitar 18 | a identificação na saída do programa) contará regressivamente de 19 | 15 abaixo, mas em intervalos de 300ms. 20 | 21 | O script `mthreads2.py` faz exatamente o mesmo, mas usa o estilo 22 | baseado em classes ao invés de funções para criar as _threads_. 23 | 24 | > Sugestão: para entender melhor o funcionamento do código, mude 25 | > os contadores criados. 26 | -------------------------------------------------------------------------------- /01.como_funciona_a_web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | projsw: aula 2 8 | 14 | 15 | 18 | 19 | 20 |
21 |

projsw: aula 2

22 |
23 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /01.como_funciona_a_web/projsw.css: -------------------------------------------------------------------------------- 1 | pre { 2 | font-size: 15px; 3 | background: #eef; 4 | padding: 1em; 5 | } 6 | 7 | figure { 8 | text-align: center; 9 | margin: 2em; 10 | } 11 | 12 | figcaption { 13 | text-align: center; 14 | } 15 | 16 | html { 17 | background: lightgray; 18 | display: grid; 19 | font-family: Roboto, Helvetica Neue, sans-serif; 20 | letter-spacing: -.00278rem; 21 | line-height: 1.4; 22 | } 23 | 24 | p > code, li > code { 25 | color: #15b; 26 | background: #eee; 27 | padding: 4px; 28 | border-radius: 4px; 29 | } 30 | 31 | li { 32 | margin: 6px; 33 | } 34 | 35 | blockquote { 36 | color: #777; 37 | border-left: 10px solid blue; 38 | font-size: 90%; 39 | margin: 0; 40 | padding-left: 1em; 41 | } 42 | 43 | body { 44 | justify-self: center; 45 | background: white; 46 | margin: 20px; 47 | padding: 6em; 48 | width: 660px; 49 | box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.3); 50 | } 51 | 52 | h2 { 53 | margin: 20px 0 0 0; 54 | font-size: 1.3rem; 55 | } 56 | 57 | p { 58 | margin: 12px 0 0 0; 59 | } 60 | 61 | ol, ul { 62 | margin: 12px 0 0 16px; 63 | } 64 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/mural-v7/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 = ` 32 |

${message.message}

33 |

${message.author}

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 |
  1. Crie uma página pessoal no servidor Apache no LCC. Para isso, siga as instruções a seguir:

    27 |
      28 |
    1. 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;

    2. 29 |
    3. 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);

    4. 30 |
    5. 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 ~;

    6. 31 |
    7. 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;

    8. 32 |
  2. 33 |
  3. 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.

  4. 34 |
  5. 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.

  6. 35 |
  7. 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 |
  8. 40 |
  9. 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.

  10. 41 |
  11. 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.

  12. 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /03.html/exercicios_2/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 |
  1. Crie uma página pessoal no servidor Apache no LCC. Para isso, siga as instruções a seguir:

    27 |
      28 |
    1. 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;

    2. 29 |
    3. 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);

    4. 30 |
    5. 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 ~;

    6. 31 |
    7. 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;

    8. 32 |
  2. 33 |
  3. 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.

  4. 34 |
  5. 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.

  6. 35 |
  7. 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 |
  8. 40 |
  9. 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.

  10. 41 |
  11. 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.

  12. 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /01.como_funciona_a_web/servidor_http.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Um mini servidor HTTP 8 | 14 | 15 | 18 | 19 | 20 |
21 |

Um mini servidor HTTP

22 |
23 |

Um minúsculo servidor HTTP

24 |

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 header Content-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 header Content-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 | ![O Padrão Arquitetural MVC](https://upload.wikimedia.org/wikipedia/commons/b/b5/ModelViewControllerDiagram2.svg "O Padrão Arquitetural MVC") 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 | 359 | ``` 360 | 361 | ### Discussão final 362 | 363 | Embora seja um app minúsculo, é fácil ver que o código final é 364 | bem mais interessante, do ponte de vista de Engenharia de 365 | Software, que a primeira versão. Nele, temos clara separação do 366 | _model_ (`imc.js`) do _controller_ (`app.js`) e da _view_ 367 | (`index.html` + `estilo.css`). Alguns, contudo, ainda poderiam 368 | argumentar que a função `atualize_imc()` devesse ser considerada 369 | parte da _view_. Nesse caso, nada impede que se use um objeto ou 370 | mesmo o sistema de módulos de JavaScript para implementar uma 371 | separação mais significativa. Isso fica a critério de cada equipe 372 | ou desenvolvedor. O que importa, contudo, é que a separação agora 373 | é mais concreta e reflete os princípios de MVC. 374 | 375 | ## Exercícios 376 | 377 | 1. Escreva uma pequena aplicação que faça cálculo de câmbio de 378 | reais para dolares americanos e euros. O _frontend_ deve exibir 379 | três _inputs_, sendo um para cada tipo de moeda. Quando o 380 | usuário digitar em qualquer um dos três, os valores dos outros 381 | dois inputs deve ser alterado automaticamente para refletir a 382 | conversão. Use o modelo MVC para construir a aplicação ao 383 | estilo do que foi a última versão do app do IMC. Use 384 | constantes para o câmbio. 385 | 386 | 2. Evolua o app de câmbio da questão anterior para que os valores 387 | de câmbio sejam lidos de um _endpoint_ no servidor que 388 | fornecerá os dados em JSON. Use a [API 389 | Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) 390 | para isso. 391 | 392 | 3. Faça o deploy de sua aplicação para sua página pessoal no LCC. 393 | O endereço de seu app deve ser no formato: 394 | `http://kirk.lcc.ufcg.edu.br/~seulogin/cambio` onde `seulogin` 395 | será trocado por _seu login_ no LCC. 396 | -------------------------------------------------------------------------------- /01.como_funciona_a_web/fundamentos.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Fundamentos Web 3 | --- 4 | # Fundamentos: Processos, Sockets e Threads 5 | 6 | O objetivo deste roteiro é que você tenha algum contato com 7 | processos, sockets, conexões internet e threads, enquanto 8 | conceitos fundamentais para uma plena compreensão da web. Para 9 | isso, neste roteiro, vamos experimentar esses conceitos usando as 10 | APIs da biblioteca padrão de Python 3. Ao final, terá os 11 | elementos necessários para construir, por conta própria, um 12 | minúsculo servidor http. 13 | 14 | 15 | ## Python 16 | 17 | Neste roteiro, utilizaremos Python 3. Observe, contudo, que as 18 | mesmas funcionalidades poderiam ser escritas em praticamente 19 | qualquer linguagem moderna: C, C++, Java, etc (muito embora, a 20 | API possa mudar). Em nosso caso, usaremos o módulo `sockets` de 21 | Python. 22 | 23 | ## Clientes 24 | 25 | Usaremos, inicialmente, o cliente universal para testar e 26 | desenvolver servidores é o `netcat` (você também poderia usar o 27 | histórico `telnet`). Contudo, quando progredirmos para uma versão 28 | minimamente funcional de servidor http, você deve passar a 29 | utilizar um _browser_. Observe que _browsers_ são clientes web 30 | universais: são capazes de emitir _requests_ e interpretar 31 | _responses_ http. 32 | 33 | ## Sockets em Python 34 | 35 | A escrita de um servidor que usa sockets python deve seguir um 36 | roteiro bem estabelecido de chamadas à API. Os métodos invocados 37 | são: 38 | 39 | - `socket()` para instanciar um novo _socket_; 40 | - `bind()` para ligar o socket ao endereço e porta desejados; 41 | - `listen()` para iniciar o servidor e deixá-lo “ouvindo” 42 | conexões 43 | - `accept()` para “aceitar” uma conexão iniciada e criar o socket 44 | de interação; e 45 | - `close()` para fechar um conexão, após concluir seu uso. 46 | 47 | Sugiro uma lida rápida na [documentação de 48 | sockets](https://docs.python.org/3/library/socket.html). Retorne 49 | a ela sempre que tiver dúvidas do que está ocorrendo e de como as 50 | coisas funcionam. 51 | 52 | ## TST 53 | 54 | Para facilitar a distribuição de atividades e exemplos do curso, 55 | vamos usar o [tst](https://github.com/daltonserey/tst) (na 56 | verdade, também me será útil para testar uma nova versão do tst 57 | que independe do antigo tst-online). Para isso, desinstalate sua 58 | versão antiga do tst: 59 | 60 | ``` 61 | $ mv ~/.tst ~/.tst-old 62 | ``` 63 | 64 | E, em seguida, siga as instruções do site e instale o tst em suas 65 | contas pessoais onde pretenda estudar (lembre, use ambientes 66 | Linux ou Mac). Em seguida, faça um teste do `tst`. Faça o 67 | download da atividade `hello`, digitando o seguinte comando em um 68 | terminal): 69 | 70 | ``` 71 | tst checkout hello 72 | ``` 73 | 74 | > IMPORTANTE: para os que ainda se lembram do _workflow_ do tst 75 | > em Programação 1, observe que você não precisa (nem deve) fazer 76 | > login, já que as atividades não são armazenadas no servidor. 77 | > Elas estão públicas na web. 78 | 79 | O comando acima irá criar um diretório `hello` contendo os 80 | arquivos necessários para a atividade. Como não se trata de um 81 | servidor fechado, nestas atividades também não há como fazer 82 | commits. A ideia é apenas que você possa baixar os exemplos e 83 | fazer as atividades. Várias atividades da disciplina serão 84 | distribuídas desta forma. 85 | 86 | ### Cadastre o site da disciplina 87 | 88 | O site de onde o tst baixou o exemplo acima é o site demo do tst. 89 | Para que você possa baixar atividades da disciplina, deve editar 90 | o arquivo `~/.tst/config.yaml` e adicionar os dados do site de 91 | nossa disciplina. Para isso, adicione uma site, contendo os 92 | seguintes dados: 93 | 94 | ``` 95 | sites: 96 | - name: projsw 97 | url: http://www.dsc.ufcg.edu.br/~dalton/projsw/tst 98 | 99 | - name: demo 100 | url: https://raw.githubusercontent.com/daltonserey/tst-demo/master 101 | ``` 102 | 103 | Observe que o site `demo` já existirá no arquivo. Assim, basta 104 | que você adicione os dados do site `projsw` acima. Importante: 105 | você deve colocar o novo site antes do site `demo`. Isso garante 106 | que o `tst` irá buscar as atividades em nosso site antes. 107 | 108 | Uma vez configurado o tst, você já poderá baixar os exemplos e 109 | exercícios iniciais desta aula, digitando: 110 | 111 | ``` 112 | $ tst checkout sockets 113 | ``` 114 | 115 | Isso deve criar o diretório `sockets` contendo os vários exemplos 116 | que usaremos no restante deste roteiro. 117 | 118 | 119 | ## _Hello, World!_ de novo... 120 | 121 | Para facilitar o entendimento, segue um servidor mínimo escrito 122 | em python. A única coisa que ele faz é aceitar uma conexão e 123 | responder 'Hello, World!' através da conexão estabelecida. 124 | 125 | ### Sequência de passos 126 | 127 | 1. Entre no diretório `sockets/hello`. Leia o código antes de 128 | executá-lo e tente entender seu funcionamento. É bastante 129 | simples e intuitivo (além de ter comentários que o explicam 130 | passo a passo). 131 | 132 | 2. Para executar o servidor, digite `python3 hello.py`. Observe 133 | que o servidor é iniciado e o código bloqueia na linha 134 | referente ao `accept()`, aguardando por uma nova conexão. 135 | 136 | 3. Em outro shell, use o `netcat` para contactar seu 137 | servidor, com o comando `netcat localhost 9090` (use `nc` se 138 | estiver em um Mac). Observe que ao estabelecer a conexão, o 139 | servidor _desbloqueia_ a execução e continua a execução depois 140 | da linha do `accept()`. 141 | 142 | > Observe que o retorno de `accept` é um par formado por um 143 | > _socket_ e um segundo par (tupla) a que chamamos de 144 | > `endereco` no código. Esse par, por sua vez, é composto de 145 | > um endereço IP e a porta usada para a conexão. 146 | 147 | 4. Tente executar o servidor novamente. É bastante provável (mas 148 | não 100% garantido) que a inicialização do servidor não tenha 149 | funcionado, porque o endereço já está em uso (mensagem 150 | `OSError: [Errno 48] Address already in use`). Para fazer o 151 | servidor funcionar imediatamente, adicione um número de porta 152 | diferente na linha de comando (lembre, acima de 1024, para 153 | usar uma porta não reservada). 154 | 155 | > Este erro reforça a observação de que um socket é, na 156 | > verdade, um recurso provido pelo sistema operacional... e 157 | > que, neste caso, ainda ele não foi liberado. Se você repetir 158 | > a operação alguns segundos depois é provável que volte a 159 | > funcionar, já que o sistema operacional terá liberado o uso 160 | > da porta. 161 | 162 | Antes de prosseguir, releia o código do servidor. Observe que há 163 | dois objetos que são _sockets_. Um é o socket do servidor 164 | propriamente dito que é onde ele ouve pedidos de conexão (`s` no 165 | código) e o outro é o socket da conexão propriamente dita 166 | (`conexao` no código). Observe ainda que ambos os _sockets_ devem 167 | ser fechados depois de usados. Por isso, é comum usarmos o `with` 168 | para usarmos sockets. O arquivo `hello2.py` mostra um servidor 169 | com a mesma funcionalidade, mas em que uso o `with` para garantir 170 | que o socket é fechado. 171 | 172 | > Observe que o uso de _sockets_ aqui é meramente para permitir a 173 | > compreensão do mecanismo e não das melhores práticas de 174 | > _sockets_ em Python. Há muito mais opções e detalhes que é 175 | > importante conhecer para construir um servidor profissional em 176 | > python. 177 | 178 | ## _Echo Server_ 179 | 180 | Um segundo exemplo clássico do aprendizado de _sockets_ é o que 181 | se chama de _servidor de eco_. Trata-se de um servidor que apenas 182 | devolve (ecoa) uma cópia de todas as mensagens recebidas para o 183 | processo que o contactou. Para implementá-lo, precisamos saber 184 | apenas dois métodos adicionais de _sockets_: 185 | 186 | - `recv()` para receber dados do _socket_ (bloqueante) 187 | - `sendall()` para enviar dados pelo _socket_ (bloqueante) 188 | 189 | > Um socket python pode ser configurado para permitir o envio e a 190 | > recepção de dados em modo _não bloqueante_. Em nosso exemplo, 191 | > usaremos apenas o modo bloqueante (que é o _default_). 192 | 193 | 1. Entre no diretório `sockets/echo`. Leia o código antes de 194 | executá-lo e tente entender seu funcionamento. É bastante 195 | simples e intuitivo. Desta vez, não há comentários, mas 196 | entendo que é suficientemente auto-explicativo. 197 | 198 | 2. Para executar o servidor, digite `python3 echo.py`. Observe 199 | que o servidor é iniciado e que, mais uma vez, o código 200 | bloqueia na linha referente ao `accept()`, aguardando por uma 201 | nova conexão. 202 | 203 | 3. Em outro shell, use o `netcat` para contactar seu servidor, 204 | com o comando `netcat localhost 9090` (use `nc` se estiver em 205 | um Mac). Escreva mensagens no cliente (no _netcat_) e observe 206 | que a cada `Enter` as mensagens são enviadas ao servidor. O 207 | servidor, por sua vez, as recebe, imprime (no terminal do 208 | servidor) e as envia de volta para o cliente. 209 | 210 | > Observe que o método `recv()` tem o valor 4096 como 211 | > parâmetro. Varie o seu valor para 1 e experimente o servidor 212 | > dessa forma, para entender o seu significado. Em seguida, 213 | > leia a documentação de 214 | > [recv](https://docs.python.org/3/library/socket.html#socket.socket.recv). 215 | 216 | 4. Para encerrar a comunicação, observe que o servidor testa se 217 | `not mensagem` é verdadeiro o que só é verdade quando `recv` 218 | retornar uma _string_ vazia de bytes (`b''` em python). 219 | Observe ainda que ao digitar apenas `` do lado cliente, 220 | um `\n` ainda é enviado para o servidor o que não termina sua 221 | execução. Para concluir o uso do `netcat` indique o fim do 222 | arquivo com um `EOF` (Ctrl-D em linux e mac ou Ctrl-Z em 223 | Windows). 224 | 225 | > Exercício 1. Sem copiar e colar o código de nenhum dos dois 226 | > servidores vistos, tente criar um servidor chamado ` 227 | > `echocaps.py` que ecoa mensagens recebidas do cliente em letras 228 | > maiúsculas. Tente apenas relembrar os aspectos principais do 229 | > código. É bastante provável que você não lembre de alguns 230 | > detalhes. Depois de fazer uma tentativa inicial, releia os 231 | > servidores e complemente o seu para fazê-lo funcionar 232 | > corretamente. 233 | 234 | > Exercício 2. Execute o servidor em uma máquina diferente (via 235 | > ssh, por exemplo). Em seguida, contacte o servidor a partir de 236 | > sua máquina de origem (ou de outra qualquer). Observe que você 237 | > você deve usar uma porta liberada para uso nas redes pelas 238 | > quais os dados irão trafegar. Você vai precisar usar 239 | > `socket.gethostbyname(socket.gethostname())` para conseguir o 240 | > IP do servidor. Observe que esse é o IP local, não o público. 241 | 242 | 243 | ## Threads em Python 244 | 245 | Em sua forma básica, cada processo tem uma única _thread_ de 246 | execução. Isto é, uma única linha do código está sendo executada 247 | em qualquer dado momento da _vida_ do processo. Contudo, a 248 | maioria dos sistemas operacionais permite que um único processo 249 | tenha mais de uma _thread_ executando “simultaneamente”. Na 250 | prática, o sistema operacional irá fazer _multi-tasking_ entre as 251 | threads de forma semelhante à que já faz com processos. Ou seja, 252 | em um sistema com uma única CPU apenas um thread estará 253 | efetivamente executando a cada instante de tempo. Como o sistema 254 | operacional, contudo, alterna a execução entre as _threads_ com 255 | frequência, a impressão do usuário é a de que eleas executam de 256 | forma efetivamente ao mesmo tempo e de forma independente. 257 | 258 | Servidores http são tipicamente multi-threaded. Isso permite que 259 | o servidor interaja com uma conexão existente, enquanto permanece 260 | ouvindo uma porta à espera de novas conexões. Da mesma forma, 261 | permite até que múltiplas conexões de diferentes clientes sejam 262 | atendidas simultaneamente. Nesta seção veremos uma brevíssima 263 | introdução à API de _threads_ de Python 3. 264 | 265 | > Relembre que o objetivo deste material não é o de se aprofundar 266 | > nas tecnologias específicas de Python, mas o de lhe oferecer 267 | > uma experiência prática com os conceitos importantes para a 268 | > compreensão da arquitetura web. Tanto _sockets_, quanto 269 | > _threads_ são tecnologias com muito mais a se considerar para 270 | > um uso profissional. Para nossos propósitos, contudo, uma breve 271 | > introdução é suficiente. 272 | 273 | Python e outras linguagens de alto nível permitem escrever código 274 | _multi-threaded_ com bastante facilidade. Com Python 3, usaremos 275 | o módulo `threading` e, em particular, o objeto/classe `Thread` 276 | nele oferecido. Esse objeto pode ser usado de duas formas: 277 | 278 | 1. Pode-se instanciar um objeto diretamente de `Thread`, passando 279 | como argumento uma função como parâmetro e invocando 280 | `start()`. Quando `start()` é invocado, uma nova _thread_ é 281 | criada junto ao sistema operacional e a função passada como 282 | parâmetro é definida como o código a ser executado pela 283 | _thread_. 284 | 285 | 2. Pode-se extender a classe `Thread` com uma classe que tenha o 286 | método `run()`. Em seguida, se instancia a classe e se invoca 287 | o método `start()` (herdado de `Thread`) nos objetos 288 | instanciados. 289 | 290 | 291 | ### Contadores em Threads 292 | 293 | Para entendermos melhor o conceito, usaremos um exemplo bastante 294 | simples: contadores em _threads_. A ideia é que possamos criar 295 | contadores que tenham seu próprio intervalo de atualização e que 296 | funcionem independentemente do restante do programa. 297 | 298 | 1. Para isso, baixe a atividade `threads` pelo tst. Use o comando 299 | `tst checkout threads`. Lembre-se que isso irá criar um diretório 300 | dentro do diretório em que o comando for executado. Assim, 301 | escolha antes onde quer que a atividade seja gravada. 302 | 303 | 2. Entre no diretório `threads` e antes de executar o programa, 304 | leia o código do script `mthreads.py`. Tente entender e prever 305 | como irá funcionar. Explique o que você acha que irá ver na 306 | saída do programa, quando ele for executado. 307 | 308 | 3. Execute o programa `mthreads.py`. Para isso, use o comando 309 | `python3 mthreads.py`. O programa criará dois contadores que 310 | funcionam “simultaneamente” (em _threads_) e cada um com seu 311 | próprio intervalo de atualização. 312 | 313 | > **Observe** a relação de ordem entre as mensagens impressas 314 | > e a posição dos respectivos `print`s no código. Perceba que 315 | > a ordem pode não ser intuitiva. Por exemplo, por que a 316 | > mensagem `Fim` não aparece ao fim de toda a execução? 317 | 318 | 4. Observe que `mthreads.py` foi escrita usando um estilo 319 | funcional (não há classes no código). As threads são criadas 320 | (instanciadas) diretamente de `Thread` e imediatamente 321 | iniciadas por `start()`. 322 | 323 | 5. O script `mthreads2.py` tem o mesmo efeito que o primeiro 324 | script. A diferença é o estilo usado. Neste caso, usamos algo 325 | mais orientado a objetos: extendemos a classe `Thread` e 326 | implementamos o método `run()`. Cabe a você escolher qual o 327 | estilo mais apropriado para o problema em questão. 328 | 329 | > Exercício 3. Nada impede instanciar e iniciar as _threads_ em 330 | > momentos diferentes. Para isso, teríamos que ter feito uma 331 | > atribuição na instanciação da _thread_ e depois ter usado o 332 | > método `start()`. 333 | 334 | ## Mini chat 335 | 336 | > Exercício 4. Escreva um mini chat, tal como especificado 337 | > abaixo. 338 | 339 | Para este exercício você precisa apenas de um servidor, porque 340 | como cliente usaremos o `netcat`. A ideia para permitir que 341 | vários usuários troquem mensagens em um chat é conectar todos os 342 | clientes a um servidor central. Dessa forma, cada mensagem deve 343 | ser enviada de um cliente ao servidor e este a repassará para 344 | todos os clientes conectados. 345 | 346 | Seu servidor deve ter uma _thread_ principal responsável por 347 | “escutar” pedidos de conexão e por instanciar e inicializar uma 348 | nova _thread_ para cada cliente conectado. A função a ser 349 | executada pela _thread_ deve adicionar o socket a uma estrutura 350 | de dados (uma lista ou dicionário) e deve ficar à espera por 351 | mensagens enviadas pelo cliente. A cada nova mensagem recebida, 352 | deve enviar uma cópia da mensagem para cada conexão presente na 353 | estrutura de dados. 354 | 355 | O comando/mensagem especial `:bye` deve poder ser usado por 356 | qualquer cliente/usuário para desconectar-se do chat. Quando um 357 | `:bye` for recebido, o socket do cliente é fechado, uma mensagem 358 | de saída é enviada para todos os usuários, o seu _socket_ é 359 | retirado da estrutura de dados e a _thread_ correspondente é 360 | encerrada. 361 | -------------------------------------------------------------------------------- /01.como_funciona_a_web/fundamentos.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Fundamentos Web 8 | 14 | 15 | 18 | 19 | 20 |
21 |

Fundamentos Web

22 |
23 |

Fundamentos: Processos, Sockets e Threads

24 |

O objetivo deste roteiro é que você tenha algum contato com processos, sockets, conexões internet e threads, enquanto conceitos fundamentais para uma plena compreensão da web. Para isso, neste roteiro, vamos experimentar esses conceitos usando as APIs da biblioteca padrão de Python 3. Ao final, terá os elementos necessários para construir, por conta própria, um minúsculo servidor http.

25 |

Python

26 |

Neste roteiro, utilizaremos Python 3. Observe, contudo, que as mesmas funcionalidades poderiam ser escritas em praticamente qualquer linguagem moderna: C, C++, Java, etc (muito embora, a API possa mudar). Em nosso caso, usaremos o módulo sockets de Python.

27 |

Clientes

28 |

Usaremos, inicialmente, o cliente universal para testar e desenvolver servidores é o netcat (você também poderia usar o histórico telnet). Contudo, quando progredirmos para uma versão minimamente funcional de servidor http, você deve passar a utilizar um browser. Observe que browsers são clientes web universais: são capazes de emitir requests e interpretar responses http.

29 |

Sockets em Python

30 |

A escrita de um servidor que usa sockets python deve seguir um roteiro bem estabelecido de chamadas à API. Os métodos invocados são:

31 | 38 |

Sugiro uma lida rápida na documentação de sockets. Retorne a ela sempre que tiver dúvidas do que está ocorrendo e de como as coisas funcionam.

39 |

TST

40 |

Para facilitar a distribuição de atividades e exemplos do curso, vamos usar o tst (na verdade, também me será útil para testar uma nova versão do tst que independe do antigo tst-online). Para isso, desinstalate sua versão antiga do tst:

41 |
$ mv ~/.tst ~/.tst-old
42 |

E, em seguida, siga as instruções do site e instale o tst em suas contas pessoais onde pretenda estudar (lembre, use ambientes Linux ou Mac). Em seguida, faça um teste do tst. Faça o download da atividade hello, digitando o seguinte comando em um terminal):

43 |
tst checkout hello
44 |
45 |

IMPORTANTE: para os que ainda se lembram do workflow do tst em Programação 1, observe que você não precisa (nem deve) fazer login, já que as atividades não são armazenadas no servidor. Elas estão públicas na web.

46 |
47 |

O comando acima irá criar um diretório hello contendo os arquivos necessários para a atividade. Como não se trata de um servidor fechado, nestas atividades também não há como fazer commits. A ideia é apenas que você possa baixar os exemplos e fazer as atividades. Várias atividades da disciplina serão distribuídas desta forma.

48 |

Cadastre o site da disciplina

49 |

O site de onde o tst baixou o exemplo acima é o site demo do tst. Para que você possa baixar atividades da disciplina, deve editar o arquivo ~/.tst/config.yaml e adicionar os dados do site de nossa disciplina. Para isso, adicione uma site, contendo os seguintes dados:

50 |
sites:
 51 | - name: projsw
 52 |   url: http://www.dsc.ufcg.edu.br/~dalton/projsw/tst
 53 | 
 54 | - name: demo
 55 |   url: https://raw.githubusercontent.com/daltonserey/tst-demo/master
56 |

Observe que o site demo já existirá no arquivo. Assim, basta que você adicione os dados do site projsw acima. Importante: você deve colocar o novo site antes do site demo. Isso garante que o tst irá buscar as atividades em nosso site antes.

57 |

Uma vez configurado o tst, você já poderá baixar os exemplos e exercícios iniciais desta aula, digitando:

58 |
$ tst checkout sockets
59 |

Isso deve criar o diretório sockets contendo os vários exemplos que usaremos no restante deste roteiro.

60 |

Hello, World! de novo…

61 |

Para facilitar o entendimento, segue um servidor mínimo escrito em python. A única coisa que ele faz é aceitar uma conexão e responder ‘Hello, World!’ através da conexão estabelecida.

62 |

Sequência de passos

63 |
    64 |
  1. Entre no diretório sockets/hello. Leia o código antes de executá-lo e tente entender seu funcionamento. É bastante simples e intuitivo (além de ter comentários que o explicam passo a passo).

  2. 65 |
  3. Para executar o servidor, digite python3 hello.py. Observe que o servidor é iniciado e o código bloqueia na linha referente ao accept(), aguardando por uma nova conexão.

  4. 66 |
  5. Em outro shell, use o netcat para contactar seu servidor, com o comando netcat localhost 9090 (use nc se estiver em um Mac). Observe que ao estabelecer a conexão, o servidor desbloqueia a execução e continua a execução depois da linha do accept().

    67 |
    68 |

    Observe que o retorno de accept é um par formado por um socket e um segundo par (tupla) a que chamamos de endereco no código. Esse par, por sua vez, é composto de um endereço IP e a porta usada para a conexão.

    69 |
  6. 70 |
  7. Tente executar o servidor novamente. É bastante provável (mas não 100% garantido) que a inicialização do servidor não tenha funcionado, porque o endereço já está em uso (mensagem OSError: [Errno 48] Address already in use). Para fazer o servidor funcionar imediatamente, adicione um número de porta diferente na linha de comando (lembre, acima de 1024, para usar uma porta não reservada).

    71 |
    72 |

    Este erro reforça a observação de que um socket é, na verdade, um recurso provido pelo sistema operacional… e que, neste caso, ainda ele não foi liberado. Se você repetir a operação alguns segundos depois é provável que volte a funcionar, já que o sistema operacional terá liberado o uso da porta.

    73 |
  8. 74 |
75 |

Antes de prosseguir, releia o código do servidor. Observe que há dois objetos que são sockets. Um é o socket do servidor propriamente dito que é onde ele ouve pedidos de conexão (s no código) e o outro é o socket da conexão propriamente dita (conexao no código). Observe ainda que ambos os sockets devem ser fechados depois de usados. Por isso, é comum usarmos o with para usarmos sockets. O arquivo hello2.py mostra um servidor com a mesma funcionalidade, mas em que uso o with para garantir que o socket é fechado.

76 |
77 |

Observe que o uso de sockets aqui é meramente para permitir a compreensão do mecanismo e não das melhores práticas de sockets em Python. Há muito mais opções e detalhes que é importante conhecer para construir um servidor profissional em python.

78 |
79 |

Echo Server

80 |

Um segundo exemplo clássico do aprendizado de sockets é o que se chama de servidor de eco. Trata-se de um servidor que apenas devolve (ecoa) uma cópia de todas as mensagens recebidas para o processo que o contactou. Para implementá-lo, precisamos saber apenas dois métodos adicionais de sockets:

81 | 85 |
86 |

Um socket python pode ser configurado para permitir o envio e a recepção de dados em modo não bloqueante. Em nosso exemplo, usaremos apenas o modo bloqueante (que é o default).

87 |
88 |
    89 |
  1. Entre no diretório sockets/echo. Leia o código antes de executá-lo e tente entender seu funcionamento. É bastante simples e intuitivo. Desta vez, não há comentários, mas entendo que é suficientemente auto-explicativo.

  2. 90 |
  3. Para executar o servidor, digite python3 echo.py. Observe que o servidor é iniciado e que, mais uma vez, o código bloqueia na linha referente ao accept(), aguardando por uma nova conexão.

  4. 91 |
  5. Em outro shell, use o netcat para contactar seu servidor, com o comando netcat localhost 9090 (use nc se estiver em um Mac). Escreva mensagens no cliente (no netcat) e observe que a cada Enter as mensagens são enviadas ao servidor. O servidor, por sua vez, as recebe, imprime (no terminal do servidor) e as envia de volta para o cliente.

    92 |
    93 |

    Observe que o método recv() tem o valor 4096 como parâmetro. Varie o seu valor para 1 e experimente o servidor dessa forma, para entender o seu significado. Em seguida, leia a documentação de recv.

    94 |
  6. 95 |
  7. Para encerrar a comunicação, observe que o servidor testa se not mensagem é verdadeiro o que só é verdade quando recv retornar uma string vazia de bytes (b'' em python). Observe ainda que ao digitar apenas <Enter> do lado cliente, um \n ainda é enviado para o servidor o que não termina sua execução. Para concluir o uso do netcat indique o fim do arquivo com um EOF (Ctrl-D em linux e mac ou Ctrl-Z em Windows).

  8. 96 |
97 |
98 |

Exercício 1. Sem copiar e colar o código de nenhum dos dois servidores vistos, tente criar um servidor chamado echocaps.py` que ecoa mensagens recebidas do cliente em letras maiúsculas. Tente apenas relembrar os aspectos principais do código. É bastante provável que você não lembre de alguns detalhes. Depois de fazer uma tentativa inicial, releia os servidores e complemente o seu para fazê-lo funcionar corretamente.

99 |
100 |
101 |

Exercício 2. Execute o servidor em uma máquina diferente (via ssh, por exemplo). Em seguida, contacte o servidor a partir de sua máquina de origem (ou de outra qualquer). Observe que você você deve usar uma porta liberada para uso nas redes pelas quais os dados irão trafegar. Você vai precisar usar socket.gethostbyname(socket.gethostname()) para conseguir o IP do servidor. Observe que esse é o IP local, não o público.

102 |
103 |

Threads em Python

104 |

Em sua forma básica, cada processo tem uma única thread de execução. Isto é, uma única linha do código está sendo executada em qualquer dado momento da vida do processo. Contudo, a maioria dos sistemas operacionais permite que um único processo tenha mais de uma thread executando “simultaneamente”. Na prática, o sistema operacional irá fazer multi-tasking entre as threads de forma semelhante à que já faz com processos. Ou seja, em um sistema com uma única CPU apenas um thread estará efetivamente executando a cada instante de tempo. Como o sistema operacional, contudo, alterna a execução entre as threads com frequência, a impressão do usuário é a de que eleas executam de forma efetivamente ao mesmo tempo e de forma independente.

105 |

Servidores http são tipicamente multi-threaded. Isso permite que o servidor interaja com uma conexão existente, enquanto permanece ouvindo uma porta à espera de novas conexões. Da mesma forma, permite até que múltiplas conexões de diferentes clientes sejam atendidas simultaneamente. Nesta seção veremos uma brevíssima introdução à API de threads de Python 3.

106 |
107 |

Relembre que o objetivo deste material não é o de se aprofundar nas tecnologias específicas de Python, mas o de lhe oferecer uma experiência prática com os conceitos importantes para a compreensão da arquitetura web. Tanto sockets, quanto threads são tecnologias com muito mais a se considerar para um uso profissional. Para nossos propósitos, contudo, uma breve introdução é suficiente.

108 |
109 |

Python e outras linguagens de alto nível permitem escrever código multi-threaded com bastante facilidade. Com Python 3, usaremos o módulo threading e, em particular, o objeto/classe Thread nele oferecido. Esse objeto pode ser usado de duas formas:

110 |
    111 |
  1. Pode-se instanciar um objeto diretamente de Thread, passando como argumento uma função como parâmetro e invocando start(). Quando start() é invocado, uma nova thread é criada junto ao sistema operacional e a função passada como parâmetro é definida como o código a ser executado pela thread.

  2. 112 |
  3. Pode-se extender a classe Thread com uma classe que tenha o método run(). Em seguida, se instancia a classe e se invoca o método start() (herdado de Thread) nos objetos instanciados.

  4. 113 |
114 |

Contadores em Threads

115 |

Para entendermos melhor o conceito, usaremos um exemplo bastante simples: contadores em threads. A ideia é que possamos criar contadores que tenham seu próprio intervalo de atualização e que funcionem independentemente do restante do programa.

116 |
    117 |
  1. Para isso, baixe a atividade threads pelo tst. Use o comando tst checkout threads. Lembre-se que isso irá criar um diretório dentro do diretório em que o comando for executado. Assim, escolha antes onde quer que a atividade seja gravada.

  2. 118 |
  3. Entre no diretório threads e antes de executar o programa, leia o código do script mthreads.py. Tente entender e prever como irá funcionar. Explique o que você acha que irá ver na saída do programa, quando ele for executado.

  4. 119 |
  5. Execute o programa mthreads.py. Para isso, use o comando python3 mthreads.py. O programa criará dois contadores que funcionam “simultaneamente” (em threads) e cada um com seu próprio intervalo de atualização.

  6. 120 |
121 |
122 |

Observe a relação de ordem entre as mensagens impressas e a posição dos respectivos prints no código. Perceba que a ordem pode não ser intuitiva. Por exemplo, por que a mensagem Fim não aparece ao fim de toda a execução?

123 |
124 |
    125 |
  1. Observe que mthreads.py foi escrita usando um estilo funcional (não há classes no código). As threads são criadas (instanciadas) diretamente de Thread e imediatamente iniciadas por start().

  2. 126 |
  3. O script mthreads2.py tem o mesmo efeito que o primeiro script. A diferença é o estilo usado. Neste caso, usamos algo mais orientado a objetos: extendemos a classe Thread e implementamos o método run(). Cabe a você escolher qual o estilo mais apropriado para o problema em questão.

  4. 127 |
128 |
129 |

Exercício 3. Nada impede instanciar e iniciar as threads em momentos diferentes. Para isso, teríamos que ter feito uma atribuição na instanciação da thread e depois ter usado o método start().

130 |
131 |

Mini chat

132 |
133 |

Exercício 4. Escreva um mini chat, tal como especificado abaixo.

134 |
135 |

Para este exercício você precisa apenas de um servidor, porque como cliente usaremos o netcat. A ideia para permitir que vários usuários troquem mensagens em um chat é conectar todos os clientes a um servidor central. Dessa forma, cada mensagem deve ser enviada de um cliente ao servidor e este a repassará para todos os clientes conectados.

136 |

Seu servidor deve ter uma thread principal responsável por “escutar” pedidos de conexão e por instanciar e inicializar uma nova thread para cada cliente conectado. A função a ser executada pela thread deve adicionar o socket a uma estrutura de dados (uma lista ou dicionário) e deve ficar à espera por mensagens enviadas pelo cliente. A cada nova mensagem recebida, deve enviar uma cópia da mensagem para cada conexão presente na estrutura de dados.

137 |

O comando/mensagem especial :bye deve poder ser usado por qualquer cliente/usuário para desconectar-se do chat. Quando um :bye for recebido, o socket do cliente é fechado, uma mensagem de saída é enviada para todos os usuários, o seu socket é retirado da estrutura de dados e a thread correspondente é encerrada.

138 | 139 | 140 | -------------------------------------------------------------------------------- /06.web_apps/3-frontend_mural/text.md: -------------------------------------------------------------------------------- 1 | ## Exemplo em sala de aula 2 | 3 | Em sala de aula decidimos fazer o _frontend_ de um pequeno app de 4 | um mural _web_. O app deve apenas coletar mensagens e publicá-las 5 | na própria página. 6 | 7 | 8 | Por ora, nos concetramos apenas na parte 9 | frontend. Por isso, devemos assumir que o _backend_ deve ser 10 | prover um _endpoint_ que fornecerá um JSON das mensagens. Para 11 | simular isso, usaremos um arquivo JSON chamado `mensagens.json` 12 | que será servido por nosso servidor de desenvolvimento via HTTP. 13 | 14 | ### Versão 1 15 | 16 | Este é o html de nossa primeira versão do app. Observe que 17 | consiste de uma tag `main` contendo todo o html e a tag `script` 18 | que faz a carga do JavaScript da aplicação. Nenhum css é usado 19 | nesta versão. 20 | 21 | ```html 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | Sua mensagem: 32 | Autor: 33 | 34 |
35 |
36 | 37 | 38 | 39 | 40 | 41 | ``` 42 | 43 | O arquivo `app.js` reúne a lógica. Observe que o _model_, aqui 44 | chamado `messages`, é mantido na variável global 45 | `window.messages`. Obviamente, essa é uma decisão temporária, 46 | para facilitar o desenvolvimento e o _debugging_. 47 | 48 | O código principal do módulo define as funções `render()` e 49 | `get_messages()` (sendo esta última uma função assíncrona). Ao 50 | final do módulo, a função `get_messages()` é invocada. Por ser 51 | uma _promise_, usamos o método `then()` para registrar uma função 52 | de callback que irá invocar a função `render()` quando a 53 | _promise_ for cumprida (ou seja, quando as mensagens houverem 54 | sido lidas). 55 | 56 | ``` 57 | window.messages = []; 58 | 59 | function render() { 60 | let $msgs = document.getElementById("msgs"); 61 | $msgs.innerHTML = ''; 62 | messages.forEach(function (message) { 63 | let html = `

${message.message}

64 |

${message.author}

65 | `; 66 | 67 | let novo = document.createElement("div"); 68 | novo.innerHTML = html; 69 | $msgs.appendChild(novo); 70 | }); 71 | } 72 | 73 | async function get_messages() { 74 | let response = await fetch('mensagens.json'); 75 | let data = await response.json(); 76 | window.messages = data; 77 | } 78 | 79 | get_messages() 80 | .then(function () { 81 | render(); 82 | }); 83 | ``` 84 | 85 | ## Versão 2 86 | 87 | Um dos problemas com o código acima é que o isolamento do _model_ 88 | é muito precário. Nesta segunda versão, vamos colocar o _model_ 89 | em um script separado, chamado `messages.js` e vamos usar o 90 | sistema de módulos de JavaScript para integrar tudo. 91 | 92 | O arquivo `messages.js` fica assim: 93 | 94 | ```javascript 95 | const messages = []; 96 | 97 | async function get_messages() { 98 | let response = await fetch('mensagens.json'); 99 | let data = await response.json(); 100 | messages.push(...data); 101 | } 102 | 103 | export {get_messages, messages}; 104 | ``` 105 | 106 | Observe que `export` desta vez não usa a palavra-chave `default`. 107 | Nesye caso, optamos por exportar um objeto contendo os dois 108 | objetos que queremos exportar: `get_messages` e `messages`. Veja 109 | que usamos ainda a notação simplificada para definir esse objeto. 110 | 111 | Um outro detalhe a se observar no código acima é a linha em que 112 | os dados são colocados em `messages`. Observe que usamos o método 113 | `push` que permite adicionar vários elementos de uma única vez em 114 | um array. Além disso, usamos o operador `...` (chamado _spread_) 115 | que desmonta um _array_ javascript em cada um de seus elementos 116 | independentes. Logo, a chamada `messages.push(...data)` é 117 | equivalente a algo do tipo `messages.push(data[0], data[1], 118 | ...)` (obviamente, este último código é apenas pseudo-código e 119 | não funciona). 120 | 121 | O código de `app.js`, por sua vez, passa a ser: 122 | 123 | ```javascript 124 | import {get_messages, messages} from './messages.js'; 125 | 126 | function render() { 127 | let $msgs = document.getElementById("msgs"); 128 | $msgs.innerHTML = ''; 129 | messages.forEach(function (message) { 130 | let html = `

${message.message}

131 |

${message.author}

132 | `; 133 | 134 | let novo = document.createElement("div"); 135 | novo.innerHTML = html; 136 | $msgs.appendChild(novo); 137 | }); 138 | } 139 | 140 | get_messages() 141 | .then(function () { 142 | render(); 143 | }); 144 | ``` 145 | 146 | ### Versão 3 147 | 148 | Nesta terceira versão, passaremos à questão visual do app. A 149 | primeira coisa que precisamos é decidir o que queremos. Assim, 150 | antes de vermos o código vamos à especificação. 151 | 152 | 1. Os campos a serem preenchidos devem ser apresentados no 153 | topo da tela. A cor da fonte deve ser um pouco mais fraca 154 | dado que não é o elemento gráfico mais importante da 155 | interface. O tamanho pode ser o mesmo, contudo. 156 | 157 | 2. A listagem de mensagens deve ser o principal destaque da 158 | página. Cada mensagem deve ser destacada das demais 159 | visualmente, com borda e fundo coloridos para facilitar 160 | a identificação e a percepção de que cada mensagem é uma 161 | unidade. 162 | 163 | 3. O texto da mensagem deve ser destacado de forma diferenciada 164 | em relação ao nome do autor ou da hora em que foi produzida. 165 | Estes últimos devem usar fonte menor e com cor mais fraca. A 166 | cor do texto deve ser diferente. 167 | 168 | Para implementar isso, começaremos adicionando uma folha de 169 | estilo. Para isso, adicionamos um elemento `link` no cabeçalho do 170 | html. 171 | 172 | ```html 173 | 174 | ... 175 | 176 | 177 | ``` 178 | 179 | E também adicionamos um arquivo `estilo.css`, contendo as regras 180 | de estilo básicas. 181 | 182 | ```css 183 | html, body { 184 | margin: 0; 185 | padding: 0; 186 | background: gray; 187 | } 188 | 189 | body { 190 | margin: 20px; 191 | background: white; 192 | padding: 20px; 193 | } 194 | 195 | p { 196 | color: #77c; 197 | font-family: arial; 198 | } 199 | ``` 200 | 201 | A primeira regra acima faz um _reset_ das margens e _padding_ dos 202 | elementos `html` e `body` (e coloca uma cor de _background_ 203 | cinza). A segunda estiliza o `body` de forma que fique destacado 204 | do html (a ideia aqui é apenas nos dar noção exata do que está 205 | ocorrendo no DOM e não efetivamente defender que esse é um estilo 206 | apropriado). Finalmente, a terceira regra estiliza o elemento `p` 207 | para uma cor mais suave e a fonte `arial`. É aqui que nos 208 | deparamos com o primeiro problema: essa regra afeta todo o DOM, 209 | incluindo as mensagens. 210 | 211 | ### Versão 4 212 | 213 | De fato, o problema mencionado acima é derivado de outro 214 | problema. Nosso DOM é uma única grande estrutura de dados sobre a 215 | qual os algoritmos de renderização e estilização atuam. Contudo, 216 | conceitualmente, as mensagens são pequenas unidades que 217 | gostaríamos que fossem tratadas de forma mais independentes. 218 | 219 | Há várias formas de resolver esse problema. Uma delas é criarmos 220 | classes CSS para especificarmos melhor quais elementos `p` devem 221 | ser afetados ou não. Ou, alternativamente, usar seletores mais 222 | bem pensados para atingir apenas os elementos desejados. Por 223 | exemplo, podemos alterar terceira a regra acima para: 224 | 225 | ```css 226 | main > p { 227 | color: #77c; 228 | font-family: arial; 229 | } 230 | ``` 231 | 232 | Observe que esta regra determina que apenas os parágrafos filhos 233 | imediatos de main sejam afetados pela mudança. 234 | 235 | Embora este caminho pareça interessante (e de fato é em algumas 236 | situações), ele tem consequências. Imagine que o app agora evolua 237 | para adicionar um `footer` e um `header`. Se esses elementos 238 | contiverem elementos `p`, eles não são mais afetados pela regra. 239 | E você precisará criar regras específicas para eles, mesmo que 240 | seja para ter o mesmo efeito. Outra situação possível é que o 241 | próprio elemento `main` pode vir a ter uma subestrutura além das 242 | mensagens. Se houver elementos `p` dentro dessa subestrutura que 243 | não sejam filhos diretos de `main`, eles não serão atingidos pela 244 | regra que criamos. Finalmente, há a questão conceitual. A ideia 245 | da regra original é dizer que qualquer parágrafo na aplicação 246 | deve ser estilizado daquela forma, *exceto* as mensagens que 247 | devem ser unidades mais independentes. A regra modificada acima 248 | não reflete isso de maneira adequada. 249 | 250 | 251 | ### Versão 5 252 | 253 | Uma ideia que poderíamos experimentar seria estilizar os 254 | parágrafos de dentro das mensagens de forma diferente. Uma forma 255 | de fazer isso poderia ser adicionar uma folha de estilo 256 | específica das mensagens. No código abaixo da função `render()` 257 | uma tentativa é feita, adicionando-se um elemento `style` no html 258 | do `div` de cada mensagem adicionada. Para efeitos de 259 | experimentação, vamos apenas mudar a cor dos parágrafos para 260 | vermelho. 261 | 262 | ```javascript 263 | function render() { 264 | let $msgs = document.getElementById("msgs"); 265 | $msgs.innerHTML = ''; 266 | messages.forEach(function (message) { 267 | let html = ` 270 |

${message.message}

271 |

${message.author}

272 | `; 273 | 274 | let novo = document.createElement("div"); 275 | novo.innerHTML = html; 276 | $msgs.appendChild(novo); 277 | }); 278 | } 279 | ``` 280 | 281 | O problema dessa abordagem é que embora a redefinição de estilo 282 | esteja contida dentro do `div` de uma mensagem, ela _vaza_ para 283 | todo o html. E, como resultado, todos os parágrafos passam as ser 284 | estilizados em vermelho, mesmo os que estão fora das mensagens. 285 | 286 | Infelizmente, esse é um problema de CSS. As folhas de estilo 287 | podem ser definidas em qualquer ponto do texto do HTML ou do DOM. 288 | Contudo, elas têm validade para toda a árvore. 289 | 290 | ### Versão 6 291 | 292 | A solução para o problema acima se dá através do uso do que se 293 | chama de [Shadow 294 | DOM](https://developers.google.com/web/fundamentals/web-components/shadowdom). 295 | A ideia é que a visualização seja composta não a partir de uma 296 | única grande árvore DOM, mas por um conjunto de pequenas árvores. 297 | Isso permite que cada sub-árvore seja independente das demais em termos de 298 | estilização. Em particular, cada uma pode ter seu próprio conjunto 299 | de regras e essas regras não devem afetar as demais. 300 | 301 | Uma forma de entender Shadow DOM é fazer uma analogia: Shadow DOM 302 | é para a estilização e CSS o que o conceito de escopo é para a 303 | programação. De fato, com Shadow DOM podemos falar do escopo das 304 | regras de CSS. 305 | 306 | O código abaixo de `render()` evolui sobre a versão anterior. 307 | Nele, usamos a API do browser para criar um Shadow DOM para o 308 | novo elemento e colocamos todo o html que está sendo criado 309 | dentro dessa sub-árvore a que chamamos, no código, de `shadow`. 310 | 311 | ```javascript 312 | function render() { 313 | let $msgs = document.getElementById("msgs"); 314 | $msgs.innerHTML = ''; 315 | messages.forEach(function (message) { 316 | let html = ` 319 |

${message.message}

320 |

${message.author}

321 | `; 322 | 323 | let novo = document.createElement("div"); 324 | let shadow = novo.attachShadow({"mode": "open"}); 325 | shadow.innerHTML = html; 326 | $msgs.appendChild(novo); 327 | }); 328 | } 329 | ``` 330 | 331 | Perceba que a diferença do código é mínima. Basicamente, a linha 332 | que cria a Shadow DOM e a linha seguinte em que mudamos o 333 | `innerHTML` da shadow ao invés do elemento original. O efeito é 334 | imediato e pode ser observado no _browser_: o estilo das 335 | mensagens agora fica retido em cada mensagem sem afetar o 336 | restante do html. Em termos do DOM, também vale a pena conferir 337 | o resultado. Para isso, inspecione o DOM no seu _browser_. Você 338 | deve ver alguma coisa semelhante à figura abaixo. 339 | 340 | ![Shadow DOM no Browser](shadow_dom.png) 341 | 342 | É importante observar que o nó original, chamado de _host_ do 343 | Shadow DOM, ainda é mantido no DOM principal. Esse nó e qualquer 344 | sub-árvore desse nó não será renderizado na interface do usuário. 345 | Como ele tem um Shadow DOM anexado, seu espaço na interface será 346 | ocupado pela renderização de sua Shadow DOM. 347 | 348 | 349 | ### Versão 7 350 | 351 | A solução anterior nos permite estilizar o DOM das mensagens com 352 | segurança. Segue agora a nova versão de `render()` contendo a 353 | nova folha de estilo, com a segurança de que ela não pode _vazar_ 354 | para o restante do DOM. 355 | 356 | ```javascript 357 | function render() { 358 | let $msgs = document.getElementById("msgs"); 359 | $msgs.innerHTML = ''; 360 | messages.forEach(function (message) { 361 | let html = ` 386 |

${message.message}

387 |

${message.author}

388 | `; 389 | 390 | let novo = document.createElement("div"); 391 | let shadow = novo.attachShadow({"mode": "open"}) 392 | shadow.innerHTML = html; 393 | $msgs.appendChild(novo); 394 | }); 395 | } 396 | ``` 397 | 398 | ### Versão 8 399 | 400 | Uma das coisas que incomoda muita gente é ter código CSS dentro 401 | do código JavaScript. Uma solução simples é usar uma folha de 402 | estilo externa tal como fazemos com html convencional. Dito isto, 403 | segue a última versão de `render()` desta vez com a adição de uma 404 | folha de estilo externa. 405 | 406 | ```javascript 407 | function render() { 408 | let $msgs = document.getElementById("msgs"); 409 | $msgs.innerHTML = ''; 410 | messages.forEach(function (message) { 411 | let html = ` 412 |

${message.message}

413 |

${message.author}

414 | `; 415 | 416 | let novo = document.createElement("div"); 417 | let shadow = novo.attachShadow({"mode": "open"}); 418 | shadow.innerHTML = html; 419 | $msgs.appendChild(novo); 420 | }); 421 | } 422 | ``` 423 | 424 | 425 | ### Versão 9 426 | 427 | Como última versão de nosso app, vejamos como seria o código de 428 | `render()` se pudéssemos contar com uma 429 | tag customizada para nossa aplicação. Vamos assumir que a tag seja 430 | `ps-message` (`ps` de _projeto de software_) e que essa tag aceite 431 | três atributos: `message`, `author` e `at` que serviriam 432 | justamente para configurar o elemento com o conteúdo associado. 433 | Se a tag existisse, poderíamos escrever código html com ela 434 | assim: 435 | 436 | ```html 437 | 440 | ``` 441 | 442 | Assim, nossa aplicação deve criar uma nova tag `ps-message` para 443 | cada mensagem recebida do servidor. Isso pode ser conseguido com 444 | o seguinte código para a função `render()` de nosso app. 445 | 446 | ```javascript 447 | function render() { 448 | let $msgs = document.getElementById("msgs"); 449 | $msgs.innerHTML = ''; 450 | messages.forEach(function (message) { 451 | let novo = document.createElement("div"); 452 | novo.innerHTML = ``; 453 | $msgs.appendChild(novo); 454 | }); 455 | } 456 | ``` 457 | 458 | Observe, contudo, que esta versão do código cria um elemento 459 | `div` apenas para comportar dentro dele o nosso elemento 460 | `ps-message`. Infelizmente, a função `createElement()` da API do 461 | _browser_ não cria um elemento com os atributos iniciais. Por 462 | isso, pode não ser adequado criar diretamente o elemento 463 | `ps-message`. É possível, por outro lado, criar o elemento e 464 | definir os atributos em seguida. O código, nesse caso, ficaria 465 | assim: 466 | 467 | ```javascript 468 | function render() { 469 | let $msgs = document.getElementById("msgs"); 470 | $msgs.innerHTML = ''; 471 | messages.forEach(function (message) { 472 | let novo = document.createElement("ps-message"); 473 | novo.setAttribute('message', message.message); 474 | novo.setAttribute('author', message.author); 475 | novo.setAttribute('at', message.at); 476 | $msgs.appendChild(novo); 477 | }); 478 | } 479 | ``` 480 | 481 | Embora seja um pouco mais longa, é perfeitamente compreensível e 482 | clara. Além disso, esta versão tem a vantagem de criar 483 | exclusivamente o elemento `ps-message`, sem a necessidade do 484 | `div` extra. 485 | --------------------------------------------------------------------------------