├── .github
├── FUNDING.yml
└── ISSUE_TEMPLATE
│ └── novo-post.md
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs
└── img
│ ├── gif_readme
│ └── blog.gif
│ └── logo
│ ├── blog_logo.png
│ ├── machine.png
│ ├── name_logo.png
│ └── opendev_logo.svg
└── texts
└── v0.0.1
├── POST_ADMINISTRAR_SISTEMAS_COM_DOCKER.md
├── POST_CONTRIBUICAO_OPEN_SOURCE.md
├── POST_CSS_GRID.md
├── POST_INTRODUCAO_CIENCIA_DE_DADOS.md
├── POST_RETROSPECTIVA_VISU_ANALOGICAS.md
├── POST_SPOTIFY_WEB_SDK.md
├── POST_TESTS_JS.md
└── POST_WEB_CRAWLERS.md
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: ['https://donorbox.org/opendevufcg']
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/novo-post.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Novo post
3 | about: Utilize quando quiser propor um tema a ser desenvolvido em um post.
4 | title: "[NOVO]"
5 | labels: 'novo post'
6 | assignees: JuanBarros2, JRobsonJr
7 | projects: Geral
8 |
9 | ---
10 |
11 | Por favor, utilize o seguinte template para sugerir um novo post:
12 |
13 | Descrição: ** descreva o tema que você acredita ser relevante para a comunidade. **
14 |
15 | - [x] Me interesso por escrever um post sobre o tema acima. Estou ciente que esse post
16 | será publicado na organização da OpenDevUFCG na plataforma [dev.to](https://dev.to/), me
17 | creditando a autoria pelo conteúdo criado nesse post e ficará disponível no presente
18 | repositório. Também estou ciente que a organização publicará posts propagando minha
19 | publicação em redes sociais, dando visibilidade ao meu post.
20 |
21 | Fique atento ao [nosso guia de contribuição](https://github.com/OpenDevUFCG/OpenDevUFCGBlog/blob/master/CONTRIBUTING.md) para guiar a produção do seu artigo.
22 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | Todas as mudanças notavéis desse projeto serão documentadas nesse arquivo.
4 |
5 | O formato é baseado no [Mantenha um Changelog](https://keepachangelog.com/pt-BR/1.0.0/),
6 | e nós aderimos ao sistema de versionamento baseado em milestone.
7 |
8 | ## [Não publicado]
9 |
10 | Data: 03-08-2019
11 | Título: Contribuindo para projetos open source com GitHub
12 | Descrição: Uma porta de entrada para pessoas que planejam começar a fazer contribuições para organizações open source.
13 | Tags: opensource, comunidade, github, ptbr.
14 | Issue: #4
15 |
16 | ## [0.1.0] - 03-08-2019
17 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Guia de postagens no dev.to
3 | published: false
4 | description: Oi, lindx. Vem sempre aqui?
5 | tags: opensource
6 | ---
7 |
8 | # Olá, queridx colega de curso!
9 |
10 | Obrigado por querer contribuir com o blog do OpenDevUFCG. Nós decidimos adotar a plataforma **dev.to** para hospedar o nosso conteúdo. Caso você ainda não tenha tido contato com esse ambiente, vale a pena dar uma olhada nas postagens de outras pessoas. Em geral, o dev.to é uma comunidade focada em promover a divulgação de ideias, dúvidas e conhecimento entre desenvolvedores. Dessa forma, o propósito desse portal está bem alinhado com o do OpenDevUFCG!
11 | Se quiser visualizar esse guia no dev.to, acesse [o seguinte link](https://dev.to/opendevufcg/guia-de-postagens-no-dev-to-3c1b-temp-slug-6645055?preview=425fc609e9fa6bbd74139294f6c7220e91b6e75c9f38c54d0549d7886b8537a20d36d21bb00ccb2de68aaaa789a28f37e13a48542d6fab81ef7ddb0c).
12 |
13 | # Primeiras orientações
14 |
15 | Se essa é a primeira vez que você está contribuindo conosco, alguns poucos passos são necessários para que você obtenha permissão para fazer publicações.
16 |
17 | 1. Crie uma conta no [dev.to](https://dev.to);
18 | 2. Acesse **Settings** > **Organization**;
19 | 3. Em **New Organization**, insira a chave secreta que iremos te passar para que você entre na organização. Clique em **Join Organization** e agora estará tudo certo!
20 | 4. Para começar a escrever o seu texto, clique em **Write a Post** e então no campo **Publish under an organization** selecione **OpenDevUFCG**.
21 |
22 | # Planejamento
23 |
24 | Teremos publicações semanais e, se você está lendo isso, provavelmente a sua está vindo em breve! Iremos determinar no planejamento a melhor data para a sua publicação de acordo com as que já estão agendadas. Quando chegar a semana da sua, o processo funciona da seguinte forma:
25 |
26 | - até **quinta-feira** esperamos receber o seu texto para que possamos revisá-lo;
27 | - até **sexta-feira** iremos dar um feedback em relação ao texto, sugerir ajustes (se for necessário);
28 | - e o **sábado** é o dia da publicação e divulgação nas nossas redes sociais.
29 |
30 | Por enquanto, **ainda** não prevemos estar fazendo publicações fora das datas planejadas. Isso não te impede de fazer sugestões ou já ir pensando em textos previamente!
31 |
32 | # Escrevendo no dev.to
33 |
34 | ## O editor do dev.to
35 |
36 | É muito provável que você já tenha trabalhado com ou ao menos visualizado Markdown, uma linguagem de marcação reconhecida por sua simplicidade de leitura e escrita. O README.md, por exemplo, é um arquivo presente na maioria dos repositórios de sistemas de controle de versão como o Git, explicando um pouco de como tudo funciona na comunidade relacionada a um projeto.
37 |
38 | O editor de postagens do dev.to dispõe de várias dicas de Markdown que podem ser acessadas clicando no botão **?**. Tudo deve ser simples de entender, mas se houver dúvida para inserir alguma coisa, não hesitem em nos procurar!
39 |
40 | O editor do dev.to também tem suporte a várias liquid tags, que facilitam bastante a adição de conteúdo incorporado na publicação. Dê uma olhada no [link para esse guia no dev.to](https://dev.to/opendevufcg/guia-de-postagens-no-dev-to-3c1b-temp-slug-6645055?preview=425fc609e9fa6bbd74139294f6c7220e91b6e75c9f38c54d0549d7886b8537a20d36d21bb00ccb2de68aaaa789a28f37e13a48542d6fab81ef7ddb0c) para ver como `{% github OpenDevUFCG/OpenDevUFCGBlog %}` é renderizado.
41 |
42 | Liquid tags semelhantes podem ser usadas para incorporar mídia de diversas outras plataformas, como YouTube, Twitter e Spotify. Não deixe de consultar a lista e como utilizá-las (também no botão **?** do editor de postagens).
43 |
44 | ## Variáveis e valores
45 |
46 | No topo de uma postagem do dev.to existe um campo dedicado à determinação dos valores atribuídos a algumas variáveis do artigo a ser publicado. A seguir, uma breve descrição de como preencher esses valores:
47 |
48 | **title:** Título do seu artigo (por exemplo, "Uma breve introdução a Redux").
49 | **published:** Valor booleano que determina se o texto estará como publicado (ou seja, visível a todos). É bem útil para que você só modifique o valor para "true" quando for hora de realmente publicar a postagem. Até lá, o artigo fica como rascunho e pode ser ajustado à vontade.
50 | **description:** Descrição da sua postagem. É como uma expansão do título; explique como o seu texto leva ao objetivo apresentado pelo título ou faça algum comentário que chame a atenção do leitor.
51 | **tags:** Sempre coloque a tag "ptbr". As demais devem ser decididas de acordo com o seu conteúdo; dê uma olhada em tags populares para determinar quais usar (o máximo é 4). Como o dev.to ainda é predominantemente em inglês, sempre use as tags em inglês. Um exemplo: "react, redux, javascript, ptbr".
52 | **canonical_url (opcional):** Se o seu texto foi publicado originalmente em algum outro blog pessoal, você pode colocar a URL do conteúdo original aqui.
53 | **cover_image (opcional):** Uma capa para a publicação. Em geral, não é necessário colocar uma capa na sua postagem, mas se você tiver uma imagem interessante e relacionada, sempre use algo de tamanho 1000 x 420.
54 | **series (opcional):** Sua publicação vai ser parte de uma série? Se sim, basta usar sempre o mesmo texto nesse campo que será adicionado um navegador às demais postagens da série ([um exemplo de como fica na prática aqui](https://dev.to/azure/learning-python-from-scratch-getting-started-windows-40e9)).
55 |
56 | # Hora de produzir!
57 |
58 | Algumas últimas orientações: quando for escrever sua postagem, tente manter o tempo de leitura (que pode ser visto clicando no botão "Preview") em uma faixa de 5 a 10 minutos. Queremos ter leituras leves e que introduzam e apeteçam a curiosidade dos leitores quanto aos conteúdos apresentados, então não há necessidade de ir muito a fundo e se estender demais. Teremos espaço para séries de postagens no futuro, o que vai permitir dividir o que seria uma postagem muito longa em um conjunto de postagens que façam sentido por conta própria.
59 |
60 | ## Terminando a sua postagem
61 |
62 | Para que ninguém tenha que se preocupar com fazer o encerramento da postagem (e para não perder a oportunidade de divulgar as redes sociais do OpenDev), encerre a postagem com o seguinte:
63 |
64 | ```
65 | Muito obrigado pela leitura! Fique atento: em breve, teremos novos artigos de contribuidores do OpenDevUFCG aqui no **dev.to**. Acompanhe o OpenDevUFCG no [Twitter](https://twitter.com/OpenDevUFCG), no [Instagram](https://instagram.com/OpenDevUFCG) e, claro, no [GitHub](https://github.com/OpenDevUFCG).
66 | ```
67 |
68 | ## <3
69 |
70 | Se houver quaisquer dúvidas ao longo do processo, também estamos sempre aqui para ajudar. <3
71 |
72 | Agradecemos pela sua disponibilidade e desejamos uma ótima experiência compartilhando conhecimento com os demais colegas de curso!
73 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 OpenDevUFCG
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
9 | Tem algum conteúdo que você quer compartilhar com a comunidade? Fala com o OpenDevUFCG que a gente te impulsiona
10 |
11 |
12 |
13 |
14 | ## Publicações
15 | | |Autor |Data |
16 | |--------------------------------|:-----------------------------:|:---------------------------:|
17 | |[Projetos open source que apoiamos no Hacktoberfest 2019](https://dev.to/opendevufcg/projetos-open-source-que-apoiamos-no-hacktoberfest-2019-34be) |[Robson Junior](https://dev.to/jrobsonjr) | 5 de Outubro de 2019 |
18 | |[Cloud e OpenStack: Uma breve introdução](https://dev.to/opendevufcg/cloud-e-openstack-uma-breve-introducao-49cb) |[Marta Laís](https://dev.to/martalais) | 21 de Setembro de 2019 |
19 | |[Usando o spotify-web-sdk para gerenciar requisições à API Web do Spotify](https://dev.to/opendevufcg/usando-o-spotify-web-sdk-para-gerenciar-requisicoes-a-api-web-do-spotify-3j3i) |[Robson Junior](https://dev.to/jrobsonjr) | 15 de Setembro de 2019 |
20 | |[Testes em JavaScript: Conceitos iniciais](https://dev.to/opendevufcg/testes-em-javascript-conceitos-iniciais-1okj) |[Júlio Guedes](https://dev.to/juliobguedes) | 7 de Setembro de 2019 |
21 | |[Usando Scrapy para obter metadados das músicas dos Parcels através do Genius](https://dev.to/opendevufcg/usando-scrapy-para-obter-metadados-das-musicas-dos-parcels-atraves-do-genius-1dhj) |[Fanny](https://dev.to/fannyvieira) | 31 de Agosto de 2019 |
22 | |[Administrando Sistemas com Docker](https://dev.to/opendevufcg/administrando-sistemas-com-docker-4pgm) |[Victor Hugo](https://dev.to/victorhundo) | 24 de Agosto de 2019 |
23 | |[Criando malhas simples com CSS Grid](https://dev.to/opendevufcg/criando-malhas-simples-com-css-grid-3kd2) |[Pedro Espíndula](https://dev.to/pedroespindula) | 17 de Agosto de 2019 |
24 | |[Introdução à Ciência de Dados](https://dev.to/opendevufcg/introducao-a-ciencia-de-dados-n4c) | [Hadrizia Santos](https://dev.to/hadrizia) | 10 de Agosto de 2019 |
25 | |[Contribuindo para projetos open source com GitHub](https://dev.to/opendevufcg/contribuindo-para-projetos-open-source-com-github-3i76) |[Lucas de Medeiros](https://dev.to/lukehxh) | 3 de Agosto de 2019|
26 |
27 | ## Como contribuir
28 | Quer contribuir? Veja como no [`CONTRIBUTING.md`](https://github.com/OpenDevUFCG/OpenDevUFCGBlog/blob/master/CONTRIBUTING.md).
29 |
--------------------------------------------------------------------------------
/docs/img/gif_readme/blog.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenDevUFCG/OpenDevUFCGBlog/b834e780f9e6eaeae628c7d6fd3a56afa6c26598/docs/img/gif_readme/blog.gif
--------------------------------------------------------------------------------
/docs/img/logo/blog_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenDevUFCG/OpenDevUFCGBlog/b834e780f9e6eaeae628c7d6fd3a56afa6c26598/docs/img/logo/blog_logo.png
--------------------------------------------------------------------------------
/docs/img/logo/machine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenDevUFCG/OpenDevUFCGBlog/b834e780f9e6eaeae628c7d6fd3a56afa6c26598/docs/img/logo/machine.png
--------------------------------------------------------------------------------
/docs/img/logo/name_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenDevUFCG/OpenDevUFCGBlog/b834e780f9e6eaeae628c7d6fd3a56afa6c26598/docs/img/logo/name_logo.png
--------------------------------------------------------------------------------
/docs/img/logo/opendev_logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/texts/v0.0.1/POST_ADMINISTRAR_SISTEMAS_COM_DOCKER.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Administrando Sistemas com Docker
3 | published: false
4 | description: Conceitos básicos dessa tecnologia partindo de uma perspectiva da administração de sistemas.
5 | tags: docker, sysadmin, gitlab.
6 | cover_image: https://thepracticaldev.s3.amazonaws.com/i/b16v56drk48gkq5092ve.png
7 | ---
8 |
9 | O uso de containers na virtualização e isolamento de aplicações está cada vez mais em alta nos projetos de código aberto e o Docker se destaca como a principal tecnologia do mercado, tendo sido recentemente classificada como a plataforma mais buscada no Stack Overflow (e a segunda mais amada, perdendo apenas para o Linux). A seguir, falaremos de conceitos básicos dessa tecnologia partindo de uma perspectiva da administração de sistemas.
10 |
11 | ## Administrar Sistemas
12 |
13 | Algumas funções que um administrador de sistemas é encarregado de exercer são de instalar, suportar e fazer manutenção de servidores e, como o nome já sugere, sistemas em geral. Isso significa que, em uma equipe de desenvolvimento, as plataformas que são utilizadas pelos desenvolvedores devem ser fornecidas por profissionais dessa área, como sistemas de controle de versão, de revisão de código, controle de processo e diversos outros sistemas criados especialmente para o ambiente de desenvolvimento.
14 |
15 | Dependendo da necessidade da equipe de desenvolvimento, as escolhas das tecnologias podem ocasionar conflitos entre si, como sistemas que usam a mesma linguagem de programação em versões distintas ou sistemas legados que utilizam tecnologias que não recebem mais suporte. Esses problemas crescem exponencialmente quando todos eles estão rodando em uma única máquina física.
16 |
17 | Conseguir separar recursos computacionais (memória, espaço em disco e processamento), além de ter um isolamento completo de cada sistema, é o ideal para o administrador de sistemas e é por isso que o Docker, aliado aos seus containers, se torna o mais novo parceiro para essa área.
18 |
19 | ## Hello Docker World!
20 |
21 | 
22 |
23 | Agora que entendemos a importância do Docker para a instalação e manutenção de sistemas, vamos falar de alguns conceitos básicos dessa tecnologia e recomendamos que você tenha o Docker instalado na sua máquina para darmos prosseguimento com esse texto. Caso não tenha instalado, recomendamos que leia a [documentação de como fazer isso](https://docs.docker.com/install/).
24 |
25 |
26 |
27 | Para verificar que seu ambiente está configurado de forma correta, basta executar o seguinte comando abaixo. Se aparecer a mensagem “Hello from Docker!”, tudo ocorreu bem:
28 |
29 | ```
30 | sudo docker run hello-world
31 | ```
32 |
33 | 
34 |
35 | Ao executar esse comando, nós chamamos o binário do Docker, criamos um container a partir da opção `run` e dizemos que o container que será criado é da imagem `hello-world:latest`. Uma imagem Docker é um container pré-configurado em que será utilizado como referência para **criar containers**. As imagens são baixadas do Docker Hub; no nosso caso, utilizamos a imagem [hello-world](https://hub.docker.com/_/hello-world)
36 |
37 | ## Subindo um sistema com Docker
38 |
39 | Agora que temos o Docker instalado e tivemos uma breve ideia de como criar um container, vamos criar algo mais útil para um ambiente de desenvolvimento: um container do GitLab!
40 |
41 | Para isso, execute:
42 | ```
43 | sudo docker run \
44 | -p 8080:80 \
45 | -p 222:22 \
46 | --name gitlab \
47 | -v /srv/gitlab/config:/etc/gitlab \
48 | -v /srv/gitlab/logs:/var/log/gitlab \
49 | -v /srv/gitlab/data:/var/opt/gitlab \
50 | gitlab/gitlab-ce:latest
51 | ```
52 | Utilizamos o parâmetro **name** para que possamos identificar com mais facilidade o container quando ele for listado através do comando `docker ps`. A saída desse comando será algo como:
53 |
54 | ```
55 | CONTAINER ID IMAGE COMMAND CREATED NAMES NAMES
56 |
57 | 80e2ce68a5bf gitlab/gitlab-ce:latest "/assets/wrapper" 5 weeks ago 0.0.0.0:222->22/tcp, 0.0.0.0:8080->80/tcp, 443/tcp gitlab
58 | ```
59 | Além do nome do container e a partir de qual imagem ele será criado, também especificamos parâmetros de **volume** (parâmetro -v) e **porta** (parâmetro -p), que são fundamentais para a administração de qualquer sistema e que explicaremos com mais detalhes a seguir.
60 |
61 | ### Volumes
62 |
63 | Volumes são a forma com que o Docker gerencia o sistema de arquivos, fazendo ligação da virtualização dentro do container com os diretórios da máquina real. Os containers foram projetados para ser efêmeros: caso existam dados que precisem ser salvos ou lidos/escritos é importante utilizar volumes; caso contrário, os dados serão perdidos com a destruição do container.
64 |
65 | Existem dois tipos de volumes: "com bind" e "sem bind". Volumes com bind são utilizados quando você quer especificar um caminho na sua máquina que será compartilhado com o container, enquanto nos volumes sem bind a especificação do endereço é feita pelo próprio Docker.
66 |
67 | A criação do volume pode ser feito na criação do container utilizando o parâmetro `-v`:
68 |
69 | ```
70 | # Exemplo de criação de volume com bind
71 | -v /home/user/container_data:/opt/app
72 | ```
73 |
74 | ```
75 | # Exemplo de criação de volume sem bind
76 | -v /opt/app (volume sem bind)
77 | ```
78 |
79 | Normalmente, volumes com bind são utilizados quando é **preciso fazer modificações** nos dados frequentemente (ao alterar os dados na pasta da sua máquina real em que está sendo feito o bind, os dados dentro do container também serão alterados). **Arquivos de configuração ou códigos de desenvolvimento são exemplos de bons usos desse tipo de volume.**
80 |
81 | Já os volumes sem bind geralmente são utilizados quando queremos **apenas salvar as informações** e queremos apenas ler esses dados. **Banco de dados** é um exemplo que pode utilizar esse tipo de volume.
82 |
83 | No nosso caso, utilizamos o parâmetro de volume três vezes, para mapear e separar arquivos de configuração, arquivos de log e os dados do GitLab:
84 |
85 | ```
86 | -v /srv/gitlab/config:/etc/gitlab \
87 | -v /srv/gitlab/logs:/var/log/gitlab \
88 | -v /srv/gitlab/data:/var/opt/gitlab \
89 | ```
90 |
91 | O diretório da esquerda, por exemplo, `/src/gitlab/config`, se refere ao diretório da máquina real, enquanto o da direita é o do container. Os arquivos alterados nesse diretório da máquina real também serão alterados dentro do container (e vice-versa).
92 |
93 | Além de facilitar a configuração e personalização dos sistemas, utilizar volumes também facilita na criação de backups ou na disponibilidade, já que ao subir um container com os dados que estão nos diretórios mapeados em outra máquina fará com que você tenha uma redundância do sistema.
94 |
95 | ### Portas
96 |
97 | Uma das coisas mais poderosas do Docker é a sua gerência de rede. É possível [rotear todo o tráfego de rede do container através do Tor](https://github.com/jessfraz/onion), por exemplo, mas vamos nos conter em apenas abordar o redirecionamento de portas que é feito utilizando o **parâmetro -p**.
98 |
99 | Serviços em redes são acessados através de portas, como páginas http (80), servidores de email (25) e login remoto (22) e precisam especificar, além do endereço IP, a porta de comunicação. O GitLab utiliza tanto uma interface web na porta 80, quanto pode-se acessá-lo remotamente com ssh na porta 22, porém é muito provável que outras aplicações já utilizem essas mesmas portas para outras aplicações na máquina real. Por isso, foram feitos os seguintes mapeamentos:
100 |
101 | ```
102 | -p 8080:80 \
103 | -p 222:22 \
104 |
105 | ```
106 |
107 | Ou seja, ao acessar a porta 8080 na máquina real, o Docker irá redirecionar para a porta 80 dentro do container, na qual está rodando o serviço web do GitLab. Da mesma forma é feito o mapeamento da porta 222 para a 22.
108 |
109 | Acessando http://localhost:8080, você terá acesso ao seu GitLab.
110 |
111 | Usando esse parâmetro do Docker com [proxy Apache](https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html), por exemplo, é possível servir diversos serviços com redirecionamento para aplicações completamente isoladas através de containers.
112 |
113 | 
114 |
115 |
116 |
117 | ## Considerações Finais
118 |
119 |
120 | Resolver problemas de conflitos, fazer cópia do sistema para realizar testes, diminuir tempo de upgrade/manutenção e até melhorar a organização dos arquivos de configurações são vantagens que o uso do Docker pode trazer para o administrador de sistemas.
121 |
122 | Com uma breve explicação foi possível subir uma poderosa aplicação como o GitLab. Encorajamos que você se aprofunde nesse mundo de containers e, para isso, recomendamos:
123 |
124 | 1. [Documentação Docker](https://docs.docker.com/);
125 | 2. [Documentação da Imagem Docker do GitLab](https://docs.gitlab.com/omnibus/docker/);
126 | 3. [Jessie Frazelle (referência no mundo Docker)](https://github.com/jessfraz/dockerfiles);
127 | 4. [Canal do YouTube LINUXtips](https://www.youtube.com/watch?v=0cDj7citEjE);
128 | 5. [Oficina de Docker no GitHub](https://github.com/victorhundo/docker-guide).
129 |
130 | Muito obrigado pela leitura! Fique atento: em breve, teremos novos artigos de contribuidores do OpenDevUFCG aqui no **dev.to**. Acompanhe o OpenDevUFCG no [Twitter](https://twitter.com/OpenDevUFCG), no [Instagram](https://instagram.com/OpenDevUFCG) e, claro, no [GitHub](https://github.com/OpenDevUFCG).
131 |
--------------------------------------------------------------------------------
/texts/v0.0.1/POST_CONTRIBUICAO_OPEN_SOURCE.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Contribuindo para projetos open source com GitHub
3 | published: false
4 | description: Uma porta de entrada para pessoas que planejam começar a fazer contribuições para organizações open source.
5 | tags: opensource, community, github, ptbr.
6 | cover_image: https://thepracticaldev.s3.amazonaws.com/i/16tq00lwa6aq3wak8jsf.jpg
7 | ---
8 |
9 | Criado em 2008 e muito aperfeiçoado desde então, o GitHub não apenas é, atualmente, o mais conhecido serviço web que oferece diversas funcionalidades aplicadas ao Git para hospedagem e versionamento de código-fonte, como também é um grande impulsionador da iniciativa open source e do trabalho em equipe. Cada vez mais, a plataforma tem adquirido uma cara de rede social, visando engajar mais pessoas em relação a contribuição em projetos de código aberto, participação em suas respectivas comunidades, além de dar mais visibilidade a tais projetos. Isso foi possível devido a inúmeras funcionalidades adotadas e implementadas pelo GitHub a fim de atingir esses objetivos, e algumas delas serão introduzidas nesse post.
10 |
11 | 
12 | *Screenshot da página inicial do GitHub atualmente*.
13 |
14 | ## Comunidade open source
15 |
16 | Antes de prosseguir no post, é preciso deixar claro um conceito para comunidade open source. Uma comunidade é formada por **Users**, **Contributors** e **Mantainers**. O grupo de usuários (*users*) é composto por pessoas que vão utilizar um sistema que está sendo desenvolvido em código aberto, e são ativas na comunidade reportando possíveis bugs no desenvolvimento e dando sugestões de melhoria e de novas *features*. Além disso, dependendo da licença utilizada pelo projeto, usuários podem fazer modificações livremente, por exemplo, alterar partes do sistema para uso pessoal, utilização como componente em outro projeto e até comercializá-lo. Para saber o que pode e o que não pode fazer, é sempre bom checar o arquivo **LICENSE** do repositório e verificar as permissões.
17 |
18 | O que diferencia um usuário de um contribuidor (*contributor*) é a efetividade para a organização que encabeça o projeto. O contribuidor levanta discussões mais profundas e úteis para a organização, altera diretamente o código e faz requisições de anexação do seu conteúdo ao original. Os motivos que levam alguém a contribuir com uma organização podem ser diversos: o desejo de melhorar aplicações que ele mesmo utiliza, identificação com a organização, aplicação prática de conhecimentos acerca de uma tecnologia, ganhar visibilidade com contribuições úteis e reais, entre outros. Esse é o espírito do open source.
19 |
20 | Vale ressaltar dois pontos: primeiro, o contribuidor não está vinculado à organização. Isso significa que alguém pode aparecer repentinamente para contribuir e, da mesma forma, pode parar a qualquer momento. Segundo, é muito comum que a organização estabeleça regras para contribuição e é importantíssimo que o contribuidor esteja por dentro de todas elas antes de começar. Por isso, ele deve ler bem o que há nos arquivos **README.md**, **CONTRIBUTING.md** e **CODE_OF_CONDUCT.md**, caso existam.
21 |
22 | Por fim, temos os mantenedores (*maintainers*). Estes são os administradores responsáveis por manter o projeto "vivo" e atualizado, pois eles são membros **efetivos** da organização, e têm privilégios de administrador no repositório do GitHub. Eles devem estar por dentro das discussões que envolvem o projeto, fazer correções de bugs, implementar features, revisar requisições de contribuidores e são os responsáveis por manter o projeto organizado: disposição dos arquivos e diretórios no repositório, alocação de *tasks*, categorização dos tópicos das discussões, entre outras obrigações.
23 |
24 | Agora, com o conceito de comunidades open source mais claro, podemos começar a falar sobre as maneiras que o GitHub encontrou para impulsioná-las e como você pode utilizar a plataforma para fazer parte de uma.
25 |
26 | ## Stars e forks
27 | 
28 |
29 | Ao acessar um repositório público no GitHub, você pode dar uma **star**, *feature* similar às "curtidas" das redes sociais mais famosas, e também pode fazer um **fork**, que consiste na criação de uma cópia independente deste repositório, que contém todo o seu conteúdo, para a sua conta de usuário no GitHub. Na tela inicial, são mostradas as **stars** e **forks** que as pessoas que você segue dão em repositórios públicos, uma funcionalidade que tem por objetivo a ampliação do alcance dos projetos open source.
30 |
31 | ## Pull Requests (PR's) e Issues
32 |
33 | A partir de um **fork**, quando terminar de fazer as alterações que julgar necessárias, você pode submeter uma requisição de anexação do seu conteúdo modificado ao conteúdo original do repositório escolhido. Essa requisição é a **pull request**, a forma mais característica de contribuição a um projeto na plataforma, que será revisada por um maintainer. Observe abaixo o processo para abrir uma nova PR:
34 |
35 | 
36 |
37 | Também é importante ter em mente que uma contribuição para um projeto open source vai além de modificações diretas no código-fonte. Um feedback, alerta de bugs no software que está sendo desenvolvido, discussões a respeito de um novo passo que pode ser tomado em prol do projeto, troca de ideias e sugestões podem impactar profundamente no futuro do projeto e até da organização. Por isso, é extremamente importante que uma organização fique sempre atenta ao que sua comunidade tem a dizer.
38 |
39 | Tendo em vista esse contexto, o GitHub disponibiliza as **issues**, um espaço que pode ser utilizado tanto por usuários comuns quanto por maintainers, para a finalidade de todos os pontos citados e também para priorização e organização das tarefas a serem desenvolvidas. Observe abaixo o processo para abrir uma nova issue:
40 |
41 | 
42 |
43 | ## Descobrindo repositórios e contribuindo
44 |
45 | É muito comum que não se saiba muito bem por onde começar a contribuir no início da "aventura" pelo mundo open source. Para minimizar essa dificuldade, você pode visualizar a seção **[discover repositories](https://github.com/discover)**, que traz recomendações muito interessantes de projetos, baseadas em outros repositórios que você deu star, em pessoas que você segue, em tecnologias recentes que você trabalhou e também em popularidade do projeto, os **trendings**.
46 |
47 | Depois de localizar um projeto com o qual se identificou, você pode explorar o arquivo **README.md** a fim de obter informações a respeito do que se trata o projeto, com quais tecnologias ele trabalha, como rodar na sua máquina, link da página principal, entre outras. É importante ter em mente que a disponibilidade dessas informações nesse arquivo depende bastante da organização que criou o projeto, e como ela organiza suas atividades.
48 |
49 | Depois de se informar sobre esse projeto e decidir que quer contribuir para ele, um ótimo ponto de partida é olhar as issues abertas e verificar as suas **labels**, que consistem em uma forma de categorização e priorização de issues e PR's para ajudar na organização do trabalho. A fim de te deixar mais confortável para começar a contribuir, é interessante que a procura inicial seja por labels de **good first issue**, **easy** e **help wanted**, por exemplo. Vale ressaltar também que essa categorização de issues e PR's por labels também depende de como a organização "se organiza".
50 |
51 | 
52 | *Screenshot da página de issues do [Tamburetei](https://github.com/OpenDevUFCG/Tamburetei) com suas labels.*
53 |
54 | Você pode contribuir com novas ideias nas discussões a respeito de alguma das issues ou fazer uma PR que a resolva. Posteriormente, quando estiver mais à vontade na organização, você pode abrir novas issues ou tentar resolver algumas mais complexas.
55 |
56 | ## Explorando o GitHub
57 |
58 | Outra forma de ter acesso a novidades de projetos open source é acessando o **[Explore](https://github.com/explore)**, uma página do GitHub que além de mostrar projetos recomendados, como mostrado no tópico anterior, também possibilita uma conexão entre as comunidades desses projetos. Você pode explorar conteúdos de curadores, buscar projetos por tópicos e labels, ter acesso a informações de posts, artigos, eventos e meetups relacionados com as tecnologias recomendadas para você, entre outras informações.
59 |
60 | 
61 |
62 | Essas são algumas das principais maneiras que o GitHub encontrou para dar visibilidade a projetos de código aberto e fomentar a colaboração dos desenvolvedores para eles. Você pode se aprofundar mais a respeito do funcionamento do Git e do GitHub e obter informações mais técnicas [neste outro post](https://medium.com/@Juliobguedes/entendendo-git-883464f379de), e começar também a apoiar o open source agora mesmo!
63 |
64 | Muito obrigado pela leitura, fique atento a mais postagens da OpenDev UFCG aqui no Dev.to! Esperamos encontrá-lo por aqui novamente. :smile:
65 |
--------------------------------------------------------------------------------
/texts/v0.0.1/POST_CSS_GRID.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Criando malhas simples com CSS Grid
3 | published: false
4 | description: Introdução a CSS grid
5 | tags: css, grid, ptbr, tips
6 | ---
7 |
8 |
9 | # Grid para ser feliz
10 |
11 | Você conhece o CSS Grid? Se não, você está perdendo uma ótima ferramenta do CSS que facilita tanto o desenvolvimento como também a manutenção de código. Nesse post, irei tratar um pouco de seu funcionamento e de como ele pode ser poderoso.
12 |
13 | # Um pouco sobre caixas
14 |
15 | Pra começar, você tem que abstrair que todo elemento HTML está envolvido por uma caixa que tanto o delimita como também o seu conteúdo (para saber mais, acesse [esse link](https://tableless.github.io/iniciantes/manual/css/box-model.html) e [esse link](https://developer.mozilla.org/pt-BR/docs/Web/CSS/box_model)). Na maioria das vezes, vão existir elementos que têm dentro do seu conteúdo outros elementos HTML, ou seja, caixas dentro de caixas, e o que o Grid faz é simplesmente organizar essas caixas de acordo com sua vontade. Como ele faz isso? Ele define uma malha quadriculada e atribui espaços dessa malha a cada elemento HTML.
16 |
17 | 
18 | Fonte: SparkBox
19 |
20 | A malha é definida por trackers, que são basicamente delimitadores das linhas e colunas, representados por esses marcadores brancos:
21 |
22 | 
23 | Fonte: SparkBox
24 |
25 | A linha é a área entre um tracker e outro de forma vertical, ou seja, cada novo espaço na altura da malha. Já a coluna é a área entre um tracker e outro de forma horizontal, ou seja, cada novo espaço no comprimento da malha.
26 |
27 | As linhas e colunas são geralmente medidas em `fr`. Essa medida foi criada exclusivamente para o CSS Grid e pode ser definida como "uma fração do espaço disponível no container do grid" (fonte: [MDN](https://developer.mozilla.org/pt-BR/docs/Web/CSS/CSS_Grid_Layout/Basic_Concepts_of_Grid_Layout)). No caso da imagem acima, as colunas têm a medida de `5fr` e as linhas de `3fr`. E sabe o melhor do `fr`? Ele se adequa ao tamanho da sua tela.
28 |
29 | Além disso, cada caixa pintada da imagem é uma área do grid onde ficará um elemento HTML. Mas calma que eu vou explicar o porquê isso é importante.
30 |
31 | # Colocando em prática
32 |
33 | Só pra você saber, os exemplos nesse post vão seguir uma estrutura onde mostrarei sempre um código CSS e uma aplicação desse código num codepen. Além disso, existirá um fluxo no qual cada código será usado como base para os códigos subsequentes.
34 |
35 | ## Preparando o terreno
36 |
37 | O primeiro passo para utilizar o grid é definir quais são os elementos que estamos utilizando e o que cada um contém. Isso é definido no HTML. Nesse caso, temos uma caixa grande que tem seis caixas pequenas dentro dela:
38 |
39 | ```html
40 |
41 |
Caixa pequena 1
42 |
Caixa pequena 2
43 |
Caixa pequena 3
44 |
Caixa pequena 4
45 |
Caixa pequena 5
46 |
Caixa pequena 6
47 |
48 | ```
49 |
50 | Para facilitar o entendimento, pode-se usar tags HTML personalizadas. Segue o exemplo de como o código acima poderia ficar:
51 |
52 | ```html
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | ```
62 |
63 | Para começar a utilização do grid, você precisa apenas definir a propriedade `display: grid` no elemento que desejar. No caso iremos utilizar essa propriedade na `caixa-grande`, já que queremos organizar o que tem dentro dela:
64 |
65 | ```css
66 | caixa-grande {
67 | display: grid;
68 | }
69 |
70 | ```
71 |
72 | {% codepen https://codepen.io/pedroespindula/pen/mNQYGQ %}
73 |
74 | Facil, né? Mas calma, ainda não temos nada pronto. O que foi definido nesse CSS foi apenas o tipo de organização (`display`) que a caixa utilizará, que é o grid. Nesse caso, estamos dizendo ao elemento apenas isso: "Use o grid pra organizar o que tem dentro de você. Não me importa como você vai fazer isso, apenas use o grid.".
75 |
76 | # Entendendo como organizar
77 |
78 | ## Organização básica
79 |
80 | Você deve ter notado que a malha foi definida de forma automática onde cada caixa menor foi colocada em uma linha da malha, ou seja, um abaixo do outro. Implicitamente, ao definir o `display: grid`, você define algumas outras propriedades padrões do grid ao elemento. A propriedade responsável por organizar o grid desse modo é a [grid-auto-flow](https://developer.mozilla.org/pt-BR/docs/Web/CSS/grid-auto-flow), que é definida de forma padrão como `grid-auto-flow: row`. Ou seja, o fluxo automático de organização do grid está definido como linha. Assim, a cada novo filho adicionado, ele vai ser posto numa nova linha.
81 |
82 | Agora imagine que queiramos organizar os elementos em colunas ao invés de linhas, ou seja, um ao lado do outro ao invés de um abaixo do outro. Basta modificar essa propriedade da seguinte forma:
83 |
84 | ```css
85 | caixa-grande {
86 | display: grid;
87 | grid-auto-flow: column;
88 | }
89 | ```
90 | {% codepen https://codepen.io/pedroespindula/pen/qeLPzx %}
91 |
92 | Nesse exemplo estamos dizendo literalmente ao elemento "Use o grid, mas organize essas caixas em colunas".
93 |
94 | ## Organização com grid-template
95 |
96 | No exemplo anterior deixamos o trabalho da definição da malha todo para o grid de forma automática. Mas e se quisermos definir uma malha personalizada? Para isso, precisamos definir o `grid-template` no elemento HTML de sua escolha. Nessa malha você vai definir três coisas: as colunas (columns), as linhas (rows) e as áreas (areas) para colocar o conteúdo da caixa que você escolher.
97 |
98 | Lembra da definição de linhas, colunas e áreas que eu dei lá em cima? Pronto, você vai fazer seguir esses conceitos para definir sua malha.
99 |
100 | ## Codificando
101 |
102 | Vamos para um exemplo concreto. Imagine que você não queira utilizar o `grid-auto-flow`, mas queira todas as caixas uma do lado da outra numa unica linha. Já que temos seis caixas pequenas, temos que definir seis colunas e uma linha, desse modo:
103 |
104 | ```css
105 | caixa-grande {
106 | display: grid;
107 | grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
108 | grid-template-rows: 1fr;
109 | }
110 | ```
111 |
112 | {% codepen https://codepen.io/pedroespindula/pen/MNLZEV %}
113 |
114 | Já que temos apenas uma linha, não precisamos defini-la explicitamente. Por isso, para facilitar, a definição pode ser feita desse modo:
115 |
116 | ```css
117 | caixa-grande {
118 | display: grid;
119 | grid-template-columns: repeat(6, 1fr);
120 | }
121 | ```
122 |
123 | {% codepen https://codepen.io/pedroespindula/pen/VogNKJ %}
124 |
125 | Além da omissão do `grid-template-rows`, utilizamos a função `repeat`. O `repeat` é utilizado para repetir várias vezes a quantidade de medida passada. Nesse caso, repetimos seis vezes a medida de `1fr`, já que todas as colunas teriam o mesmo tamanho de 1fr. O repeat é uma função muito poderosa e será abordada de forma mais aprofundada em outro post.
126 |
127 |
128 | Agora, e se quisermos uma malha 3x2? Continua muito simples:
129 |
130 | ```css
131 | caixa-grande {
132 | display: grid;
133 | grid-template-columns: repeat(3, 1fr);
134 | grid-template-rows: repeat(2, 1fr);
135 | }
136 | ```
137 |
138 | {% codepen https://codepen.io/pedroespindula/pen/YzKwNBj %}
139 |
140 | Em todos esses casos, tanto as colunas como as linhas estão com tamanhos iguais, mas podemos definir com tamanhos diferentes também. Por exemplo:
141 |
142 | ```css
143 | caixa-grande {
144 | display: grid;
145 | grid-template-columns: 2fr repeat(2, 1fr);
146 | grid-template-rows: repeat(2, 1fr);
147 | }
148 | ```
149 |
150 | {% codepen https://codepen.io/pedroespindula/pen/OJLMWee %}
151 |
152 | Agora a primeira coluna tem o dobro do tamanho das outras colunas. Simples, né?
153 |
154 | ## Usando áreas no grid
155 |
156 | Em nenhum desses exemplos utilizamos o `grid-template-areas`, no entanto, todos funcionaram bem. Funcionam justamente por causa da dedução do CSS Grid que mencionei anteriormente, pois ele atribui sequencialmente as propriedades da malha a cada filho.
157 |
158 | No entanto, caso queiramos alterar a ordem das caixas, precisamos alterar o HTML. Num exemplo simples como esse, basta trocar a ordem das linhas das caixas e cada conteúdo será alterado. No entanto, em projetos complexos, se torna inviável alterar o HTML. Para a nossa sorte existe o `grid-template-areas`.
159 |
160 | Nele você define, literalmente, áreas que a malha terá. Como exemplo:
161 |
162 |
163 | ```css
164 | caixa-grande {
165 | display: grid;
166 | grid-template-columns: 2fr repeat(2, 1fr);
167 | grid-template-rows: repeat(2, 1fr);
168 | grid-template-areas:
169 | "c-pequena1 c-pequena2 c-pequena3"
170 | "c-pequena4 c-pequena5 c-pequena6";
171 | }
172 | ```
173 |
174 | O container pai está pronto, mas precisamos ainda definir dentro dos filhos a qual área cada um está atribuído, o que é feito desse modo:
175 |
176 | ```css
177 | caixa-pequena1 {
178 | grid-area: c-pequena1;
179 | }
180 |
181 | caixa-pequena2 {
182 | grid-area: c-pequena2;
183 | }
184 |
185 | caixa-pequena3 {
186 | grid-area: c-pequena3;
187 | }
188 |
189 | caixa-pequena4 {
190 | grid-area: c-pequena4;
191 | }
192 |
193 | caixa-pequena5 {
194 | grid-area: c-pequena5;
195 | }
196 |
197 | caixa-pequena6 {
198 | grid-area: c-pequena6;
199 | }
200 | ```
201 |
202 | {% codepen https://codepen.io/pedroespindula/pen/VwZePoG %}
203 |
204 | Só uma observação, a definição da propriedade do `grid-area` é sem aspas mesmo, se você colocar a área envolta de aspas ele não vai funcionar.
205 |
206 | Tudo pronto! Essa definição ficou igual ao exemplo anterior onde não tinhamos o `grid-template-areas`, mas imagine que queiramos trocar a posição da `caixa-pequena2` pela `caixa-pequena4`. Você precisa apenas alterar a ordem no `grid-template-areas`, modificando assim apenas o CSS sem mexer no HTML, desse modo:
207 |
208 |
209 | ```css
210 | caixa-grande {
211 | display: grid;
212 | grid-template-columns: 2fr repeat(2, 1fr);
213 | grid-template-rows: repeat(2, 1fr);
214 | grid-template-areas:
215 | "c-pequena1 c-pequena4 c-pequena3"
216 | "c-pequena2 c-pequena5 c-pequena6";
217 | }
218 | ```
219 |
220 | {% codepen https://codepen.io/pedroespindula/pen/YzKwZzR %}
221 |
222 | As áreas do grid não possibilitam somente a mudança fácil da posição de elementos HTML. A forma mais poderosa que ele pode ser usada é para a definição de malhas complexas. Uma coisa que não tínhamos feito antes é usar mais de uma vez uma área na definição do `grid-template-areas`, mas esse é o motivo principal de usarmos o grid.
223 |
224 | Para você entender melhor, imagine que temos agora que fazer uma página principal que tenha uma barra lateral, um conteúdo, um cabeçalho e um rodapé. O cabeçalho deve ficar sempre em cima, a barra lateral no lado esquerdo, o conteúdo principal no lado direito e o rodapé sempre em baixo da barra lateral. O conteúdo principal é três vezes maior que a barra lateral em largura e cinco vezes maior que o cabeçalho e o rodapé em altura.
225 |
226 | Parece complexo, né? Não é; isso fica fácil de organizar com grid e com grid-areas. Aconselho você a tentar fazer esse exemplo primeiro antes de ver o jeito que eu fiz. Já fez? Ótimo! Não conseguiu? Sem problemas, a gente tá aqui pra isso! Nem tentou? Tô de olho, hein!?
227 |
228 | Vamos à resolução: primeiro, definimos todos os elementos:
229 |
230 | ```html
231 |
232 |
233 |
234 |
235 |
236 |
237 | ```
238 |
239 | Depois o grid:
240 |
241 | ```css
242 | pagina-principal {
243 | display: grid;
244 | }
245 | ```
246 |
247 | Eu normalmente defino o `grid-template-areas` antes, pois assim tenho uma visualização melhor do que estou tentando fazer:
248 | ```css
249 | pagina-principal {
250 | display: grid;
251 | grid-template-areas:
252 | "cabecalho cabecalho"
253 | "barra-lateral conteudo-principal"
254 | "rodape conteudo-principal";
255 | }
256 | ```
257 |
258 | Depois, podemos atribuir cada área a um elemento filho:
259 |
260 | ```css
261 | cabecalho {
262 | grid-area: cabecalho;
263 | }
264 |
265 | barra-lateral {
266 | grid-area: barra-lateral;
267 | }
268 |
269 | conteudo-principal {
270 | grid-area: conteudo-principal;
271 | }
272 |
273 | rodape {
274 | grid-area: rodape;
275 | }
276 |
277 | ```
278 |
279 | Mas ainda falta definir os tamanhos que foram pedidos; pra isso, usamos a definição das linhas e das colunas. Como o conteúdo principal vai ser três vezes maior que a barra lateral, teremos uma coluna três vezes maior que a outra, e como o conteúdo principal vai ser quatro vezes maior que o cabeçalho e o rodapé, a linha do conteúdo principal vai ter que ser quatro vezes maior que as linhas do cabeçalho e do rodapé:
280 |
281 | ```css
282 | pagina-principal {
283 | display: grid;
284 | grid-template-columns: 1fr 3fr;
285 | grid-template-rows: 1fr 4fr 1fr;
286 | grid-template-areas:
287 | "cabecalho cabecalho"
288 | "barra-lateral conteudo-principal"
289 | "rodape conteudo-principal";
290 | }
291 | ```
292 |
293 | {% codepen https://codepen.io/pedroespindula/pen/gOYPmrN %}
294 |
295 | Pronto, temos a nossa página principal feita!
296 |
297 | # Hora de experimentar
298 |
299 | Agora que você já sabe um pouco de CSS Grid, brinque um pouco com essa propriedade e teste coisas que eu não expliquei nesse miniguia. Tá sem ideia? Então bora lá: Se eu tiver uma área não utilizada no grid, o que acontece? E se eu quiser colocar uma área vazia entre cada coluna e cada linha? E se eu não souber a quantidade de elementos filhos, como eu faço? E se eu não quiser nenhum espaço vazio? Tudo isso é possivel com o Grid, garanto a você.
300 |
301 | # É isto!
302 |
303 | Meu nome é Pedro e muito obrigado pela leitura! Sou contribuidor do OpenDevUFCG e desenvolvedor front-end. Se desejar entrar em contato comigo, é só me mandar uma mensagem no [Twitter](https://twitter.com/pedro_espindula) e se quiser ver um pouco do meu trabalho, dá uma olhada no meu [GitHub](https://github.com/pedro_espindula).
304 |
305 | Fique atento: em breve, teremos novos artigos de contribuidores do OpenDevUFCG aqui no **dev.to** (Inclusive meus). Acompanhe o OpenDevUFCG no [Twitter](https://twitter.com/OpenDevUFCG), no [Instagram](https://instagram.com/OpenDevUFCG) e, claro, no [GitHub](https://github.com/OpenDevUFCG).
306 |
307 |
--------------------------------------------------------------------------------
/texts/v0.0.1/POST_INTRODUCAO_CIENCIA_DE_DADOS.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introdução à Ciência de Dados
3 | published: false
4 | description: Este post traz uma introdução à Ciência de Dados, uma área em crescimento e bastante promissora. Vem comigo!
5 | tags: datascience, ptbr
6 | cover_image: https://thepracticaldev.s3.amazonaws.com/i/saia38h9cnyql3ggyd06.jpg
7 | ---
8 |
9 | # Introdução à Ciência de Dados
10 |
11 | Oi meninis, turo bom?
12 |
13 | Tem interesse em saber mais sobre ciência de dados? Veio ao lugar certo! Aqui explicarei o que é, suas principais áreas, aplicações bem bacanas e as disciplinas do curso que você pode cursar caso se interesse.
14 |
15 | ## 1. Ciência + Dados
16 |
17 | As definições de ciência e dados (ou *data science*) compõem perfeitamente uma definição para ciência de dados, e vou te explicar o porquê:
18 |
19 | A ciência pode ser descrita como *"o esforço para descobrir e aumentar o conhecimento humano de como o universo funciona"*.
20 |
21 | Já os dados *"são simples observações sobre o estado do mundo"*. Esse estado pode ser qualquer coisa: curtidas de uma foto no Facebook, o gênero de uma música, número de commits em um dia no GitHub, a quantidade de sanitários de uma escola, enfim... *qualquer coisa mesmo*.
22 |
23 | Então, um significado para ciência de dados poderia ser *o esforço para explorar, descobrir e extrair conhecimento sobre um assunto através de observações.*. Na prática, esse conhecimento geralmente se dá investigando, inferindo através de estatística e/ou tentando adivinhar uma "função mágica" que explique os dados a partir de aprendizagem de máquina (ou *machine learning*).
24 |
25 | ## 2. Principais atuações
26 |
27 | Agora que você já tem uma ideia do que é data science, veremos o que se pode ser feito neste ramo de computação conhecendo as suas principais áreas. Antes de começar, adianto que você pode (e provavelmente vai) trabalhar com mais uma área, pois elas estão bastante interligadas.
28 |
29 | A ciência de dados pode ser subdividida em duas seções: descritiva e a preditiva.
30 |
31 | ### 2.2. Ciência de Dados Descritiva
32 |
33 | A ciência descritiva compreende a área da exploração e investigação dos dados, geralmente com a finalidade de informar o público sobre um determinado assunto, `sempre sobre algo que já passou`.
34 |
35 | Para isso, a análise feita tipicamente parte de um conjunto de perguntas que o cientista busca responder através dos dados, conceitos de estatísticas e domínio sobre o assunto, geralmente na forma de gráficos bonitos e bem explicativos.
36 |
37 | Por exemplo, suponha que você queira fazer uma investigação sobre o gasto da cota parlamentar dos deputados da legislatura atual: quem gasta mais, em que, quais os gastos por estados, partidos, e por aí vai. Para responder a estas perguntas, você precisará agrupar e sumarizar os dados do uso da cota.
38 |
39 |
40 | **Aplicações tops:**
41 | * Todos os posts do [Nexo Gráfico](https://www.nexojornal.com.br/grafico/) são incríveis, com assuntos atuais (muitos sobre o Brasil) e gráficos lindos;
42 | * As publicações do [The Pudding](https://pudding.cool/) também são incríveis e servem muito de inspiração no quesito visualização da informação;
43 | * Os relatórios do nosso professor de ciência de dados descritiva Nazareno Andrade. Achei alguns feitos por ele [aqui](https://rpubs.com/nazareno);
44 |
45 |
46 | ### 2.3. Ciência de Dados Preditiva
47 |
48 | A ciência de dados preditiva usa os dados do passado para tentar `prever o futuro`. O cientista utiliza estatística, técnicas de aprendizagem de máquina (que é muito por cima uma mistura de álgebra linear, derivadas, integrais, somatórios, funções e outros temperos) para que o algoritmo descubra uma "função mágica" que ache um padrão nos dados conhecidos e os explique de forma a inferir um dado que não se conhece ainda.
49 |
50 | Por exemplo, suponha que você queira comprar uma casa de X quartos, no bairro Y, com ano de construção Z mas que não está à venda ainda, portanto não se sabe ainda o preço. Suponha também que você tem um conjunto dessas informações sobre outras casas que já foram vendidas, e que por isso possuem preço de venda. O cientista de dados preditivos usará algoritmos que irão aprender, a partir dos dados das vendas de casa passadas, quais são os critérios mais importantes e definitivos para a definição do preço de uma casa e desta forma estimar o preço da casa que você quer. Top, né? :D
51 |
52 | **Aplicações tops:**
53 |
54 | Esta é a área mais promissora da atualidade. Com a descoberta das redes neurais, os algoritmos são cada vez mais inteligentes e utilizados em diferentes áreas como medicina, arte, música, etc.
55 |
56 | * Detecção de câncer de pele usando Redes Neurais. ([link](https://medium.com/@mishaallakhani/detecting-skin-cancer-using-deep-learning-82deba830328))
57 | * Criando obras de arte a partir de imagem e estilo de pintura.([link](https://medium.com/tensorflow/neural-style-transfer-creating-art-with-deep-learning-using-tf-keras-and-eager-execution-7d541ac31398))
58 | * A partir de um vídeo, recria movimentos de dança e faz parecer que a pessoa realmente esta dançando. ([link](https://carolineec.github.io/everybody_dance_now/))
59 |
60 | >**PS: Não esquecer da engenharia de dados**
61 | >
62 | >A parte de engenharia de dados responsável por processar e padronizar os dados a fim de deixá-los manipuláveis, lindos e cheirosos é uma tarefa que todo cientista de dados vai precisar fazer alguma vez na vida, já que nem tudo são flores e nem todos os dados estão padronizados e/ou completos.
63 |
64 | ## 3. O que você precisa para começar na área
65 |
66 | Gostou? Está pronto para começar a trilhar em destino à Ciência de dados? Tenho algumas dicas para você.
67 |
68 | ### 3.1. Base
69 |
70 | É **fortemente** aconselhado que se tenha uma boa base de cálculo, estatística, programação e álgebra linear. Mas não precisa enlouquecer para ser o maior especialista no assunto! Essa base é importante para entender o que está acontecendo por baixo das coisas que você está fazendo.
71 |
72 | A notícia boa é que o nosso curso abrange bastante estas áreas, então fique tranquili! :D
73 |
74 | ### 3.2. Disciplinas específicas
75 |
76 | Você pode cursar as disciplinas ofertadas pelo curso que relacionadas à ciência de dados.
77 |
78 | * Ciência de Dados Descritiva: ofertada no 1º semestre do ano letivo pelo professor Nazareno <3 com ênfase na criação de relatórios exploratórios utilizando estatística sobre assuntos muito legais, como música, cinema e séries;
79 | * Ciência de Dados Preditiva: ofertada no 2º semestre do ano letivo pelo professor Leandro <3 com foco em Machine Learning e um pouco de Deep Learning;
80 | * Visualização da Informação: ofertada no 2º semestre do ano letivo pelo professor Nazareno <3 com ênfase em técnicas de melhores formas de visualizar a informação;
81 | * Recuperação da Informação: ofertada no 1º semestre do ano letivo pelo professor Leandro <3 com foco em processamento de linguagem natural, sistemas de recomendação, análise de sentimento, etc.
82 |
83 | ### 3.3. Cursos online
84 |
85 | Uma outra forma bem eficiente de aprender ciência de dados é fazer cursos online em plataformas como [Udacity](https://www.udacity.com/), [Coursera](https://www.coursera.org/), [Udemy](https://www.udemy.com/), entre outros.
86 |
87 | Nesta [planilha](https://docs.google.com/spreadsheets/d/13hP5jw5ahp0ZJMFkDZdLh0g-Ws7i_WM18ZfY7HoV0Rs/edit?usp=sharing) estão alguns cursos recomendados por Marianne Linhares, além de diversos outros materiais para quem se interessou e quer saber mais sobre o assunto.
88 |
89 | ## 4. Foi eterno enquanto durou...
90 |
91 | ... mas o post chegou ao fim! :(
92 |
93 | Eu amei fazê-lo e espero ter apresentado o básico de ciência de dados de forma clara e compreensível. Qualquer dúvida, reclamação ou sugestão, fique à vontade para `adicionar comentários neste post` ou conversar comigo fora dele: é só pesquisar 'hadrizia' nas plataformas, provavelmente serei a primeira (e única) a aparecer haha.
94 |
95 | ## 5. Obrigada!
96 |
97 | Muito obrigada pela leitura! Fique atento: em breve, teremos novos artigos de contribuidores do OpenDevUFCG aqui no dev.to. Acompanhe o OpenDevUFCG no [Twitter](https://twitter.com/OpenDevUFCG), no [Instagram](https://www.instagram.com/OpenDevUFCG/) e, claro, no [GitHub](https://github.com/OpenDevUFCG).
98 |
--------------------------------------------------------------------------------
/texts/v0.0.1/POST_RETROSPECTIVA_VISU_ANALOGICAS.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Retrospectiva OpenDevUFCG 2019 com visualizações analógicas
3 | published: false
4 | description: Observando os dados da organização no ano de 2019.
5 | tags: ptbr, data, design, opensource
6 | ---
7 |
8 | Olá! Chegou o final do ano, para muitos é tempo de férias, de se reunir com a família e de relembrar momentos do ano que está chegando ao fim. A OpenDevUFCG também não ia deixar passar em branco isso, então, que tal recapitular alguns acontecimentos de 2019 através de visualizações analógicas?
9 |
10 | ### Antes de mostrar as visus, um questionamento: o que são visualizações analógicas?
11 |
12 | São desenhos ou ilustrações, feitas à mão mesmo, sobre algum dado. A referência dessas visus é o trabalho [Dear Data](http://www.dear-data.com/theproject) das designers Giorgia Lupi e Stefanie Posavec originado em 2015, quando decidiram criar visualizações sobre dados do dia a dia e enviá-las através de cartões postais como forma de comunicar uma à outra o que estava acontecendo em suas rotinas. Por exemplo:
13 |
14 | 
15 |
16 | 
17 |
18 | As imagens acima foram tiradas do livro _Dear Data_ e, ao final do post, indicarei algumas referências do trabalho delas.
19 |
20 | ### Então, sabendo um pouco sobre visus analógicas, bora ver como os dados da OpenDevUFCG se comportaram ao longo do ano de 2019?
21 |
22 | Em 2019, a OpenDevUFCG completou um ano e, como resultado, tivemos o crescimento da equipe e do número de projetos, como podemos observar na imagem a seguir.
23 |
24 | 
25 |
26 | Tendo em vista que o time _Core_ cresceu, também tivemos mais ideias de projetos surgindo. É o que você vai ver na próxima imagem:
27 |
28 | 
29 |
30 | É de grande satisfação ver o crescimento de nossos projetos e que nosso espírito de comunidade está impactando muitas pessoas, e como resultado, ver que nossa ideia está influenciando as pessoas a querer saber mais e participar de projetos _open source_.
31 |
32 | Então, movidos por isso, pensamos em organizar o **Hacktoberfest OpenDevUFCG**, um evento para reunir boas ideias e pessoas legais para ajudar a comunidade através de atividades como palestras, minicursos, HackTime e abertura para sugestões. Agradecemos muito pelo apoio de todos, doadores e patrocinadores que compraram a ideia e deram apoio para o evento acontecer! Mas, deixando um pouco as palavras de lado, que tal ver os dados disso tudo?
33 |
34 | 
35 |
36 | Bem, tentei destacar os principais pontos da nossa organização esse ano. E, como prometido, links com mais detalhes sobre o projeto Dear Data:
37 |
38 | - [O projeto](http://www.dear-data.com/theproject);
39 | - [Vídeo TED](https://www.ted.com/talks/giorgia_lupi_how_we_can_find_ourselves_in_data);
40 | - [Livro](http://www.dear-data.com/thebook);
41 | - [Medium @Giorgia Lupi](https://medium.com/@giorgialupi).
42 |
43 | Então, gente, é isto, de novo gostaria de agradecer a cada um que acredita em nossos projetos e na nossa comunidade! Foi um ano muito legal para nossa organização, de bastante crescimento e de aprendizado, e sempre saibam, estamos dispostos para ajudar. Obrigada pela leitura, até ano que vem!
44 |
--------------------------------------------------------------------------------
/texts/v0.0.1/POST_SPOTIFY_WEB_SDK.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Usando o spotify-web-sdk para gerenciar requisições à API Web do Spotify
3 | description: Não nos chame, nós te chamamos!
4 | tags: react, webdev, showdev, ptbr
5 | canonical_url: https://dev.to/calluswhatyouwant/using-spotify-web-sdk-to-handle-requests-to-the-spotify-web-api-2oj6
6 | cover_image: https://thepracticaldev.s3.amazonaws.com/i/6a3wfu20y6db2gojyuzi.png
7 | ---
8 |
9 | Eu e [Renan](https://dev.to/joserenan) temos uma organização open source chamada [Call Us What You Want](https://github.com/calluswhatyouwant), na qual desenvolvemos aplicações com foco em música. Um de nossos projetos é um SDK para a API Web do Spotify, que criamos para servir de apoio para outras aplicações que fazem uso desses dados.
10 |
11 | Nessa postagem, vou mostrar como o [spotify-web-sdk](https://github.com/calluswhatyouwant/spotify-web-sdk) pode facilitar a recuperação e a gerência de dados de usuários do Spotify. Farei isso descrevendo o processo de desenvolvimento de uma aplicação inspirada por um problema *muito sério* que eu enfrentava como usuário dessa plataforma de streaming.
12 |
13 | {% github https://github.com/calluswhatyouwant/spotify-web-sdk %}
14 |
15 | # Um problema do mundo real (*A little motivation*)
16 |
17 | A seção de álbuns da minha biblioteca do Spotify é bem desorganizada, mas não exatamente por culpa minha — na implementação do aplicativo, sempre que um single é adicionado às músicas, o mesmo também acaba aparecendo como álbum. Isso acaba sendo um problema na hora de localizar algo para ouvir, já que eu prefiro consumir álbuns do início ao fim no lugar de entrar no modo aleatório das playlists.
18 |
19 | 
20 | Comfort Crowd and People são singles, mas também aparecem na minha biblioteca como álbuns.
21 |
22 | Pensei em uma solução simples: criei uma [playlist](https://open.spotify.com/user/jrobsonjr/playlist/7lWSJamQptWauCMIEDXgYC?si=HZZTKAY8RWObSGEdYm-4uA) contendo a primeira faixa de cada álbum que eu já ouvi. Ter essa playlist é muito útil para que eu também mantenha o registro do dia em que ouvi os trabalhos inteiros pela primeira vez (pelo menos quando eu lembro de adicioná-los assim que termino de escutá-los). Problema resolvido... ao menos parcialmente. Bem, para manter as coisas ainda mais organizadas, eu ainda queria poder ordenar as referências aos álbuns pelas suas datas de lançamento, algo que o Spotify ainda não dá suporte (mesmo com usuários pedindo por essa feature [há mais de cinco anos](https://community.spotify.com/t5/Live-Ideas/Your-Music-Sort-Music-in-quot-Your-Music-quot-by-Year/idi-p/744893)).
23 |
24 | Procurei por ferramentas que pudessem gerenciar esse método de ordenação para mim e descobri uma aplicação web chamada [Sort Your Music](http://sortyourmusic.playlistmachinery.com/). Apesar de ser super simples e eficiente de usar, a abordagem de ordenação deles sobrescreve a data de adição das faixas. Sei que por agora você já deve estar revirando os olhos com a minha insatisfação, mas, sabendo que é possível conseguir o resultado que eu desejo, decidi implementar a minha própria ferramenta. Nada como um pequeno DIY, certo?
25 |
26 | 
27 | Perdoe-me caso você não enfrente esse problema no seu dia a dia.
28 |
29 | # Devagar e sempre
30 |
31 | A API Web do Spotify disponibiliza dois endpoints para gerência de playlists: [um](https://developer.spotify.com/documentation/web-api/reference/playlists/replace-playlists-tracks) que permite sobrescrever todas as faixas e [outro](https://developer.spotify.com/documentation/web-api/reference/playlists/reorder-playlists-tracks/) que pode ser usado para mover uma faixa ou um bloco de faixas. Ao contrário do primeiro, este não modifica o *timestamp* que indica quando as faixas foram adicionadas à playlist e o identificador de quem as adicionou (para playlists colaborativas).
32 |
33 | 
34 | Visualização de como funciona o endpoint "reordenação de faixas de uma playlist". Fonte: Spotify for Developers
35 |
36 | Como visto acima, esse endpoint funciona de forma que ordenar uma playlist requer várias requisições consecutivas (basicamente uma requisição por faixa na playlist), o que também significa que leva muito mais tempo do que simplesmente sobrescrever tudo. Sacrifícios tiveram que ser feitos, mas o resultado é exatamente o esperado: aqui está o [Spotify Playlist Editor](https://github.com/calluswhatyouwant/spotify-playlist-editor)!
37 |
38 | 
39 | Quando você tiver feito login, algo assim deve aparecer na tela inicial da aplicação.
40 |
41 | # Detalhando o processo (mas não demais)
42 |
43 | Eu queria prototipar uma aplicação React o mais rápido possível, então usei o [create-react-app](https://facebook.github.io/create-react-app/), que me poupou muito tempo de configurações iniciais. Em geral, encorajo que você tente entender como criar uma aplicação React do zero, mas isso é com certeza é uma mão na roda quando se tem pressa. Para manter tudo simples, o **Spotify Playlist Editor** é *serverless*, mas permite que todos acessem seus dados do Spotify através de uma implementação do [fluxo de concessão implícita](https://developer.spotify.com/documentation/general/guides/authorization-guide/), uma abordagem simples para conseguir token de acesso à API.
44 |
45 | Incluí alguns pacotes e ferramentas para simplificar o processo de codificação (por exemplo, [Bootstrap](https://getbootstrap.com) para que eu me preocupasse menos com estilos e [prettier](https://prettier.io/) para ajudar a manter o código padronizadinho). Também considero interessante mencionar que uso [Flow](https://flow.org) para checagem estática de tipos. Eu amo que JavaScript tem tipagem dinâmica, mas como o SDK lida com muitos modelos que têm, por sua vez, muitos atributos, Flow se torna um ótimo aliado.
46 |
47 | 
48 | Você pode até odiar o Flow, mas o fato é que React + Flow + IntelliSense é um verdadeiro dream team.
49 |
50 | ## Conheça o melhor amigo ~~autoproclamado~~ da API Web do Spotify, o spotify-web-sdk!
51 |
52 | Aqui está um trecho de código tirado do componente *UserPage*. Você pode ver que alguns imports são feitos diretamente ao SDK:
53 |
54 | ```javascript
55 | /* @flow */
56 |
57 | import React, { Component } from 'react';
58 | import {
59 | /* Functions that wrap Spotify endpoints */
60 | init,
61 | getCurrentUserPlaylists,
62 | getCurrentUserProfile,
63 | /* Models */
64 | Page,
65 | PlaylistSimplified,
66 | PrivateUser,
67 | } from 'spotify-web-sdk';
68 |
69 | type Props = {
70 | history: any,
71 | };
72 |
73 | type State = {
74 | page: Page,
75 | playlists: PlaylistSimplified[],
76 | user: PrivateUser,
77 | };
78 |
79 | class UserPage extends Component {
80 | // ...
81 | }
82 | ```
83 |
84 | Uma vez que o usuário esteja conectado ao Spotify, *UserPage* é a página principal da aplicação. Seu principal propósito é exibir uma lista de playlists do usuário, com um botão que permite selecionar uma playlist de interesse. Inicialmente, cinco playlists são recuperadas:
85 |
86 | ```javascript
87 | componentDidMount = async () => {
88 | const page = await getCurrentUserPlaylists({ limit: 5 });
89 | this.setState({
90 | page,
91 | playlists: page.items,
92 | });
93 | }
94 | ```
95 |
96 | Ao manter o objeto *page* no state do componente, requisitar mais playlists é tão simples quanto poderia ser. Isso é graças à lógica de recuperar os próximos dados já estar implementada lá na declaração da classe *Page* no *spotify-web-sdk*:
97 |
98 | ```javascript
99 | class Page {
100 | // ...
101 | async getNextPage() {
102 | if (!this.hasNext()) throw new Error('There are no more pages');
103 | const params = {
104 | ...this.queryParams,
105 | limit: this.limit,
106 | offset: this.offset + this.limit,
107 | };
108 | const response = await this.getAxiosPageInstance().get('/', { params });
109 | return new Page(response.data, this.t, this.wrapper);
110 | }
111 | }
112 | ```
113 |
114 | Dessa forma, lidar com toda essa lógica no editor de playlists se resume a uma simples chamada de função, dispensando a necessidade de manter-se a par de valores como limite e offset:
115 |
116 | ```javascript
117 | loadMorePlaylists = async () => {
118 | const { page } = this.state;
119 | const nextPage = await page.getNextPage();
120 | // Relaxa e deixa o spotify-web-sdk lidar com o trabalho duro!
121 | this.setState(prevState => {
122 | const playlists = prevState.playlists.concat(nextPage.items);
123 | return { playlists, page: nextPage };
124 | });
125 | };
126 | ```
127 |
128 | O propósito principal da aplicação é permitir que os usuários ordenem suas playlists, então vamos focar nisso agora. Do componente *PlaylistPage*, o usuário pode selecionar um método de ordenação (incluindo por data de lançamento!). Por baixo, minha implementação determina primeiro a ordem final esperada das faixas e então faz uso de chamadas sequenciais para reordená-las. É assim que funciona mais ou menos em código:
129 |
130 | ```javascript
131 | import { reorderPlaylistTracks } from 'spotify-web-sdk';
132 |
133 | export const sortPlaylistTracksByAttribute = async (
134 | playlistId: string,
135 | attribute: string
136 | ) => {
137 | let insertionOrder = await getInsertionOrder(playlistId, attribute);
138 | return insertionOrder.reduce(async (previousPromise, current) => {
139 | await previousPromise;
140 | return moveTrackToTop(playlistId, current);
141 | }, Promise.resolve());
142 | };
143 |
144 | const getInsertionOrder = async (
145 | playlistId: string,
146 | attribute: string
147 | ) => { /* Determina a ordem de inserção baseada no atributo escolhido. */ };
148 |
149 | const moveTrackToTop = (id: string, oldIndex: number) =>
150 | reorderPlaylistTracks(id, oldIndex, { rangeLength: 1, insertBefore: 0 });
151 | ```
152 |
153 | A propósito, se você não está familizarizado com essa abordagem de resolver Promises sequencialmente usando *Array.prototype.reduce()*, tem um excelente artigo explicando como e, mais importantemente, a **razão** disso funcionar. Dá uma olhada lá no [CSS Tricks](https://css-tricks.com/why-using-reduce-to-sequentially-resolve-promises-works/)!
154 |
155 | Como mostrado acima, o objetivo principal do spotify-web-sdk é poupar tempo dos desenvolvedores que desejam fazer acesso à API Web do Spotify. No lugar de fazer chamadas diretas à API e se preocupar com detalhes, a maior parte do seu trabalho deve ser importar uma função e invocá-la no código.
156 |
157 | Caso você tenha interesse em construir sua própria aplicação usando dados do Spotify, a versão atual do *spotify-web-sdk* está [hospedada no NPM](https://npmjs.com/package/spotify-web-sdk) e pode ser adicionada rapidamente em um projeto com package.json rodando `npm add spotify-web-sdk`.
158 |
159 | O **Spotify Playlist Editor** também está no ar [aqui](https://calluswhatyouwant.github.io/spotify-playlist-editor/) se você quiser brincar um pouco. Caso você experiencie qualquer coisa inesperada ao usar a aplicação, é apenas um treino para estimular o espírito open source: crie uma issue no repositório do GitHub! PRs também são sempre bem-vindas (tem umas issues abertas e o Hacktoberfest está logo aí...).
160 |
161 | {% github https://github.com/calluswhatyouwant/spotify-playlist-editor %}
162 |
163 | Se quiser ficar por dentro das novidades dos projetos do Call Us What You Want, acompanhe o nosso [perfil no Twitter](https://twitter.com/CallUsWhat)!
164 |
165 | ---
166 |
167 | Muito obrigado pela leitura! Fique atento: em breve, teremos novos artigos de contribuidores do OpenDevUFCG aqui no **dev.to**. Acompanhe o OpenDevUFCG no [Twitter](https://twitter.com/OpenDevUFCG), no [Instagram](https://instagram.com/OpenDevUFCG) e, claro, no [GitHub](https://github.com/OpenDevUFCG).
--------------------------------------------------------------------------------
/texts/v0.0.1/POST_TESTS_JS.md:
--------------------------------------------------------------------------------
1 | Diferentemente do que muitos pensam, o desenvolvimento de uma aplicação Web ou Mobile necessita de testes, seja para assegurar a qualidade do produto, o funcionamento, e até mesmo a aparência, durante a evolução do código.
2 | Quando nosso software está bem consolidado em termos de testes, podemos estabelecer estratégias de integração e deploy contínuos (CI/CD). Esses métodos atuam para garantir que nossa aplicação não tenha sofrido efeitos colaterais com as adições, modificações e correções que estarão sendo enviadas à branch principal para deploy. Nesse post, serão introduzidos os conceitos de **Spies** e **Stubs**, e como eles são úteis durante o desenvolvimento de um conjunto de testes de unidade.
3 |
4 | # Teste de Unidade
5 |
6 | Vamos supor o seguinte cenário: temos uma aplicação que requer o cadastro dos seus usuários com um *username*, que deve ter tamanho de pelo menos 3 caracteres. Para tal, podemos adicionar no código de cadastro uma verificação para o tamanho do username:
7 |
8 | ```javascript
9 | function cadastrar(username, senha) {
10 | if (username.length < 3) {
11 | throw new Error('O username necessita de pelo menos 3 caracteres');
12 | }
13 | // Continua o cadastro
14 | };
15 | ```
16 |
17 | Quando escrevemos testes para a função de cadastro, nossa intenção seria testar diferentes casos, escolhendo **valores limite**, para podermos testar a qualidade da nossa verificação e se estamos deixando passar algum cenário indesejado. Por enquanto, não vamos nos importar tanto com a sintaxe, mas na semântica:
18 |
19 | ```javascript
20 | describe('testes da função de cadastro', () => {
21 | it('testa um username válido', () => {
22 | expect(cadastrar('teste', 'teste')).to.not.throw();
23 | /* Nesse caso, espera-se que não seja lançado um erro,
24 | * visto que o username tem três ou mais caracteres
25 | */
26 | });
27 | it('testa um username invalido', () => {
28 | expect(cadastrar('te', 'teste')).to
29 | .throw('O username necessita de pelo menos 3 caracteres');
30 | /* Nesse outro caso, como o username tem menos de 3 caracteres,
31 | * espera-se que seja lançado um erro com a mensagem descrita
32 | */
33 | });
34 | // testes de senha, e outros fluxos do cadastro
35 | });
36 | ```
37 |
38 | Nesse caso, estamos testando apenas a função de cadastro, ou seja, um teste unitário que testa apenas uma "unidade básica" do sistema (entenda unidade básica como aquela unidade que não chama outras funções internamente). De agora em diante, a ideia é termos funções mais complicadas que isso, ou seja, funções que precisam chamar outras funções na sua execução, por envolverem lógicas mais complexas.
39 |
40 | # Spies
41 |
42 | 
43 |
44 | Imagine agora que, uma vez cadastrado, também seja possível alterar esse *username*. Temos, então, duas situações possíveis em que desejamos verificar se o que o usuário inseriu é válido. Para isso, podemos refatorar nosso código atual de maneira a reutilizar as linhas que verificam se o *username* está no padrão correto:
45 |
46 | ```javascript
47 | function verificaUsername(username) {
48 | if (username.length < 3) {
49 | throw new Error('O username necessita de pelo menos 3 caracteres');
50 | }
51 | };
52 |
53 | function cadastrar(username, senha) {
54 | verificaUsername(username);
55 | // Continua o cadastro
56 | };
57 | ```
58 |
59 | Com o código refatorado, é também preciso refatorar os testes, para que se adequem ao contexto real do código:
60 |
61 | ```javascript
62 | describe('testes da função de cadastro', () => {
63 | it('testa um username válido', () => {
64 | const spy = sinon.spy(verificaUsername);
65 | expect(cadastrar('teste', 'teste')).to.not.throw();
66 | expect(spy).to.have.been.called;
67 | });
68 | it('testa um username invalido', () => {
69 | const spy = sinon.spy(verificaUsername);
70 | expect(cadastrar('te', 'teste')).to
71 | .throw('O username necessita de pelo menos 3 caracteres');
72 | expect(spy).to.have.been.called;
73 | });
74 | // testes de senha, e outros fluxos do cadastro
75 | });
76 | ```
77 |
78 | Agora que já vimos como os spies são declarados e verificados, é mais fácil entender seu significado: um spy serve para verificar se uma função foi chamada ou não durante a execução de outra função. No nosso exemplo, pedimos para que o sinon (a biblioteca de testes que estamos usando) "espie" o método `verificaUsername` e, após a chamada para a execução de `cadastrar`, verificamos se `verificaUsername` foi chamada.
79 |
80 | Entretanto, existe uma particularidade importante a se notar no nosso código: quando testamos um username inválido, a exceção ainda é lançada. Isso nos faz notar que nosso spy não modifica nada no código em execução, apenas verifica se as chamadas internas a uma função são realmente chamadas.
81 |
82 | # Stubs
83 |
84 | Mudando um pouco a perspectiva dentro do sistema que estamos construindo, podemos pensar num sistema mais complexo e que funciona numa certa sequência de operações e, para executar a operação seguinte, a anterior precisa ter sido executada corretamente. Por exemplo:
85 |
86 | ```javascript
87 | function operacaoComplexa() {
88 | return operacaoMenor().then((resposta) => {
89 | if (resposta.param) {
90 | // ...
91 | } else {
92 | // ...
93 | }
94 | return x;
95 | }).catch((erro) => {
96 | throw new Error(erro);
97 | });
98 | }
99 | ```
100 |
101 | A função acima não parece ter uma lógica nem um motivo bem definidos, como é o caso da função de cadastro. Entretanto, não é esse o ponto em que precisamos focar: podemos ver que o retorno da `operacaoMenor` é importante para entendermos o que será retornado nessa função, seja em caso de sucesso ou em caso de erro. Consideremos então que, por exemplo, essa função menor faz uma requisição a um serviço externo, uma API por exemplo.
102 |
103 | Na execução do nosso código, o código dessa função executará normalmente, fazendo a requisição necessária. Durante os testes, entretanto, não se deve fazer uma chamada à API, já que a API pode alterar dados reais da aplicação, deixar o banco de dados inconsistente e causar muitos outros problemas. Precisamos então de uma forma para testar a operação complexa sem realmente executar o código de `operacaoMenor`, e para isso servem os **stubs**.
104 |
105 | 
106 |
107 | Então, o que exatamente um Stub faz? Durante a execução dos nossos testes, um stub substitui uma função existente no código por uma função representativa, na qual é possível controlar o seu retorno. Através desse controle, o restante do código pode executar normalmente e é possível percorrer todos os cenários da execução do programa durante os testes, manipulando o retorno do stub conforme adequado. Vejamos como seria a aplicação de um stub no código dessa função:
108 |
109 | ```javascript
110 | describe('testa operacaoComplexa', () => {
111 | it('testa cenario 1 do then', async () => {
112 | const stub = sinon.stub(operacaoMenor)
113 | .resolves({ param: true });
114 | const retornoComplexo = await operacaoComplexa();
115 | expect(retornoComplexo).to.eql(/* retorno no caso 1 */);
116 | expect(stub).to.have.been.called;
117 | });
118 | it('testa cenario 2 do then', async () => {
119 | const stub = sinon.stub(operacaoMenor)
120 | .resolves({ param: false });
121 | const retornoComplexo = await operacaoComplexa();
122 | expect(retornoComplexo).to.eql(/* retorno no caso 2 */);
123 | expect(stub).to.have.been.called;
124 | });
125 | it('testa cenario catch', () => {
126 | const stub = sinon.stub(operacaoMenor)
127 | .rejects('mensagem de erro');
128 | operacaoComplexa()
129 | .then(() => {
130 | throw new Error('Operação não deveria ter dado certo');
131 | }).catch((erro) => {
132 | expect(erro).to.eql('mensagem de erro');
133 | });
134 | expect(stub).to.have.been.called;
135 | });
136 | });
137 | ```
138 |
139 | O teste acima verifica os três cenários que colocamos no código da nossa função. O teste parece ser grande, mas cobre apenas os três fluxos básicos na execução da `operacaoComplexa`. Explicando em alto nível a sintaxe:
140 | * no caso 1, estamos dizendo que a `operacaoMenor` deve ser um stub que resolve, no retorno da Promise, um objeto `{ param: true }`;
141 | * no caso 2, estamos dizendo que a `operacaoMenor` deve ser um stub que resolve, no retorno da Promise, um objeto `{ param: false }`;
142 | * no caso 3, de erro, estamos dizendo que a `operacaoMenor` deve ser um stub que rejeita, no retorno da Promise, sendo `'mensagem de erro'` a string retornada no erro.
143 |
144 | Nesse caso específico, nossa função complexa tinha uma chamada assíncrona (uma Promise) e, por isso, utilizamos `resolves` e `rejects` no nosso stub; caso fosse uma função síncrona, poderíamos ter utilizado `returns` normalmente.
145 |
146 | ## Plus!
147 |
148 | Existem diversas bibliotecas que podem ser utilizadas para testes em JavaScript. Algumas das mais famosas são [Mocha](https://mochajs.org/#getting-started), [Sinon](https://sinonjs.org) e [Chai](https://chaijs.com), que geralmente são utilizados em conjunto. Atualmente, uma das bibliotecas que está sendo bastante visada é o [Jest](https://jestjs.io). Se você está pensando em como começar a aplicar o que aprendeu aqui, te sugiro fazer alguns testes simples num dos sites que você hospeda no GitHub Pages — um portfolio, um pequeno projeto de disciplina, quem sabe? Qualquer um desses vai te dar um bom contato inicial :).
149 |
150 | ## Chegamos ao fim desse post... :(
151 |
152 | Mas não se preocupe, há muito mais conteúdo do OpenDevUFCG para ler aqui no dev.to, e em breve ainda mais posts saindo do forno.
153 |
154 | Agradeço bastante pela leitura, e se quiser entrar em contato comigo, só mandar um [Tweet](https://twitter.com/juliobguedes)! Se quiser ler mais textos meus, confere meu [Medium](https://medium.com/@Juliobguedes/) que em breve mais posts vão sair.
155 |
--------------------------------------------------------------------------------
/texts/v0.0.1/POST_WEB_CRAWLERS.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Usando Scrapy para obter metadadados das músicas dos Parcels, através do Genius.
3 | published: false
4 | description: Uso de webcrawler para extração de metadados de músicas
5 | tags: crawling, crawler, ptbr, scrapy
6 | ---
7 |
8 | Você já quis obter dados de um serviço que não disponibiliza uma API? Se você faz Computação na UFCG, provavelmente já escutou alguém falando do bot de matrícula. Já perguntou como ele funciona? Pois bem, no post de hoje você aprenderá como é possível fazer essas coisinhas e poderá usar sua criatividade para brincar e explorar ainda mais.
9 |
10 | ## Mr. Robot
11 |
12 | 
13 |
14 | Um **web crawler** pode ser definido como um script ou programa que é utilizado para acessar um website, extrair conteúdo e descobrir páginas relacionadas ao mesmo, repetindo o processo até que não existam mais páginas a ser pesquisadas.
15 | Eles são muito importantes, pois as engines de busca os utilizam para retornar resultados eficientes; devido a isso, frequentemente, algumas pessoas se referem a eles como bots da internet. De uma forma superficial, você pode pensar no crawler como um carinha que faz requisições para as páginas que ele encontra e, à medida que percorre essas páginas, faz o parse do seu HTML.
16 |
17 | ## Pré-Requisitos
18 |
19 | Para conseguir reproduzir o que faremos nesse tutorial você precisará ter o Python 3 configurado. Uma alternativa é usar o [virtualenv](https://www.digitalocean.com/community/tutorial_series/how-to-install-and-set-up-a-local-programming-environment-for-python-3), que possibilita a criação de um ambiente de desenvolvimento isolado.
20 |
21 | Além do Python 3, faremos uso do [**Scrapy**](https://scrapy.org/), um framework que possui as ferramentas necessárias para **extrair** dados de websites, **processar** os que você queira e **armazená-los** na estrutura de seu interesse. Apesar de ser possível construir um crawler usando módulos fornecidos pelo próprio Python, à medida que o seu projeto cresce, pode se tornar complicado gerenciar a execução do seu robôzinho, por isso faremos uso desse framework. Entretanto, caso você tenha interesse em conhecer outras alternativas, deixarei links nas referências.
22 |
23 | Para instalá-lo, utilize o índice de pacotes do Python([`PyPI`](https://pypi.org/)), através do seguinte comando:
24 |
25 | `pip install Scrapy`
26 |
27 | Feito isso, podemos dar início à nossa implementação.
28 |
29 | ## Além das letras
30 |
31 | Se você usa o Spotify, já viu que vez ou outra em algumas músicas são exibidas as letras e algumas informações interessantes que são extraídas do [**Genius**](https://genius.com/). Motivada em obter essas informações de uma banda que eu conheci recentemente, chamada [**Parcels**](https://open.spotify.com/artist/3oKRxpszQKUjjaHz388fVA) (inclusive, fica aqui a sugestão: se você gosta de *Daft Punk*, provavelmente vai curtir a música deles), decidi construir esse crawler.
32 |
33 |
34 | 
35 |
36 | A primeira coisa que um crawler precisa é de um ponto inicial, também chamado de **seed**, que será usado para iniciar a extração dos conteúdos. No nosso caso, esse ponto será a página da banda no Genius ([essa aqui](https://genius.com/artists/Parcels)).
37 |
38 | Uma vez que temos isso, podemos iniciar a construção do nosso robôzinho. Crie um arquivo python chamado `genius.py`, que possui o seguinte conteúdo:
39 |
40 | ```python
41 | import scrapy
42 |
43 | class GeniusSpider(scrapy.Spider):
44 | name = "genius"
45 | start_urls = ['https://genius.com/artists/Parcels']
46 |
47 | ```
48 |
49 | Primeiro, importamos o **Scrapy** para ter acesso às funcionalidades que esse módulo fornece. Em seguida, criamos um classe chamada `GeniusSpider`, baseada na `Spider` do Scrapy; é ela que define quais métodos estamos hábeis a usar, métodos estes que poderão nos auxiliar durante a execução do crawler. Por fim, definimos o nome do spider como **genius** e o nosso *seed* como sendo a página dos Parcels.
50 |
51 | Diferentemente do que fazemos para executar scripts em Python, usaremos o comando que o próprio Scrapy provê por meio da sua CLI:
52 |
53 | `scrapy runspider genius`
54 |
55 | 
56 |
57 | Certo, temos várias letrinhas bonitinhas e outras nem tanto, mas o que tudo isso quer dizer?
58 |
59 | 1. O Scrapy carregou e configurou o que precisava para iniciar;
60 | 2. Requisitou a url que definimos no `start_urls`, e baixou o seu conteúdo;
61 | 3. Repassou esse conteúdo para um método parse. Como não o tínhamos criado, nada aconteceu e ele finalizou a execução.
62 |
63 | ## Extraindo metadados
64 |
65 | O próximo passo é definir **como** o Scrapy deve extrair o conteúdo: fazemos isso definindo o método **parse**. Nesse passo, é importante que você entenda como estão organizados os elementos da sua página, pois será essencial para transmitir ao crawler quais locais ele deve **raspar** quando extrair a página.
66 |
67 | 
68 |
69 | Analisando a imagem acima, você pode ver que à sua direita existe uma listagem de cards com as músicas populares que, quando clicados, nos levam ao conteúdo que queremos. Dessa forma, esse deve ser o local de onde nosso crawler irá extrair os links.
70 |
71 |
72 | O Scrapy extrai o conteúdo através de seletores, que são "padrões" ou "modelos" que casam com os elementos de uma árvore do documento e portanto podem ser usados para selecionar os nós de um documento HTML. Para conseguir fazer isso, o Scrapy fornece duas formas: através do Xpath e através de seletores CSS; usaremos os seletores CSS por questão de simplicidade.
73 |
74 | Usando o inspetor do meu browser para analisar quais os nós que contêm esse link e então formar o nosso seletor, consegui notar que o elemento que contém essa lista de cards é uma div. Veja no GIF:
75 |
76 | ```html
77 |
80 | ```
81 |
82 | 
83 |
84 | Sendo assim, podemos informar ao **Scrapy** que ele deve obter algo como:
85 |
86 | `div.mini_clard_grid-song a::attr(href)`
87 |
88 | Isso indica que queremos os links que são filhos de um elemento da classe `mini_card_grid-song`, por isso o `.` (como em CSS). Além disso, adicionamos esse trecho `::attr(href)` depois da tag `a`, porque se definíssemos o padrão apenas com a tag teríamos todo o nó HTML, e não apenas a url.
89 |
90 | Para entender melhor sobre os seletores CSS, veja [esse link](https://docs.scrapy.org/en/latest/topics/selectors.html) da documentação.
91 |
92 | Assim, podemos construir nosso método:
93 |
94 | ```python
95 | def parse(self, response):
96 | songs_urls = response.css('div.mini_card_grid-song a::attr(href)').getall()
97 | for song_url in songs_urls:
98 | yield scrapy.Request(url=song_url, callback=self.parse_lyrics_page)
99 | ```
100 |
101 | O método é composto por um parâmetro **response**, que indica o conteúdo obtido após o crawler ter requisitado nossa url inicial. Feito isso, temos as urls das músicas sendo obtidas a partir do nosso seletor; essa lista de urls que o crawler requisitará é chamada de **frontier**. É importante ressaltar que usamos o `getAll()`, porque queremos extrair **todos** os seletores que casarem com o padrão que passamos, mas às vezes estamos interessados apenas na primeira ocorrência e podemos fazer uso do `get()`. Uma vez que temos as urls, fazemos as requisições, passando a url e uma **callback**, que é uma função que será executada após o crawler fazer download da url que passamos.
102 |
103 | Ótimo, conseguimos alcançar as páginas das nossas músicas, mas depois que chegamos nelas, o que faremos? Seguimos um processo muito parecido com o anterior, diferindo apenas que, ao invés de tentar extrair links, poderemos extrair as informações desejadas. Do passo anterior, vemos que o método que será executado após ser consultada a página da música é o `parse_lyrics_page`, então adicione esse trecho ao seu arquivo:
104 |
105 | ```python
106 |
107 | def parse_lyrics_page(self, response):
108 | title = response.css('div.song_body-lyrics h2::text').get()
109 | song_metadata = response.css('div.rich_text_formatting p::text').getall()
110 | artists = set(response.css('span.metadata_unit-info a::text').getall())
111 | lyric = response.css('div.lyrics p::text').getall()
112 | annotations_ids = response.css('a::attr(annotation-fragment)').getall()
113 |
114 | item = {
115 | 'title': title,
116 | 'artists': artists,
117 | 'lyric': lyric,
118 | 'metadata': song_metadata,
119 | 'snippet_lyric': [],
120 | 'annotations': []
121 | }
122 |
123 | if annotations_ids:
124 | return self.get_annotations(response, annotations_ids, item)
125 | else:
126 | return item
127 |
128 | def get_annotations(self, response, annotations_ids, item):
129 | for annotation_id in annotations_ids:
130 | url = urljoin(response.url, annotation_id)
131 | yield scrapy.Request(
132 | url=url,
133 | callback=self.parse_annotation_page,
134 | meta={'item': item}
135 | )
136 | ```
137 |
138 | Muita coisa, né? Mas vamos por partes, como diria Jack.
139 |
140 | Para extração das informações básicas como os artistas, a letra, o título e os metadados, não temos nenhuma novidade em relação ao que fizemos na extração dos links, já que só precisamos fornecer o seletor e ele irá obter a informação.
141 |
142 | No entanto, as anotações seguem um comportamento diferente. Olhando a página, você verá que os elementos que deveriam conter as informações de anotações das músicas contém apenas um identificador, que redireciona para outra página que abriga o conteúdo delas. Então, o que estamos fazendo é:
143 |
144 | Se existem anotações, obtenha seus ids e, para cada id obtido, concatene a nossa url inicial:
145 |
146 | `urljoin(response.url, annotation_id)`
147 |
148 | E, em seguida, fazemos uma requisição para cada um deles. Mas espere, essa requisição tem algo que ainda não vimos antes: um parâmetro chamado `meta`.
149 |
150 | 
151 |
152 | O `meta` é a forma que o Scrapy provê para que consigamos nos comunicar entre uma página e outra. Então, o que estamos querendo dizer é que toda vez que ele consultar as páginas das anotações, leve consigo as informações que já extraímos dessa. Isso será útil para unirmos os dois objetos e retornamos os resultados.
153 |
154 | Agora que você está na página de anotações, encontre os seletores das anotações e retorne o resultado, e pronto, você terá feito o crawler. Deve ficar algo como mostrado no trecho abaixo:
155 |
156 | ```python
157 | def parse_annotation_page(self, response):
158 | snippet_lyric = response.css("meta[property='og:title']::attr(content)").getall()
159 | annotations = response.css("meta[property='og:description']::attr(content)").getall()
160 | item = response.meta['item']
161 | item['snippet_lyric'] = snippet_lyric
162 | item['annotations']= annotations
163 |
164 | return item
165 | ```
166 |
167 | Caso não existam anotações, ele já retornará o resultado, que é o objeto com as informações que obtivemos na primeira página. Para salvar o que coletamos em um `json`, usamos o comando:
168 |
169 | `scrapy crawl genius -o parcels-lyrics.json`
170 |
171 | Você terá algo com:
172 | {% gist https://gist.github.com/fanny/06aaad556fcba66abbfba4dd22e18f3c file=parcels-lyrics.json %}
173 |
174 | **Nota:** É possível salvar o dado em outros formatos, como csv, dê uma olhada na [documentação](https://docs.scrapy.org/en/latest/topics/feed-exports.html#topics-feed-exports).
175 |
176 | E é isso, você construiu o crawler! :hoo-ray:
177 |
178 | 
179 |
180 | ## Considerações finais
181 |
182 | Até aqui você aprendeu os conceitos básicos para construir um crawler. À medida que seu projeto expandir, você precisará lidar com outras coisas, como *politeness policies*, porque pense: se fizermos muitas requisições para um site, podemos sobrecarregá-lo e torná-lo indisponível por algum tempo para outros usuários. Leitura de sitemaps também são importantes para garantir que o crawler consiga obter efetivamente certos links que o website acredita ser essenciais, além de várias outras técnicas.
183 |
184 | Entretanto, agora que você já sabe o básico, me conta nos comentários alguma ideia que você pensa em construir! E se tiver qualquer dúvida ou sugestão, fique à vontade para adicionar comentários neste post ou trocar uma ideia comigo fora dele (minhas redes sociais estão mapeadas no meu perfil).
185 |
186 | E se quiser estar por dentro do que eu tô fazendo e escutando (música é realmente uma das minhas paixões além de Computação), me segue no [Spotify](https://open.spotify.com/user/anotherfanny) ou [Last.fm](https://www.last.fm/pt/user/Fannyvieira25) e [GitHub](https://github.com/fanny).
187 |
188 | ## Referências
189 |
190 | ### Tutoriais
191 |
192 | - [Documentacão do Scrapy](https://docs.scrapy.org/en/latest/intro/overview.html);
193 | - [Fazendo web crawlers em Python - Inglês](https://www.datacamp.com/community/tutorials/making-web-crawlers-scrapy-python);
194 | - [Utilizando o Scrapy do Python para monitoramento em sites de notícias - PTBR](https://medium.com/@marlessonsantana/utilizando-o-scrapy-do-python-para-monitoramento-em-sites-de-not%C3%ADcias-web-crawler-ebdf7f1e4966).
195 |
196 | ### Livros
197 |
198 | - [Learning Scrapy](https://www.amazon.com.br/Learning-Scrapy-Dimitris-Kouzis-Loukas/dp/1784399787);
199 | - [Web Scraping with Python](https://www.amazon.com/Web-Scraping-Python-Collecting-Modern/dp/1491910291/ref=as_li_ss_tl?ie=UTF8&qid=1469961194&sr=8-1&keywords=web+scraping+with+python&linkCode=sl1&tag=scraping06-20&linkId=eda03663399eeff90133094d677e4cd4).
200 |
201 | ## Obrigada
202 |
203 | Muito obrigada pela leitura! Fique atento: em breve, teremos novos artigos de contribuidores do OpenDevUFCG aqui no dev.to. Acompanhe o OpenDevUFCG no [Twitter](https://www.twitter.com/OpenDevUFCG/), no [Instagram](https://www.instagram.com/OpenDevUFCG/) e, claro, no [GitHub](https://www.github.com/OpenDevUFCG/).
204 |
--------------------------------------------------------------------------------