Abra seu Código!!!http://blog.abraseucodigo.com.br/2018-02-01T00:05:00-02:00Como gerenciar minhas senhas?2018-02-01T00:05:00-02:002018-02-01T00:05:00-02:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2018-02-01:/como-gerenciar-minhas-senhas.html<p class="first last">Mostrando um pouquinho do gpg, oathtool e pwgen que eu uso para gerenciar minhas senhas</p>
<p>Esses dias em uma call no Hangout com alguns colegas estive falando sobre senhas, gpg, two-factor-authentication e essas coisas.</p>
<p>Eis que pensei que esse papo rápido deveria virar um post detalhado, pois percebi que muitos dos colegas que ali falavam comigo não conheciam algumas palavrinhas mágicas que eu estava falando.</p>
<p>Nesse post vou explicar o que EU faço para gerenciar minhas senhas, se vocês quiserem usar TUDO que eu falar aqui fiquem a vontade, a maioria das ferramentas aqui citadas funcionam lindamente em ambientes Linux, não faço ideia se vão funcionar em Mac OSX e Windows.</p>
<div class="section" id="como-gerar-senhas-seguras">
<h2>Como gerar senhas seguras?</h2>
<p>Gosto bastante de um comandinho mágico chamado <cite>pwgen</cite> que é facilmente instalado pelo pacote de mesmo nome em sistemas Debian-like pelo comando apt-get:</p>
<div class="highlight"><pre><span></span><span class="gp">$ </span>sudo apt-get install pwgen
</pre></div>
<p>Este comando é muito simples de usar, basta abrir um terminar e digitar o seu respectivo nome:</p>
<div class="highlight"><pre><span></span><span class="gp">$ </span>pwgen
<span class="go">ki8eTeef Acho1or1 oophuGh5 shawoe6F ahmi9ieG Iej3ohmi oS3unais ve0eiv5X</span>
<span class="go">iew5gaiP othai7Ch UFoh1eep naid5Sho Koo5Hai9 cohThaa8 eiBok1ae koofei8S</span>
<span class="go">Dae8Wai1 Pae1lo8u Iega7uah uNoawae8 Ood7uiN0 raiQu3ai wae6ca4U ohNg9aer</span>
<span class="go">Shahbio9 taeP9Oku weo7Rapa iu3yieC6 oThi2gen ahK4ahsa Dah3shei nai2Baiy</span>
<span class="go">ohz5Wur8 eeShee6x Ahyah0Be ieYo9Ail hoo6tooP aej7ToPh Veith3do Ro0fahv8</span>
<span class="go">Mie4meeK aeghooR0 sha6eeVo uc1Juish eeXui7le IeY5eiY5 queT7Iev Pha4ahvu</span>
<span class="go">eXah4ulu Oong9eig IoNgie9e Ieng0pie mah3Einu jeiwen6U bu5Ohw4d VeeXak2a</span>
<span class="go">bohse7uY EiJee2Ie xae0aiSh huo3Yei5 leCh7ezu Soh2thoh Chughei2 Yu7ahHed</span>
<span class="go">Audutee4 naN5aequ upu3Ka8z Choh6Chu nae9ic1E Heewuy7A quai5Te2 Oebai8ae</span>
<span class="go">ux0Xieng Hoong6pi Ezoh1aid eiVe1Ue2 IetaB3ei eib4Phee go6ooRee ool0aiWi</span>
<span class="go">uN4pua8y Icheiy5w EiK3aeci pae1Eej9 ao3Phaif Jeequoo3 uJ5doequ bu4Xooph</span>
<span class="go">eiGa1pha ahh6Ei0R Xai8thoo Quae4Iez AiMas9ew Ri3lahpi ahL6Eija ohyeih6A</span>
<span class="go">phe2Oor3 IaBaex6i bohTo0ba Ait0Uosh gaih1Bei Ohs7thei Ais1uejo UYah1shi</span>
<span class="go">oor7Eec3 pahgh7Ch ekie5aFu io0ohpuK aghai2Ba Hie4Aeb4 ohve9Chi Iev9Mu3H</span>
<span class="go">iej0aiNi OhCh5ied hie3ooGh eiguuJ1a zu7Aut6a Teen7owi eiy2Ahvu daCui2iv</span>
<span class="go">Uc4Dothi Emah2oht Suv6poi5 Eej3quoa Mooj8eim Teifu1ah ZuaDeez3 Ereoqu9I</span>
<span class="go">ua0juoTe Fash9eig eiY6Ohr0 Imie8jie dohJoh4t ieNg2coo XahQuu8s eereS0ri</span>
<span class="go">Da7yiyu1 zaepoo0A to0Aexoh gooxoh0D Aezoofo3 aeZ5oopo Shoo6iip aemahx0R</span>
<span class="go">Thay5gah aethu0Ph Yaedei4d ixur7Xoh Eidee8ri ata9oNoo yoox9Zee foow9Muz</span>
<span class="go">pah1Oe0w Xiec1vie quee2Aeb neXe0ahl choh2Oan nai0Uhae iek8pahG iu1ohZ7u</span>
</pre></div>
<p>Ele vai gerar uma pancada de senhas exatamente como demostrado acima. Porém tem outros parâmetros bem legais, exemplos:</p>
<div class="highlight"><pre><span></span><span class="gp"># </span>gera uma senha somente
<span class="gp">$ </span>pwgen -1
<span class="go">zoh1So3D</span>
<span class="gp"># </span>gera uma senha simples de <span class="m">20</span> caracteres
<span class="gp">$ </span>pwgen -1 <span class="m">20</span>
<span class="go">iesheiquaesae0uGhuK5</span>
<span class="gp"># </span>gerar senhas mais complexas para serem memorizadas com o parâmetro -s
<span class="gp">$ </span>pwgen -s -1 <span class="m">20</span>
<span class="go">rtUyok8K5nFdcWQpuHZU</span>
<span class="gp"># </span>gerar senhas com símbolos
<span class="gp">$ </span>pwgen -1 -y
<span class="go">ieShe"c8</span>
<span class="gp">$ </span>pwgen -1 -y <span class="m">20</span>
<span class="go">eiriaraidee8Naecaap~</span>
<span class="gp">$ </span>pwgen -1 -y <span class="m">20</span> -s
<span class="go">_7kwgl7tu1QEb#^Qz/K></span>
</pre></div>
<p>Perceba atentamente que os parâmetros podem ser combinados para gerar senhas mais complexas.</p>
</div>
<div class="section" id="como-guardar-minhas-senhas">
<h2>Como guardar minhas senhas?</h2>
<p>Eu utilizo a um tempinho o comando <cite>gpg</cite> para fazer isso. O <cite>gpg</cite> parte integrante do pacote <cite>gnupg</cite> e se você quiser saber mais sobre essa história pode dar uma <a class="reference external" href="https://pt.wikipedia.org/wiki/GNU_Privacy_Guard">lida aqui</a>.</p>
<p>Para instalar o comando <cite>gpg</cite> via apt-get é muito simples:</p>
<div class="highlight"><pre><span></span><span class="gp">$ </span>sudo apt-get install gnupg
</pre></div>
<p>Porém vale lembrar que na maioria das distribuições do Linux atualmente este comando vem instalado por padrão. Após a instalação suponha que eu temos um arquivo chamado <cite>passwords</cite> e vou exibir seu conteúdo:</p>
<div class="highlight"><pre><span></span><span class="gp">$ </span>cat passwords
<span class="go">serviço xpto</span>
<span class="go">usuário: teste</span>
<span class="go">senha: 12345</span>
<span class="go">--------</span>
<span class="go">serviço xablau</span>
<span class="go">usuário: abc</span>
<span class="go">senha: 4321</span>
<span class="go">--------</span>
</pre></div>
<p>Vou encriptar esse arquivo com uma senha mestra usando o <cite>gpg</cite> (nesta etapa a senha mestra será solicitada):</p>
<div class="highlight"><pre><span></span><span class="gp">$ </span>gpg -c passwords
</pre></div>
<p>Após a execução deste comando acima um novo arquivo será gerado nomeado como <cite>passwords.gpg</cite> este arquivo é a saída encriptada com o <cite>gpg</cite>. Mostrando seu conteúdo (abaixo) podemos ver que realmente ele está encriptado:</p>
<div class="highlight"><pre><span></span><span class="gp">$ </span>cat passwords.gpg
<span class="go"><jӘ�h1��ҁ�(ߠ+����a����</span>
<span class="go"> 7l3_�۔F�>��,;g��z2�qk�g`ZE�-�i��w��k���������X�d)��O���o�igi�)7></span>
<span class="go"> ˳</span>
<span class="go">�E~�3���Q̿4� �[x+48�&�̋6g-��Xd�</span>
</pre></div>
<p>A saída será esse monte de caracteres estranhos conforme mostrado acima. Para que nós consigamos ler esse arquivo novamente podemos executar novamente o comando <cite>gpg</cite> conforme mostrado abaixo (você deverá informar a senha mestra nesta etapa, caso você não a tenha informado durante esta "seção de shell"):</p>
<div class="highlight"><pre><span></span><span class="gp">$ </span>gpg -d passwords.gpg
<span class="go">gpg: AES encrypted data</span>
<span class="go">gpg: encrypted with 1 passphrase</span>
<span class="go">serviço xpto</span>
<span class="go">usuário: teste</span>
<span class="go">senha: 12345</span>
<span class="go">--------</span>
<span class="go">serviço xablau</span>
<span class="go">usuário: abc</span>
<span class="go">senha: 4321</span>
<span class="go">--------</span>
</pre></div>
<p>Pronto! Feito isso agora podemos apagar o arquivo antigo chamado <cite>passwords</cite> e ficamos com o arquivo <cite>passwords.gpg</cite> criptografado que só eu tenho a senha. Vamos remover:</p>
<div class="highlight"><pre><span></span><span class="gp">$ </span>rm passwords
</pre></div>
<p>Eu recomendo como "boas práticas" no uso do <cite>gpg</cite>:</p>
<ol class="arabic simple">
<li>Não nomear o arquivo de senhas como <cite>passwords</cite> nem como <cite>senhas</cite> nem outro nome óbvio;</li>
<li>Se quiser você também poderá omitir o nome <cite>.gpg</cite> ao final do arquivo, a extensão não é necessária para que o <cite>gpg</cite> consiga ler o arquivo;</li>
<li>Crie um arquivo de senhas com uma senha mestra diferente para cada conjunto de senhas que você tem, como por exemplo os seguintes conjuntos: senhas do trabalho, senhas de redes sociais, senhas de email ... etc</li>
</ol>
<p>Seguindo esses passos não garanto que sua segurança esteja 100% garantida, mas irá melhorar bastante! A propósito você pode encriptar outros arquivos e não somente arquivos texto, você conseguirá encriptar qualquer arquivo com o <cite>gpg</cite>.</p>
</div>
<div class="section" id="como-gerenciar-tokens-de-autenticacao-por-dois-fatores-2fa">
<h2>Como gerenciar "tokens" de autenticação por dois fatores (2FA)?</h2>
<p>Autenticação por dois fatores hoje em dia é um negócio indispensável para qualquer serviço web (ou não web) que você utiliza, se você ainda não conhece deveria conhecer <a class="reference external" href="https://pt.wikipedia.org/wiki/Identifica%C3%A7%C3%A3o_por_dois_fatores">clique aqui para saber mais</a>.</p>
<p>Porém este tipo de autenticação pode ser o vilão da sua vida quando é mal administrado. Vamos dar um exemplo clássico:</p>
<pre class="literal-block">
Eu sou um usuário de um serviço X e uso autenticação por dois fatores e para isso tenho no meu smartphone um aplicativo magnífico chamado Google Authenticator, sempre que algum serviço requisita meu token eu vou no meu smartphone e recebo esse token através do aplicativo do celular.
</pre>
<p>Esse usuário acima era eu a alguns meses atrás, porém comecei a pensar que eu poderia sofrer um assalto, me roubarem o celular e eu ficar a ver navios.</p>
<p>É uma verdade que todo serviço que disponibiliza autenticação por dois fatores também te disponibiliza tokens de recuperação para o caso de acontecerem tragédias (como assaltos) e você perder o "app autenticador" com seus tokens todos.</p>
<p>Porém tem outra opção para você mesmo gerenciar/gerar seus tokens! Você construir sua própria ferramenta gerenciadora/geradora de tokens!</p>
<p>Quem me deu essa dica foi meu amigo <a class="reference external" href="https://disqus.com/by/jaimeasnchezhidalg/">Jayme</a>. E pra falar a verdade você nem vai construir tanta coisa assim, pois existe um utilitário de linha de comando que te ajuda a fazer isso. Vamos começar instalando o pacote <cite>oauthtool</cite>:</p>
<div class="highlight"><pre><span></span><span class="gp">$ </span>sudo apt-get install oathtool
</pre></div>
<p>Legal! Instalado! Agora vamos testar:</p>
<div class="highlight"><pre><span></span><span class="gp">$ </span>oathtool --base32 --totp <span class="s2">"xablaus"</span>
<span class="go">865242</span>
<span class="gp">$ </span>oathtool --base32 --totp <span class="s2">"xablaus"</span>
<span class="go">376849</span>
</pre></div>
<p>Percebam que a cada vez que eu executei este comando com a secret <cite>xablaus</cite> ele me gerou um código. Este código seria o nosso token válido para autenticação por dois fatores.</p>
<p>Agora para que você possa descobrir o secret do seu serviço você pode usar qualquer aplicativo que leia QR Codes (que não seja o Google Authenticator), eu atualmente uso um muito legal (que pode ser baixado no Google Play gratuitamente) chamado <a class="reference external" href="https://play.google.com/store/apps/details?id=tw.mobileapp.qrcode.banner&hl=pt">Código QR Reader</a>.</p>
<p>Quando eu uso este aplicativo apontando para um código QR que os serviços de 2FA me dão eu tenho algo parecido com isso:</p>
<img alt="imagem da app QR Reader" class="align-center" src="images/como-gerenciar-minhas-senhas.png" />
<p>Baseado no link retornado do QR Code você deverá usar somente a parte do secret que seria o código <cite>HKAOMWCXK6NCVIX7XHUIABCSYC3J42MQWBBDPHG2GKRELBTT5YISYYUULA7ZPOQD</cite>, com esse código em mãos podemos usar o comando <cite>oathtool</cite>:</p>
<div class="highlight"><pre><span></span><span class="gp">$ </span>oathtool --base32 --totp <span class="s2">"HKAOMWCXK6NCVIX7XHUIABCSYC3J42MQWBBDPHG2GKRELBTT5YISYYUULA7ZPOQD"</span>
<span class="go">866946</span>
<span class="gp">$ </span>oathtool --base32 --totp <span class="s2">"HKAOMWCXK6NCVIX7XHUIABCSYC3J42MQWBBDPHG2GKRELBTT5YISYYUULA7ZPOQD"</span>
<span class="go">640765</span>
</pre></div>
<p>Como vocês podem ver na imagem esse código eu gerei a partir da minha conta pessoal da Amazon Web Services (obviamente que não estou usando este no momento senão meio mundo agora estaria tentando me invadir neste momento), para que sejam gerados tokens de outros serviços o mesmo procedimento poderia ser usado tranquilamente.</p>
</div>
<div class="section" id="conclusao-final">
<h2>Conclusão final</h2>
<p>Agora que você aprendeu a fazer tudo isso, que tal misturar as coisas para trazer mais segurança? Vou deixar duas dicas:</p>
<ul class="simple">
<li>Você pode armazenar seus secrets encriptados com <cite>gpg</cite> e criar um script simplório para que o oathtool solicite sua chave mestra do gpg para depois gerar seus tokens;</li>
<li>Você poderá armazenar recovery codes e outras coisas na sua máquina sem problema nenhum se isso estiver devidamente encriptado com <cite>gpg</cite>. Desta forma se alguma tragédia ocorrer com os seus tokens de autenticação por duplo fator você poderá ler a partir do arquivo encriptado seus recovery codes e recuperar sua conta normalmente;</li>
<li>Não confia na sua máquina? Nem eu! Você poderá jogar arquivos encriptados para o Dropbox/Google Drive ou outro serviço na nuvem para não perder suas senhas e acessos caso seu notebook seja roubado ou seu hd misteriosamente explodir do nada do dia para a noite.</li>
</ul>
<p>Espero que tenham gostado do post! Deixem seus comentários ai abaixo que a gente continua a conversa.</p>
</div>
Usando vetores em Shell Script2018-01-09T20:33:00-02:002018-01-09T20:33:00-02:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2018-01-09:/usando-vetores-em-shell-script.html<p class="first last">Como usar vetores em Shell Script e como eles podem me ajudar?</p>
<p>Eu assumo! Eu sou um shelleiro!</p>
<p>Gosto muito de Shell Script e já cheguei a trabalhar cerca de 2 anos programando com Shell Script em uma empresa muito massa chamada <a class="reference external" href="http://www.automalogica.com.br/">Automalógica</a>, onde eu desenvolvia scripts em Shell para geração de bases de um sistema <a class="reference external" href="https://pt.wikipedia.org/wiki/Sistemas_de_Supervis%C3%A3o_e_Aquisi%C3%A7%C3%A3o_de_Dados">SCADA</a> em específico chamado <a class="reference external" href="http://www.cepel.br/produtos/sage-sistema-aberto-de-gerenciamento-de-energia.htm">SAGE</a>, muitos e muitos parsers desenvolvidos.</p>
<p>Como eu gosto muito de Shell sempre vejo algumas vantagens no seu uso como:</p>
<ul class="simple">
<li>Ser uma ótima linguagem de programação (me desculpem os puristas que não gostam que fale que Shell é uma linguagem) para administração de sistemas GNU/Linux, pois você consegue usar todos os comandos disponíveis dentro da programação;</li>
<li>Ser rápida para codificar coisas simples;</li>
<li>Funciona em todo sistema operacional Linux e até alguns que não são Linux, pois geralmente a galera usa <a class="reference external" href="https://pt.wikipedia.org/wiki/Bash">Bash (Bourne again Shell)</a> para codificar.</li>
</ul>
<p>Porém cuidado! Muito Shell pode dificultar sua vida. Shell não tem estruturas complexas de dados e muito menos orientação a objeto e coisas do tipo o que faz com que programas muito grandes fiquem impossíveis de serem mantidos.</p>
<div class="section" id="para-que-voce-pode-usar-vetores-em-shell-script">
<h2>Para que você pode usar vetores em Shell Script?</h2>
<p>Vetores ou arrays são conhecidos em muitas linguagens de programação principalmente para a finalidade de iterar sobre um conjunto de elementos usando alguma estrutura de iteração (for, while, do/while, etc), e em Shell Script não é diferente.</p>
<p>Vamos a um exemplo didático disso, imagine que eu gostaria de pegar a primeira coluna de um csv e criar pastas/diretórios com esses nomes:</p>
<div class="highlight"><pre><span></span><span class="ch">#!/bin/bash</span>
<span class="nv">FIRST_COLUMN</span><span class="o">=(</span> <span class="k">$(</span>awk -F<span class="s2">","</span> <span class="s1">'(NR>1){print $1}'</span> arquivo.csv<span class="k">)</span> <span class="o">)</span>
<span class="k">for</span><span class="o">((</span><span class="nv">cont</span><span class="o">=</span><span class="m">0</span><span class="p">;</span>cont<<span class="si">${#</span><span class="nv">FIRST_COLUMN</span><span class="p">[@]</span><span class="si">}</span><span class="p">;</span>cont++<span class="o">))</span><span class="p">;</span> <span class="k">do</span>
mkdir <span class="si">${</span><span class="nv">FIRST_COLUMN</span><span class="p">[</span><span class="nv">$cont</span><span class="p">]</span><span class="si">}</span>
<span class="k">done</span>
</pre></div>
<p>Considerando que a estrutura do meu arquivo csv fosse algo parecido com isso abaixo:</p>
<div class="highlight"><pre><span></span>$ cat arquivo.csv
diretorios,status
teste1,false
teste2,false
</pre></div>
<p>Ao executar este script eu teria os seguintes diretórios criados: <cite>teste1</cite> e <cite>teste2</cite>.</p>
</div>
<div class="section" id="como-essa-magica-acontece">
<h2>Como essa mágica acontece?</h2>
<p>Para entender como isso acontece primeiro temos que entender como criamos um vetor em Shell Script, vamos criar um vetor que contem as strings <cite>a</cite>, <cite>b</cite> e <cite>c</cite> em nosso terminal:</p>
<div class="highlight"><pre><span></span>$ MEU_VETOR=('a' 'b' 'c')
</pre></div>
<p>Estou usando <cite>$</cite> somente para representar que estou digitando um comando em meu terminal do sistema, não é necessário você escrever este <cite>$</cite>. Repare que nem preciso criar um arquivo de Shell Script (.sh) para fazer isso, posso fazer diretamente no terminal.</p>
<p>Os elementos do vetor são separados por espaços, para pegar cada elemento separadamente podemos fazer assim:</p>
<div class="highlight"><pre><span></span>$ echo ${MEU_VETOR[0]}
a
$ echo ${MEU_VETOR[1]}
b
$ echo ${MEU_VETOR[2]}
c
</pre></div>
<p>Para pegar todos os elementos de uma vez podemos fazer:</p>
<div class="highlight"><pre><span></span>$ echo ${MEU_VETOR[@]}
a b c
</pre></div>
<p>E por fim para pegar a quantidade de elementos que temos neste vetor podemos fazer:</p>
<div class="highlight"><pre><span></span>$ echo ${#MEU_VETOR[@]}
3
</pre></div>
<p>Sabendo desses conceitos separadamente fica fácil iterar no vetor, veja só:</p>
<div class="highlight"><pre><span></span>$ for ((cont=0; cont<${#MEU_VETOR[@]}; cont++)); do echo "-> ${MEU_VETOR[$cont]}"; done
-> a
-> b
-> c
</pre></div>
<p>Coloquei uma setinha <cite>-></cite> no comando <cite>echo</cite> só para ficar mais bonito :), mas não é necessário.</p>
</div>
<div class="section" id="entendendo-o-awk-que-foi-usado-no-primeiro-exemplo">
<h2>Entendendo o awk que foi usado no primeiro exemplo</h2>
<p>O <a class="reference external" href="http://tldp.org/LDP/abs/html/awk.html">awk</a> é um comando muito poderoso do GNU/Linux e novamente me desculpem os puristas, mas pra mim é uma outra linguagem de programação, pois tem loops, condicionais e etc apesar de ter uma característica similar ao Shell Script em ser muito ruim para criar sistemas grandes, pois não possui estruturas de dados complexas.</p>
<p>No primeiro exemplo usei 0,00000001% do que o awk oferece, ele até é digno de outro post no futuro. Vamos as explicações, se você executar o awk de forma bem simplista no arquivo exemplo olhe o que vai acontecer:</p>
<div class="highlight"><pre><span></span>$ cat arquivo.csv # somente para vcs lembrarem da estrutura do arquivo
diretorios,status
teste1,false
teste2,false
$ awk -F"," '{print $1}' arquivo.csv
diretorios
teste1
teste2
</pre></div>
<p>Bem simples né? Ele usa como delimitador dos campos a vírgula devido ao parâmetro <cite>-F</cite> e faz um <cite>print</cite> da primeira coluna. Agora o legal é que da pra ignorar a primeira linha com o <cite>NR</cite> que é um nome reservado do awk que significa <cite>NUMBER ROW</cite>, veja só:</p>
<div class="highlight"><pre><span></span>$ awk -F"," '(NR>1) {print $1}' arquivo.csv
teste1
teste2
</pre></div>
<p>Agora é simples! É só jogar o resultado deste comando em um vetor e é só alegria.... veja:</p>
<div class="highlight"><pre><span></span>$ MEU_VETOR=( $(awk -F"," '(NR>1) {print $1}' arquivo.csv) )
teste1
teste2
</pre></div>
<p>O <cite>$(<comando>)</cite> é uma sintaxe do bash para executar um comando e trazer seu resultado para uma variável, que no caso nossa variável é o nosso vetor e justamente por isso preciso dos parênteses para especificar que é um vetor.</p>
</div>
<div class="section" id="conclusao">
<h2>Conclusão</h2>
<p>Este foi apenas um exemplo simples de como os vetores em Shell Script podem lhe ajudar no dia a dia, exemplos de outras coisas em que já precisei usá-los:</p>
<ul class="simple">
<li>Rodar vários comandos de banco de dados iterando comando a comando;</li>
<li>Copiar arquivos para diversas máquinas ao mesmo tempo por ssh iterando pelas máquinas;</li>
<li>Criar backup de diversos diretórios iterando um a um para realizar o backup.</li>
</ul>
<p>As opções são infinitas!</p>
<p>Como recomendações finais, se você gostou desse post <a class="reference external" href="https://www.youtube.com/watch?v=XBkBnKmu94U">clique aqui veja esta palestra do Aurélio Jargas no FISL 17</a>, é muito massa! Também deixo a dica do Canivete Suíço do Shell (Bash) também do Aurélio, <a class="reference external" href="http://aurelio.net/shell/canivete/">clique aqui</a> para dar uma conferida.</p>
<p>Conta aqui pra nós depois nos comments pra que você precisou usar ou está afim de usar Shell Script e seus vetores ;)</p>
<p>Flw!</p>
</div>
systemd um caso de amor e ódio2018-01-07T12:04:00-02:002018-01-07T12:04:00-02:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2018-01-07:/systemd-amor-e-odio.html<p class="first last">Por que amar e odiar o systemd?</p>
<p>Um fato é que na computação tudo muda a todo instante e mudanças sempre devem ser analisadas e estudadas antes de termos uma opinião.</p>
<p>Eu já odiei systemd pois não tinha parado para estudar e sempre tentava fazer as coisas utilizando o SysV Init e pensava que tudo ia funcionar, porém OS DOIS SÃO MUITO DIFERENTES! Não adianta ficar com raiva do systemd, basta entender que ele é uma outra coisa e que ele não é o SysV Init ai você começará a gostar dele :D.</p>
<p>Quando estudei para a minha primeira certificação <a class="reference external" href="http://www.lpi.org/">LPI</a> as distribuições GNU/Linux como um todo usavam o padrão de inicialização do sistema SysV Init, hoje isso mudou e a maioria das distribuições usam o padrão systemd.</p>
<div class="section" id="qual-a-diferenca">
<h2>Qual a diferença?</h2>
<p>Em termos técnicos de baixo nível eu não sei a diferença por que nunca parei para estudar profundamente isso (se alguém quiser rolar um papo a gente conversa nos comments abaixo do post), mas no nível superficial o que mudou foi:</p>
<ul class="simple">
<li>Arquivos de configuração;</li>
<li>Comandos.</li>
</ul>
<p>Achei umas referências bacanas nestes links para quem quiser se aprofundar mais o assunto:</p>
<ul class="simple">
<li><a class="reference external" href="https://www.ibm.com/developerworks/community/blogs/752a690f-8e93-4948-b7a3-c060117e8665/entry/comparativo_upstart_sysvinit_systemd_openrc?lang=en">Comparativo: upstart, sysvinit, systemd, openrc</a></li>
<li><a class="reference external" href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/752a690f-8e93-4948-b7a3-c060117e8665/entry/systemd_parte_1?lang=pt_br">Bê-á-bá do Systemd, parte 1</a></li>
<li><a class="reference external" href="https://www.digitalocean.com/community/tutorials/understanding-systemd-units-and-unit-files">Understanding Systemd Units and Unit Files</a></li>
<li><a class="reference external" href="http://stato.blog.br/wordpress/systemd-versus-sysvinit/">Systemd versus SysVinit</a></li>
</ul>
</div>
<div class="section" id="como-criar-um-servico">
<h2>Como criar um serviço?</h2>
<p>A um tempinho atrás precisei criar um serviço para o tsurud que é o <a class="reference external" href="https://pt.wikipedia.org/wiki/Daemon_(computa%C3%A7%C3%A3o)">daemon (se não sabe o que é daemon clica neste link)</a> do <a class="reference external" href="http://blog.abraseucodigo.com.br/como-criar-seu-paas-com-tsuru.html">Tsuru PaaS</a> pois quando fui subir o PaaS ele não iniciava esse processo sozinho e não achei isso muito legal. Para criar o serviço eu comecei criando o arquivo <cite>tsuru-server-api.service</cite> da seguinte forma:</p>
<div class="highlight"><pre><span></span># vim /etc/systemd/system/tsuru-server-api.service
[Unit]
Description=Tsuru (tsuru-server-api)
After=network.target
Requires=mongod.service redis.service
Documentation=https://tsuru.io/
[Service]
Type=simple
ExecStart=/usr/bin/tsurud api --config=/etc/tsuru/tsuru.conf
PIDFile=/var/run/tsuru-server-api.pid
Restart=always
User=tsuru
Group=tsuru
[Install]
WantedBy=default.target
</pre></div>
<p>Cada serviço é atrelado a uma Unit do systemd. Dito isso vamos a explicação.</p>
<p>Explicando:</p>
<ul class="simple">
<li>Unit: Define metadados sobre a Unit;</li>
<li>Description: Apenas uma descrição da sua Unit;</li>
<li>After: Diz que este serviço só subirá depois que subir network.target, pois é um serviço que precisa de rede funcionando;</li>
<li>Requires: Diz que se mongod.service ou redis.service falhar esse serviço também irá falhar, e ficará parado (não será iniciado);</li>
<li>Documentation: Uma url de documentação para o serviço;</li>
<li>Service: Define o serviço daquela unit;</li>
<li>Type: O tipo <cite>simple</cite> faz com que systemd considere que o serviço deve ser iniciado imediatamente (mais <a class="reference external" href="https://wiki.archlinux.org/index.php/Systemd_(Portugu%C3%AAs)#Tipo">opções aqui</a>)</li>
<li>ExecStart: Comando que deve iniciar o daemon em questão;</li>
<li>PIDFile: Arquivo que armazenará o <a class="reference external" href="https://pt.stackoverflow.com/questions/191815/o-que-seria-um-pid">PID (não sabe o que é PID clique aqui)</a> do processo;</li>
<li>Restart: Seguir a tabelinha contida <a class="reference external" href="https://www.freedesktop.org/software/systemd/man/systemd.service.html#Restart=">aqui</a> para entender melhor essa configuração.</li>
<li>User: Usuário dono do processo;</li>
<li>Group: Grupo dono do processo;</li>
<li>Install: Esta seção é opcional e é usada para definir o comportamento de uma unit;</li>
<li>WantedBy: Especifica como a unit deve ser habilitada, no caso está configurada da maneira default pelo valor <cite>default.target</cite>.</li>
</ul>
<p>Preferi colocar esse exemplo mais realista para vocês entenderem melhor, porém qualquer serviço pode ser criado baseado neste do Tsuru que eu criei, basta trocar os valores das configurações.</p>
</div>
<div class="section" id="comandos">
<h2>Comandos</h2>
<p>Depois de criar a sua unit com o seu service basta fazer reload do daemon do systemd para ele conseguir "enxergar" esse novo service:</p>
<div class="highlight"><pre><span></span>systemctl daemon-reload
</pre></div>
<p>Outros comandos úteis:</p>
<div class="highlight"><pre><span></span># iniciar um service
systemctl start <service>
# ver status do service
systemctl status <service>
# parar um service
systemctl stop <service>
# listar dependências de um service
systemctl list-dependencies tsuru-server-api
# re-habilitar uma unit que foi modificada
systemctl reenable <service>
# visualizar todos os services do sistema
systemctl -a
</pre></div>
<p>Últimas referências:</p>
<ul class="simple">
<li><a class="reference external" href="https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/System_Administrators_Guide/sect-Managing_Services_with_systemd-Unit_Files.html#sect-Managing_Services_with_systemd-Unit_File_Create">9.6.2. Creating Custom Unit Files</a></li>
<li><a class="reference external" href="https://superuser.com/questions/955922/enabled-systemd-unit-does-not-start-at-boot">enabled systemd unit does not start at boot</a></li>
</ul>
<p>Gostou? Deixa seu comment ai ;)</p>
<p>Flw!</p>
</div>
Ponteiros são legais2018-01-06T19:03:00-02:002018-01-06T19:03:00-02:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2018-01-06:/ponteiros-sao-legais.html<p class="first last">Veja aqui como ponteiros em Golang são legais!</p>
<p>A pouco comecei a estudar a <a class="reference external" href="https://golang.org/">linguagem Go</a> e como comecei bem no nível iniciante eu estou estudando os tipos e as estruturas de dados da linguagem. Achei bem legal o funcionamento da passagem de valor para as funções, TODO valor passado por padrão para uma função não altera o "objeto" original. Hein? Não entendeu? Vamos lá...</p>
<div class="section" id="exemplo-de-passagem-de-valores-para-uma-funcao">
<h2>Exemplo de passagem de valores para uma função</h2>
<p>Imagine que você vai fazer uma função bem bobinha que incrementa números, você teria algo assim:</p>
<div class="highlight"><pre><span></span><span class="n">package</span> <span class="n">main</span>
<span class="kn">import</span> <span class="s2">"fmt"</span>
<span class="n">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">number</span> <span class="o">:=</span> <span class="mi">10</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s2">"number: %p - %+v</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="o">&</span><span class="n">number</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="n">number</span> <span class="o">=</span> <span class="n">increment</span><span class="p">(</span><span class="n">number</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s2">"number: %p - %+v</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="o">&</span><span class="n">number</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">func</span> <span class="n">increment</span><span class="p">(</span><span class="n">number</span> <span class="nb">int</span><span class="p">,</span> <span class="n">numberIncrement</span> <span class="nb">int</span><span class="p">)</span> <span class="nb">int</span> <span class="p">{</span>
<span class="n">number</span> <span class="o">=</span> <span class="n">number</span> <span class="o">+</span> <span class="n">numberIncrement</span>
<span class="k">return</span> <span class="n">number</span>
<span class="p">}</span>
</pre></div>
<p>Executando este código teríamos uma saída assim:</p>
<div class="highlight"><pre><span></span>number: 0xc42001c0d0 - 10
number: 0xc42001c0d0 - 20
</pre></div>
<p>Usei o <cite>%p</cite> para exibir o endereço de memória da variável number. Agora veja que curioso quando exibimos o endereço de memória dentro da função <cite>increment</cite>:</p>
<div class="highlight"><pre><span></span><span class="n">package</span> <span class="n">main</span>
<span class="kn">import</span> <span class="s2">"fmt"</span>
<span class="n">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">number</span> <span class="o">:=</span> <span class="mi">10</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s2">"number: %p - %+v</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="o">&</span><span class="n">number</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="n">number</span> <span class="o">=</span> <span class="n">increment</span><span class="p">(</span><span class="n">number</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s2">"number: %p - %+v</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="o">&</span><span class="n">number</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">func</span> <span class="n">increment</span><span class="p">(</span><span class="n">number</span> <span class="nb">int</span><span class="p">,</span> <span class="n">numberIncrement</span> <span class="nb">int</span><span class="p">)</span> <span class="nb">int</span> <span class="p">{</span>
<span class="n">number</span> <span class="o">=</span> <span class="n">number</span> <span class="o">+</span> <span class="n">numberIncrement</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s2">"increment: %p</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="o">&</span><span class="n">number</span><span class="p">)</span>
<span class="k">return</span> <span class="n">number</span>
<span class="p">}</span>
</pre></div>
<p>Saída:</p>
<div class="highlight"><pre><span></span>number: 0xc42001c0d0 - 10
increment: 0xc42001c0e8
number: 0xc42001c0d0 - 20
</pre></div>
<p>Ou seja, o compilador criará uma nova variável em um novo endereço de memória realizará o cálculo dentro da função <cite>increment</cite> SEM ALTERAR o valor original da variável <cite>number</cite> que está na função <cite>main</cite>. Bem interessante isso né?</p>
</div>
<div class="section" id="como-fazer-com-que-o-go-use-a-mesma-variavel-e-economize-memoria">
<h2>Como fazer com que o Go use a mesma variável e economize memória?</h2>
<p>A resposta é simples, ponteiros! E o uso também é muito simples, apesar de o conceito de ponteiros assustar muita gente que aprendeu C no passado :D.</p>
<p>Vamos dar uma pequena mudada no programinha:</p>
<div class="highlight"><pre><span></span><span class="n">package</span> <span class="n">main</span>
<span class="kn">import</span> <span class="s2">"fmt"</span>
<span class="n">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">number</span> <span class="o">:=</span> <span class="mi">10</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s2">"number: %p - %+v</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="o">&</span><span class="n">number</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="n">increment</span><span class="p">(</span><span class="o">&</span><span class="n">number</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s2">"number: %p - %+v</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="o">&</span><span class="n">number</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">func</span> <span class="n">increment</span><span class="p">(</span><span class="n">number</span> <span class="o">*</span><span class="nb">int</span><span class="p">,</span> <span class="n">numberIncrement</span> <span class="nb">int</span><span class="p">)</span> <span class="p">{</span>
<span class="o">*</span><span class="n">number</span> <span class="o">=</span> <span class="o">*</span><span class="n">number</span> <span class="o">+</span> <span class="n">numberIncrement</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s2">"increment: %p</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
<span class="p">}</span>
</pre></div>
<p>Agora mudamos, não vamos mais mandar o valor de <cite>number</cite>, mas vamos mandar o endereço de memória de <cite>number</cite> para a função <cite>increment</cite>, ou seja, passamos um ponteiro de <cite>number</cite> para a função <cite>increment</cite>. Fazendo isso a função irá incrementar o valor diretamente em <cite>number</cite>, no mesmo da função <cite>main</cite>.</p>
<p>Veja a execução do código acima:</p>
<div class="highlight"><pre><span></span>number: 0xc42009a000 - 10
increment: 0xc42009a000
number: 0xc42009a000 - 20
</pre></div>
<p>Bacana né? Gostou? Deixa seu comentário ai ;)</p>
<p>Flw!</p>
</div>
Não guarde conhecimento! Compartilhe!2018-01-03T23:52:00-02:002018-01-03T23:52:00-02:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2018-01-03:/nao-guarde-conhecimento-compartilhe.html<p class="first last">Compartilhe! Compartilhe! E compartilhe!</p>
<p>Estive pensando muito sobre as minhas atitudes como desenvolvedor de software e também como pessoa. Cheguei a conclusão que eu sou um cara perfeccionista a um nível muito extremo e gostaria de explicar por que isso não deve acontecer com as pessoas de modo geral.</p>
<div class="section" id="feito-e-melhor-que-perfeito">
<h2>Feito é melhor que perfeito</h2>
<p>Sempre quando eu vou escrever um post, ou qualquer outra coisa que fique pública na internet eu penso em MUITA coisa:</p>
<ul class="simple">
<li>O texto está bem escrito gramaticalmente?</li>
<li>O texto está bem escrito ortograficamente?</li>
<li>O pessoal vai se interessar pelo assunto?</li>
<li>A linguagem está clara e objetiva o suficiente?</li>
</ul>
<p>Essas perguntas te TRAVAM e fazem com que você demore mais a compartilhar o conhecimento que você tem. Cheguei a esta conclusão a pouco pois guardei um <a class="reference external" href="http://blog.abraseucodigo.com.br/melhorando-seus-logs-com-elk.html">post</a> deste blog por 1 ANO! 1 ANO! Isso é MUITO tempo! Eu poderia ter ajudado muitas pessoas neste 1 ano parado :(.</p>
<p>Eis que me lembrei do meu blog antigo, que era feito no blogger e ainda vive <a class="reference external" href="http://abraseucodigo.blogspot.com.br/">até hoje</a>, olhei mais uma vez pra ele e pensei "porra Rafael, naquela época você compartilhava conhecimento baseado em porra nenhuma", e ao meu ver esse é o espírito de um blog que talvez eu tenha esquecido, o espírito é o que sempre ouço na <a class="reference external" href="https://cargobr.com/">empresa que trabalho</a> atualmente, que o "feito é melhor que o perfeito!".</p>
</div>
<div class="section" id="filosofando-sobre-o-que-acontece-com-o-conhecimento">
<h2>Filosofando sobre o que acontece com o conhecimento</h2>
<p>Fiz um post a um tempo atrás sobre <a class="reference external" href="http://blog.abraseucodigo.com.br/instalando-qualquer-versao-do-python-no-linux-macosx-utilizando-pyenv.html">como instalar Python</a> e é um dos posts que mais tem movimento neste blog e por que isso acontece? Por que ele é um post perfeito? NÃO! Porque ele é um post "humano" que ajuda as pessoas e as pessoas fazem comentários embaixo dele usando o disqus que ajuda outras pessoas que lêem o post e isso é maravilhoso!</p>
<p>Pois o blog acaba não sendo construído só por mim, eu aprendo com os comentários e as pessoas do comentário ensinam outras pessoas e o conhecimento se propaga, e isso é maravilhoso.</p>
</div>
<div class="section" id="conclusao">
<h2>Conclusão</h2>
<p>Não se apegue a falsas ilusões de que você fará um texto perfeito, um blog não tem foco acadêmico e nem o mesmo rigor (digo isso pois estou cursando como aluno especial o programa de mestrado da UFSCar de Sorocaba), portanto ESCREVA, e se não quiser escrever PALESTRE, e se não quiser palestrar FAÇA VÍDEOS e publique na internet, porém sempre compartilhe conhecimento pois você vai aprender mais com isso.</p>
<p>Siga a dica do <a class="reference external" href="https://youtu.be/-ANx41sZNIQ?t=59s">ET Bilú</a> e busque conhecimento mas não se esqueça de compartilhar ;).</p>
<img alt="bilu" src="images/nao-guarde-conhecimento-compartilhe/01.jpg" />
<p>PS: A parte do Bilú foi só pela zueira mesmo hahaha, se não conhecem o ET Bilú vale a pena clicar no link.</p>
</div>
Melhorando seus logs com Elasticsearch + Kibana + Logstash2018-01-03T23:50:00-02:002018-01-03T23:50:00-02:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2018-01-03:/melhorando-seus-logs-com-elk.html<p class="first last">Deixe seus logs mais legíveis, rastreáveis e bonitos</p>
<p>Em meu último post <a class="reference external" href="http://blog.abraseucodigo.com.br/a-importancia-de-um-log.html">A importância de um log</a> expliquei um pouco porque é importante ter logs em uma aplicação.</p>
<p>Neste post demonstrarei como centralizar seus logfiles usando a stack ELK (Elasticsearch + Logstash + Kibana). Existem ferramentas que já salvam o log da aplicação diretamente no Logstash, este não será o foco deste post, quem sabe um próximo ;).</p>
<p>AVISO IMPORTANTE: Faz algum tempo que venho guardando este post, e mesmo ele estando incompleto (me desculpem pelo perfeccionismo) já vou postar logo, pois eu mesmo sinto falta de ler o que está aqui, já precisei subir essa stack 2 vezes e tive que consultar um arquivo markdown meio feio. Então colaborem! Quando quiserem me perguntem qualquer coisa sobre o assunto e estarei disposto a ajudar e a melhorar o que escrevi aqui.</p>
<div class="section" id="antes-de-mais-nada">
<h2>Antes de mais nada...</h2>
<p>O que é Elastic? A <a class="reference external" href="https://www.elastic.co/">Elastic</a> é uma empresa sensacional (opinião minha) que criou várias ferramentas interessantes para facilitar a vida do desenvolvedor :). Os produtos que serão abordados neste post são:</p>
<ul class="simple">
<li>Elasticsearch: Uma ferramenta muito utilizada para indexar/armazenar dados e devolvê-los de forma rápida a quem consulta estes dados através de uma API bem poderosa;</li>
<li>Logstash: Recebe dados de diversas fontes diferentes, simultaneamente, processa e armazena estes dados em algum <a class="reference external" href="https://www.elastic.co/guide/en/logstash/current/output-plugins.html">lugar</a>, funciona como se fosse analogamente um "roteador de logs";</li>
<li>Kibana: Nosso visualizador de logs que consome a api do Elasticsearch e mostra essa informação em forma gráfica ou textual;</li>
<li>Filebeat: Um client que lê logs de algum host e os envia para o logstash.</li>
</ul>
</div>
<div class="section" id="instalar-jdk">
<h2>Instalar JDK</h2>
<div class="highlight"><pre><span></span>sudo apt-get install openjdk-8-jre-headless
</pre></div>
<p>Elastic search 2.x não funciona com Java9.</p>
<p>Referência: <a class="reference external" href="https://github.com/elastic/elasticsearch/issues/18761">https://github.com/elastic/elasticsearch/issues/18761</a></p>
</div>
<div class="section" id="instalar-elasticsearch">
<h2>Instalar Elasticsearch</h2>
<div class="highlight"><pre><span></span>wget -qO - https://packages.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
echo "deb https://packages.elastic.co/elasticsearch/2.x/debian stable main" | sudo tee -a /etc/apt/sources.list.d/elasticsearch-2.x.list
apt-get update && sudo apt-get install elasticsearch
update-rc.d elasticsearch defaults 95 10
/bin/systemctl daemon-reload
/bin/systemctl enable elasticsearch.service
</pre></div>
<p>Configurar arquivo <cite>/etc/elasticsearch/elasticsearch.yml</cite> com a entrada:</p>
<div class="highlight"><pre><span></span>network.host: localhost
</pre></div>
<p>Isso serve para não abrir o elastic search para fora, senão outras pessoas podem controlar o cluster.</p>
<p>Referências:</p>
<ul class="simple">
<li><a class="reference external" href="https://www.digitalocean.com/community/tutorials/how-to-install-elasticsearch-logstash-and-kibana-elk-stack-on-ubuntu-14-04">https://www.digitalocean.com/community/tutorials/how-to-install-elasticsearch-logstash-and-kibana-elk-stack-on-ubuntu-14-04</a></li>
<li><a class="reference external" href="https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-repositories.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-repositories.html</a></li>
</ul>
</div>
<div class="section" id="instalar-kibana">
<h2>Instalar Kibana</h2>
<div class="highlight"><pre><span></span>echo "deb https://packages.elastic.co/kibana/4.6/debian stable main" | sudo tee -a /etc/apt/sources.list.d/kibana.list
apt-get update && sudo apt-get install kibana
update-rc.d kibana defaults 95 10
/bin/systemctl daemon-reload
/bin/systemctl enable kibana.service
</pre></div>
<p>Configurar arquivo <cite>/opt/kibana/config/kibana.yml</cite> com a entrada:</p>
<div class="highlight"><pre><span></span>server.host: "localhost"
</pre></div>
<p>Para deixar Kibana acessível somente a localhost, para sair externamente vamos usar um proxy reverso no Nginx.</p>
<p>Referência: <a class="reference external" href="https://www.elastic.co/guide/en/kibana/current/setup-repositories.html">https://www.elastic.co/guide/en/kibana/current/setup-repositories.html</a></p>
</div>
<div class="section" id="instalar-nginx">
<h2>Instalar Nginx</h2>
<div class="highlight"><pre><span></span>apt-get install nginx apache2-utils
</pre></div>
<p>Gerar senha/usuário para acesso ao painel do kibana:</p>
<div class="highlight"><pre><span></span>htpasswd -c /etc/nginx/htpasswd.users kibanaadmin
unlink /etc/nginx/sites-enabled/default
</pre></div>
<p>Configurar/criar o arquivo <cite>/etc/nginx/sites-available/kibana</cite>:</p>
<div class="highlight"><pre><span></span>server {
listen 80;
# server_name example.com;
auth_basic "Restricted Access";
auth_basic_user_file /etc/nginx/htpasswd.users;
location / {
proxy_pass http://localhost:5601;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
</pre></div>
<p>Criar link simbólico:</p>
<div class="highlight"><pre><span></span>ln -sf /etc/nginx/sites-available/kibana /etc/nginx/sites-enabled/kibana
</pre></div>
<p>Reiniciar o serviço do Nginx para aplicar nova configuração:</p>
<div class="highlight"><pre><span></span>sudo service nginx restart
</pre></div>
<p>Esta configuração faz com que o Nginx se comporte como um <cite>websocket-proxy</cite> em que após a autenticação o usuário seja redirecionado para o painel do Kibana.</p>
<p>Após fazer esta configuração você poderá testar no seu browser se o painel está acessível da forma que você configurou:</p>
<img alt="login painel kibana" src="images/melhorando-seus-logs-com-elk/01.png" />
<img alt="painel kibana" src="images/melhorando-seus-logs-com-elk/02.png" />
<p>Referências:</p>
<ul class="simple">
<li><a class="reference external" href="https://www.nginx.com/blog/websocket-nginx/">https://www.nginx.com/blog/websocket-nginx/</a></li>
<li><a class="reference external" href="https://www.digitalocean.com/community/tutorials/how-to-install-elasticsearch-logstash-and-kibana-elk-stack-on-ubuntu-14-04">https://www.digitalocean.com/community/tutorials/how-to-install-elasticsearch-logstash-and-kibana-elk-stack-on-ubuntu-14-04</a></li>
</ul>
</div>
<div class="section" id="instalar-logstash">
<h2>Instalar logstash</h2>
<div class="highlight"><pre><span></span>wget -qO - https://packages.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
echo "deb https://packages.elastic.co/logstash/2.4/debian stable main" | sudo tee -a /etc/apt/sources.list
sudo apt-get update && sudo apt-get install logstash
</pre></div>
<p>Logstash está instalado mas ainda não foi configurado, será configurado mais abaixo neste mesmo post. :)</p>
<p>Referência:</p>
<ul class="simple">
<li><a class="reference external" href="https://www.elastic.co/guide/en/logstash/current/installing-logstash.html">https://www.elastic.co/guide/en/logstash/current/installing-logstash.html</a></li>
</ul>
</div>
<div class="section" id="gerando-certificados-ssl">
<h2>Gerando certificados SSL</h2>
<p>Para começar a usar o Filebeat nos nossos servers clientes "conectados" ao nosso servidor Elastic, nós precisamos criar um par de certificados SSL. O certificado é usado pelo Filebeat para verificar a identidade no servidor Elastic. Crie os diretórios segundo os comandos abaixo:</p>
<div class="highlight"><pre><span></span>mkdir -p /etc/pki/tls/certs
mkdir /etc/pki/tls/private
</pre></div>
<p>Nós faremos a configuração baseada em IP pois vamos partir do pressuposto que você não tenha DNS caso você esteja usando DNS (com resolução de nomes tudo bonitinho) nos seus servidores então siga os passos da <a class="reference external" href="https://www.digitalocean.com/community/tutorials/how-to-install-elasticsearch-logstash-and-kibana-elk-stack-on-ubuntu-14-04#generate-ssl-certificates">Option 2</a> do tutorial da Digital Ocean.</p>
<p>Vamos adicionar nosso ip privado ao subjectAltName (SAN), para fazer isso vamos editar o arquivo <cite>/etc/ssl/openssl.cnf</cite>, encontre a sessão <cite>[ v3_ca ]</cite> e adicione seu ip conforme mostrado abaixo:</p>
<div class="highlight"><pre><span></span>subjectAltName = IP: ELK_server_private_IP
</pre></div>
<p>Agora gere os certificados e chaves privadas nos locais apropriados (/etc/pki/tls) com os comandos abaixo:</p>
<div class="highlight"><pre><span></span>cd /etc/pki/tls
sudo openssl req -config /etc/ssl/openssl.cnf -x509 -days 3650 -batch -nodes -newkey rsa:2048 -keyout private/logstash-forwarder.key -out certs/logstash-forwarder.crt
</pre></div>
<p>O certificado <cite>logstash-forwarder.crt</cite> será copiado para todos os servidores que irão enviar log para o Logstash.</p>
</div>
<div class="section" id="configurando-logstash">
<h2>Configurando Logstash</h2>
<p>A configuração é feita em formato JSON, e fica em <cite>/etc/logstash/conf.d</cite>. A configuração consiste em três seções: entradas (inputs), filtros (filters) e saídas (outputs).</p>
<p>Vamos criar um arquivo de configuração chamado <cite>/etc/logstash/conf.d/02-beats-input.conf</cite> e setar nossa entrada <cite>Filebeat</cite>:</p>
<div class="highlight"><pre><span></span>input {
beats {
port => 5044
ssl => true
ssl_certificate => "/etc/pki/tls/certs/logstash-forwarder.crt"
ssl_key => "/etc/pki/tls/private/logstash-forwarder.key"
}
}
</pre></div>
<p>Esta configuração especifica que o entrada do <cite>beats</cite> irá escutar na porta 5044 e irá usar nosso certificado e chave privada criados anteriormente.</p>
<p>Agora vamos criar uma configuração chamada <cite>/etc/logstash/conf.d/10-syslog-filter.conf</cite>, onde nós vamos adicionar um <cite>filter</cite> para as nossas mensagens de syslog:</p>
<div class="highlight"><pre><span></span>filter {
if [type] == "syslog" {
grok {
match => { "message" => "%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{DATA:syslog_program}(?:\[%{POSINT:syslog_pid}\])?: %{GREEDYDATA:syslog_message}" }
add_field => [ "received_at", "%{@timestamp}" ]
add_field => [ "received_from", "%{host}" ]
}
syslog_pri { }
date {
match => [ "syslog_timestamp", "MMM d HH:mm:ss", "MMM dd HH:mm:ss" ]
}
}
}
</pre></div>
<p>Este <cite>filter</cite> analisa os logs rotulados como tipo "syslog" e tenta usar <cite>grok</cite> para parsear os logs entrantes para deixá-los estruturados e <cite>queryáveis</cite> (pesquisáveis via query).</p>
<p>Agora por fim vamos criar uma configuração chamada <cite>/etc/logstash/conf.d/30-elasticsearch-output.conf</cite>:</p>
<div class="highlight"><pre><span></span>output {
elasticsearch {
hosts => ["localhost:9200"]
sniffing => true
manage_template => false
index => "%{[@metadata][beat]}-%{+YYYY.MM.dd}"
document_type => "%{[@metadata][type]}"
}
}
</pre></div>
<p>Este <cite>output</cite> basicamente configura o Logstash para armazenar os dados do beats no Elasticsearch que está rodando em <cite>localhost:9200</cite> e indexa por nome posteriormente.</p>
<p>Agora vamos testar a configuração criada com o comando abaixo:</p>
<div class="highlight"><pre><span></span>service logstash configtest
</pre></div>
<p>Se a configuração estiver OK então prossiga com os comando abaixo:</p>
<div class="highlight"><pre><span></span>service logstash restart
update-rc.d logstash defaults 95 10
</pre></div>
</div>
<div class="section" id="baixar-e-instalar-dashboards-do-kibana">
<h2>Baixar e instalar Dashboards do Kibana</h2>
<p>Elastic provê diversos exemplos de Dashboard do Kibana e padrões de index do Beats para te ajudar a iniciar com o Kibana.</p>
<p>Baixe os dashboards no diretório home:</p>
<div class="highlight"><pre><span></span># cd ~
# curl -L -O http://download.elastic.co/beats/dashboards/beats-dashboards-1.3.1.zip
# apt-get install unzip
# unzip beats-dashboards-1.3.1.zip
# cd beats-dashboards-1.3.1/
# ./load.sh
</pre></div>
<p>Agora alguns indexes vão estar disponíveis no painel do Kibana:</p>
<img alt="indexes painel kibana" src="images/melhorando-seus-logs-com-elk/03.png" />
<p>Referência:</p>
<ul class="simple">
<li><a class="reference external" href="https://www.elastic.co/guide/en/beats/libbeat/current/load-kibana-dashboards.html">https://www.elastic.co/guide/en/beats/libbeat/current/load-kibana-dashboards.html</a></li>
</ul>
</div>
<div class="section" id="adicionando-o-certificado-nos-clientes">
<h2>Adicionando o certificado nos clientes</h2>
<p>Copie o certificado para o servidor cliente:</p>
<div class="highlight"><pre><span></span>scp /etc/pki/tls/certs/logstash-forwarder.crt user@client_server_private_address:/tmp
</pre></div>
<p>Trocar <cite>user</cite> por um usuário válido e <cite>client_server_private_address</cite> por um IP privado válido.</p>
<p>No cliente mova o certificado para o local correto:</p>
<div class="highlight"><pre><span></span>mkdir -p /etc/pki/tls/certs
cp /tmp/logstash-forwarder.crt /etc/pki/tls/certs/
</pre></div>
</div>
<div class="section" id="instalando-filebeat-nos-clientes">
<h2>Instalando Filebeat nos clientes</h2>
<p><a class="reference external" href="https://www.elastic.co/guide/en/beats/libbeat/1.3/setup-repositories.html">https://www.elastic.co/guide/en/beats/libbeat/1.3/setup-repositories.html</a></p>
<div class="highlight"><pre><span></span>curl https://packages.elasticsearch.org/GPG-KEY-elasticsearch | sudo apt-key add -
echo "deb https://packages.elastic.co/beats/apt stable main" | sudo tee -a /etc/apt/sources.list.d/beats.list
sudo apt-get update && sudo apt-get install filebeat
sudo update-rc.d filebeat defaults 95 10
</pre></div>
</div>
<div class="section" id="configurando-filebeat-nos-clientes">
<h2>Configurando Filebeat nos clientes</h2>
<p>Agora precisamos configurar o Filebeat para que ele conecte no nosso servidor Elastic. Vamos configurar o Filebeat, a configuração fica em <cite>/etc/filebeat/filebeat.yml</cite>.</p>
<p><strong>Nota importante: O arquivo de configuração do Filebeat é em formato YAML a indentação é muito importante! Se a configuração não for indentada adequadamente os procedimentos abaixo poderão não funcionar.</strong></p>
<p>Abaixo da seção <cite>prospectors</cite> do arquivo podemos ver a seção <cite>paths</cite>:</p>
<div class="highlight"><pre><span></span>############################# Filebeat ######################################
filebeat:
# List of prospectors to fetch data.
prospectors:
# Each - is a prospector. Below are the prospector specific configurations
-
# Paths that should be crawled and fetched. Glob based paths.
# To fetch all ".log" files from a specific level of subdirectories
# /var/log/*/*.log can be used.
# For each file found under this path, a harvester is started.
# Make sure not file is defined twice as this can lead to unexpected behaviour.
paths:
- /var/log/*.log
#- c:\programdata\elasticsearch\logs\*
</pre></div>
<p>A configuração padrão faz com que o Filebeat receba todos os logs de <cite>/var/log/</cite> justamente por isso é usado o wildcard <cite>*.log</cite>. Para que não sejam enviados todos os logs podemos especificar os arquivos desejados, como por exemplo o arquivo <cite>auth.log</cite> que armazena registros de autenticação do sistema operacional. Vamos modificar este trecho do arquivo:</p>
<div class="highlight"><pre><span></span>############################# Filebeat ######################################
filebeat:
# List of prospectors to fetch data.
prospectors:
# Each - is a prospector. Below are the prospector specific configurations
-
# Paths that should be crawled and fetched. Glob based paths.
# To fetch all ".log" files from a specific level of subdirectories
# /var/log/*/*.log can be used.
# For each file found under this path, a harvester is started.
# Make sure not file is defined twice as this can lead to unexpected behaviour.
paths:
- /var/log/auth.log
</pre></div>
<p>Agora vamos procurar uma linha onde tem um parâmetro chamado <cite>document_type</cite>:</p>
<div class="highlight"><pre><span></span># Type to be published in the 'type' field. For Elasticsearch output,
# the type defines the document type these entries should be stored
# in. Default: log
#document_type: log
</pre></div>
<p>Altere esta linha para:</p>
<div class="highlight"><pre><span></span># Type to be published in the 'type' field. For Elasticsearch output,
# the type defines the document type these entries should be stored
# in. Default: log
document_type: syslog
</pre></div>
<p>Isso especifica para o servidor Elastic que este tipo de log se refere ao <cite>syslog</cite>. Podem ser criados outros <cite>prospectors</cite> com diferentes tipos de log, mas cuidado com a indentação do arquivo sempre!</p>
<p>Agora próximo da seção <cite>output</cite> na seção <cite>#logstash</cite> temos as seguintes linhas:</p>
<div class="highlight"><pre><span></span># Configure what outputs to use when sending the data collected by the beat.
# Multiple outputs may be used.
output:
.. linhas omitidas ..
### Logstash as output
#logstash:
# The Logstash hosts
#hosts: ["localhost:5044"]
# Number of workers per Logstash host.
#worker: 1
</pre></div>
<p>Vamos descomentar a linha da seção do <cite>logstash</cite> para habilitar esta seção e modificar o parâmetro <cite>hosts</cite>:</p>
<div class="highlight"><pre><span></span># Configure what outputs to use when sending the data collected by the beat.
# Multiple outputs may be used.
output:
.. linhas omitidas ..
### Logstash as output
logstash:
# The Logstash hosts
hosts: ["ELK_server_private_IP:5044"]
# Number of workers per Logstash host.
#worker: 1
</pre></div>
<p>Na seção <cite>tls</cite> temos o seguinte trecho:</p>
<div class="highlight"><pre><span></span>#tls:
# List of root certificates for HTTPS server verifications
#certificate_authorities: ["/etc/pki/root/ca.pem"]
# Certificate for TLS client authentication
#certificate: "/etc/pki/client/cert.pem"
</pre></div>
<p>Iremos descomentar a linha <cite>tls</cite> para habilitarmos a seção e vamos acrescentar o nosso certificado no parâmetro <cite>certificate_authorities</cite>:</p>
<div class="highlight"><pre><span></span>tls:
# List of root certificates for HTTPS server verifications
certificate_authorities: ["/etc/pki/tls/certs/logstash-forwarder.crt"]
# Certificate for TLS client authentication
#certificate: "/etc/pki/client/cert.pem"
</pre></div>
<p>Agora execute os comandos abaixo antes de continuar:</p>
<div class="highlight"><pre><span></span>sudo service filebeat restart
</pre></div>
</div>
<div class="section" id="testando-a-instalacao-do-filebeat">
<h2>Testando a instalação do Filebeat</h2>
<p>Descubra seus indexes:</p>
<div class="highlight"><pre><span></span>curl -X GET http://localhost:9200/_cat/indices?v
</pre></div>
<p>Consulte dados dos nós do elasticsearch indexes:</p>
<div class="highlight"><pre><span></span>curl -X GET http://localhost:9200/_cat/nodes?v
</pre></div>
<p>Consulte a saúde do cluster:</p>
<div class="highlight"><pre><span></span>curl -X GET http://localhost:9200/_cat/health?v
</pre></div>
<p>Apagando indexes:</p>
<div class="highlight"><pre><span></span>curl -X DELETE "http://localhost:9200/*meta*"
</pre></div>
<p>Consulte dados dos seus indexes:</p>
<div class="highlight"><pre><span></span>curl -X GET 'http://localhost:9200/filebeat-*/_search?pretty'
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 25,
"successful" : 25,
"failed" : 0
},
"hits" : {
"total" : 1342,
"max_score" : 1.0,
"hits" : [ {
"_index" : "filebeat-2016.10.12",
"_type" : "syslog",
"_id" : "AVfTM99Ff0lITMzrGB2j",
"_score" : 1.0,
"_source" : {
"message" : "Oct 12 11:37:06 bloodmary mate-screensaver-dialog: gkr-pam: unlocked login keyring",
"@version" : "1",
"@timestamp" : "2016-10-12T15:37:06.000Z",
"source" : "/var/log/auth.log",
"offset" : 1063,
"fields" : null,
"beat" : {
"hostname" : "bloodmary",
"name" : "bloodmary"
},
"type" : "syslog",
"input_type" : "log",
"count" : 1,
"host" : "bloodmary",
"tags" : [ "beats_input_codec_plain_applied" ],
"syslog_timestamp" : "Oct 12 11:37:06",
"syslog_hostname" : "bloodmary",
"syslog_program" : "mate-screensaver-dialog",
"syslog_message" : "gkr-pam: unlocked login keyring",
"received_at" : "2016-10-17T15:12:51.070Z",
"received_from" : "bloodmary",
"syslog_severity_code" : 5,
"syslog_facility_code" : 1,
"syslog_facility" : "user-level",
"syslog_severity" : "notice"
}
},
... linhas omitidas...
</pre></div>
<p>Se esta consulta dos indexes trouxe 0 registros então Elasticsearch não está recebendo/indexando seus logs como deveria.</p>
</div>
<div class="section" id="setando-seu-index-principal">
<h2>Setando seu index principal</h2>
<p>Para poder visualizar seus logs no menu Discover do Kibana basta setar seu index principal conforme mostrando no gif abaixo:</p>
<img alt="setando index principal" src="images/melhorando-seus-logs-com-elk/04.gif" />
<ul class="simple">
<li>Esta imagem foi copiada do tutorial da digital ocean a qual esse post se baseou</li>
</ul>
<p>Feito isso agora basta criar seus dashboards personalizados e trabalhar com queries para analisar seus logs :)... quem sabe em um futuro próximo eu posto sobre isso também, mas pra você não ficar ai esperando leia a documentação da Elastic, pois é muito boa! Segue o link da doc oficial para continuar os estudos:</p>
<ul class="simple">
<li><a class="reference external" href="https://www.elastic.co/guide/index.html">https://www.elastic.co/guide/index.html</a></li>
</ul>
</div>
<div class="section" id="em-caso-de-erros">
<h2>Em caso de erros</h2>
<p>Depurar filebeat:</p>
<div class="highlight"><pre><span></span>filebeat -e -v -d '*' -c /etc/filebeat/filebeat.yml
</pre></div>
<p>No meu notebook deu problema pois o filebeat tentava bater no elasticsearch pela localhost:9200 e dava erro, removi essa conf e tudo ficou bem.</p>
<p>Pela minha análise preliminar se ele não alcança algum host ele não sobe log pra nenhum.</p>
<p>Para depurar a conexão SSL: <a class="reference external" href="https://www.elastic.co/guide/en/beats/filebeat/current/configuring-tls-logstash.html">https://www.elastic.co/guide/en/beats/filebeat/current/configuring-tls-logstash.html</a></p>
</div>
A importância de um log2017-01-14T10:43:00-02:002017-01-14T10:43:00-02:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2017-01-14:/a-importancia-de-um-log.html<p class="first last">O que é log? E pra que logar?</p>
<p>Imagine que você criou uma aplicação Web (independente da linguagem de programação) e que durante o seu desenvolvimento você usou <a class="reference external" href="https://pt.wikipedia.org/wiki/Test_Driven_Development">TDD</a> bem certinho, como deve ser. Usando <a class="reference external" href="https://en.wikipedia.org/wiki/Code_coverage">testes de cobertura</a> junto ao TDD você consegue garantir que um determinado percentual do seu código está assegurado pelos testes que você escreveu, e isso é lindo!</p>
<p>Porém o que você não consegue garantir com TDD é se o teu usuário final está acessando sua aplicação da maneira que você programou para ele acessar, exceções sempre vão acontecer meu amigo! Esta é a vida dura de um desenvolvedor.</p>
<p>Seja por um desvio de comunicação entre quem programa e quem irá usar o programa, ou seja por um deslize qualquer que você como programador tenha cometido, exceções sempre irão acontecer, não adianta falar que não!</p>
<p>Agora cabe a você entender e tratar estas exceções, mas como? Sua aplicação está lá longe de você, rodando em alguma máquina em alguma Cloud XYZ e você está aqui "cego", você não vê as exceções acontecerem e quando elas acontecem, quem vê geralmente é o teu cliente, e provavelmente quando ele ver a exceção acontecer isso irá gerar insatisfação e até falta de segurança pra ele.</p>
<p>Como resolver isso? Como visualizar suas exceções para que elas não aconteçam mais? A resposta é simples meu amigo! Faça com que sua aplicação gere logs!</p>
<div class="section" id="o-que-e-um-log">
<h2>O que é um log?</h2>
<p>Log por essência é um arquivo de texto puro, com linhas, onde cada linha tem hora e data de uma ação que tem determinada importância na aplicação.</p>
<p>Hoje existem formas mais legais do que simplesmente armazenar em um arquivo texto, dentre as opções que temos você pode mandar estes logs para o <a class="reference external" href="https://logentries.com/">Logentries</a>, podemos mandar logs como se fossem mensagens para o <a class="reference external" href="https://slack.com/">Slack</a> ou <a class="reference external" href="https://telegram.org/">Telegram</a>, mandar o arquivo de log para o <a class="reference external" href="https://aws.amazon.com/pt/s3/">S3</a> e analisar com alguma ferramenta sua, ou pode usar a stack da <a class="reference external" href="https://www.elastic.co/">Elastic</a> (Logstash, Kibana e Elasticsearch) entre outras inúmeras opções.</p>
</div>
<div class="section" id="o-que-eu-preciso-logar">
<h2>O que eu preciso logar?</h2>
<p>Você deve logar tudo que é importante para o bom funcionamento das operações que sua aplicação executa, entretanto, quando você não sabe o que é importante, tudo é importante.</p>
<p>Quando você não sabe o que logar, faça log de tudo! Sim, isso mesmo!</p>
<p>Isso vai ser chato no no começo, pois tudo que acontecer no sistema vai aparecer pra você e você terá que analisar, mas uma coisa é certa, a primeira vez que você ler um log que contêm tudo da sua aplicação algumas coisas vão acontecer com certeza:</p>
<ol class="arabic simple">
<li>Um entendimento melhor sobre o seu sistema: Quando você loga tudo, você consegue ver o comportamento real do seu sistema, como ele está funcionando de verdade, qual o fluxo das operações que ele faz e baseado nisso você consegue melhorar as operações e o fluxo como um todo de maneira que tenha um melhor desempenho para você e também para o seu cliente (até em termos de usabilidade);</li>
<li>Chateação: Quando você chega ao momento de se perguntar <cite>Por que eu estou logando essa ação?</cite> provavelmente essa linha de log não é importante pra você, pense sobre isso e veja se realmente faz diferença pra você, se não é importante, prontamente pare de logar isso, a leitura do log ficará mais fácil;</li>
</ol>
<p>Ainda sobre <cite>Chateação</cite> existem algumas abordagens para resolver isso, uma delas é você separar os logs por nível de importância.</p>
</div>
<div class="section" id="separar-por-nivel-de-importancia">
<h2>Separar por nível de importância</h2>
<p>No Python temos alguns loglevels que podemos usar:</p>
<ul class="simple">
<li>info / debug: Usado para propósitos informativos, onde info é para informações menos detalhadas e debug para informações mais detalhadas;</li>
<li>warning: Usado para logar uma ação que precisa ser notada, mas não é algo crítico ainda;</li>
<li>error / exception / critical: Erros e problemas críticos no geral, usamos error para problemas que não são críticos, critical para problemas críticos e exception para logar exceptions juntamente ao Traceback.</li>
</ul>
<p>Porém é mais fácil entender isso na prática.</p>
</div>
<div class="section" id="saindo-um-pouco-da-teoria-e-indo-para-a-pratica">
<h2>Saindo um pouco da teoria e indo para a prática</h2>
<p>Vamos imaginar que já temos um programa que é nada mais nada menos do que uma calculadora:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span>
<span class="k">def</span> <span class="nf">mul</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="k">return</span> <span class="n">x</span> <span class="o">*</span> <span class="n">y</span>
<span class="k">def</span> <span class="nf">sub</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="k">return</span> <span class="n">x</span> <span class="o">-</span> <span class="n">y</span>
<span class="k">def</span> <span class="nf">div</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="n">x</span> <span class="o">/</span> <span class="n">y</span>
<span class="k">except</span> <span class="ne">ZeroDivisionError</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Impossível dividir por 0!"</span><span class="p">)</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="n">add</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="n">mul</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="n">div</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="n">div</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="n">sub</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
</pre></div>
<p>Ao executar este nosso programinha teríamos a seguinte saída:</p>
<div class="highlight"><pre><span></span>$ python calc.py
12
32
2.0
Impossível dividir por 0!
None
4
</pre></div>
<p>Até ai tudo bem certo? Tudo bem pois não vamos executar cálculos "ilegais", agora se você fosse um usuário desavisado com certeza você tentaria fazer operações deste tipo:</p>
<div class="highlight"><pre><span></span><span class="o">...</span> <span class="n">linhas</span> <span class="n">omitidas</span> <span class="o">...</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="n">add</span><span class="p">(</span><span class="s1">'a'</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="n">mul</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">))</span>
</pre></div>
<p>Ou seja, este cálculo resultaria em uma exceção não tratada por você o que resultaria em problema e insatisfação por parte do usuário.</p>
</div>
<div class="section" id="como-logar-as-acoes-da-nossa-calculadora">
<h2>Como logar as ações da nossa calculadora?</h2>
<p>Para isso vamos usar o módulo <a class="reference external" href="https://docs.python.org/3/howto/logging.html">logger</a> embutido (built-in) no Python.</p>
<p>Vamos ver como fica:</p>
<div class="highlight"><pre><span></span> <span class="mi">1</span> <span class="kn">import</span> <span class="nn">logging</span>
<span class="mi">2</span> <span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span><span class="n">filename</span><span class="o">=</span><span class="s1">'mycalculator.log'</span><span class="p">,</span> <span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">,</span>
<span class="mi">3</span> <span class="nb">format</span><span class="o">=</span><span class="s1">'</span><span class="si">%(asctime)s</span><span class="s1"> </span><span class="si">%(levelname)s</span><span class="s1"> </span><span class="si">%(funcName)s</span><span class="s1"> => </span><span class="si">%(message)s</span><span class="s1">'</span><span class="p">)</span>
<span class="mi">4</span>
<span class="mi">5</span>
<span class="mi">6</span> <span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="mi">7</span> <span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s1">'paramethers: x=</span><span class="si">{}</span><span class="s1">, y=</span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">))</span>
<span class="mi">8</span> <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span>
<span class="mi">9</span>
<span class="mi">10</span>
<span class="mi">11</span> <span class="k">def</span> <span class="nf">mul</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="mi">12</span> <span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s1">'paramethers: x=</span><span class="si">{}</span><span class="s1">, y=</span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">))</span>
<span class="mi">13</span> <span class="k">return</span> <span class="n">x</span> <span class="o">*</span> <span class="n">y</span>
<span class="mi">14</span>
<span class="mi">15</span>
<span class="mi">16</span> <span class="k">def</span> <span class="nf">sub</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="mi">17</span> <span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s1">'paramethers: x=</span><span class="si">{}</span><span class="s1">, y=</span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">))</span>
<span class="mi">18</span> <span class="k">return</span> <span class="n">x</span> <span class="o">-</span> <span class="n">y</span>
<span class="mi">19</span>
<span class="mi">20</span>
<span class="mi">21</span> <span class="k">def</span> <span class="nf">div</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="mi">22</span> <span class="k">try</span><span class="p">:</span>
<span class="mi">23</span> <span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s1">'paramethers: x=</span><span class="si">{}</span><span class="s1">, y=</span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">))</span>
<span class="mi">24</span> <span class="k">return</span> <span class="n">x</span> <span class="o">/</span> <span class="n">y</span>
<span class="mi">25</span> <span class="k">except</span> <span class="ne">ZeroDivisionError</span><span class="p">:</span>
<span class="mi">26</span> <span class="n">logging</span><span class="o">.</span><span class="n">exception</span><span class="p">(</span><span class="s1">'paramethers: x=</span><span class="si">{}</span><span class="s1">, y=</span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">))</span>
<span class="mi">27</span> <span class="nb">print</span><span class="p">(</span><span class="s2">"Impossível dividir por 0!"</span><span class="p">)</span>
<span class="mi">28</span>
<span class="mi">29</span> <span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="mi">30</span> <span class="nb">print</span><span class="p">(</span><span class="n">add</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
<span class="mi">31</span> <span class="nb">print</span><span class="p">(</span><span class="n">mul</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
<span class="mi">32</span> <span class="nb">print</span><span class="p">(</span><span class="n">div</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
<span class="mi">33</span> <span class="nb">print</span><span class="p">(</span><span class="n">div</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span>
<span class="mi">34</span> <span class="nb">print</span><span class="p">(</span><span class="n">sub</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
</pre></div>
<p>Explicando separadamente:</p>
<ul class="simple">
<li>Linha 1: Faço o import do módulo de logging;</li>
<li><dl class="first docutils">
<dt>Linha 2: Defino como será o meu log:</dt>
<dd><ul class="first last">
<li><cite>filename</cite>: Qual será o nome do meu arquivo de log;</li>
<li><cite>format</cite>: Qual o formato as linhas do meu log serão gravadas.</li>
</ul>
</dd>
</dl>
</li>
<li>Linha 7, 12, 17: Ao chamar a função <cite>add</cite> será gravado um log de debug com os parâmetros que usamos para chamar a função;</li>
<li>Linha 26: Quando a função <cite>div</cite> causar <cite>ZeroDivisionError</cite> esta exceção será gravada no log juntamente com o traceback.</li>
</ul>
<p>Ao executar este programinha um arquivo <cite>mycalculator.log</cite> será gerado com o log das operações executadas, veja só:</p>
<div class="highlight"><pre><span></span>$ python calc.py
12
32
2.0
Impossível dividir por 0!
None
4
$ cat mycalculator.log
2017-01-14 14:06:44,351 DEBUG add => paramethers: x=8, y=4
2017-01-14 14:06:44,351 DEBUG mul => paramethers: x=8, y=4
2017-01-14 14:06:44,351 DEBUG div => paramethers: x=8, y=4
2017-01-14 14:06:44,351 DEBUG div => paramethers: x=8, y=0
2017-01-14 14:06:44,351 ERROR div => paramethers: x=8, y=0
Traceback (most recent call last):
File "calc.py", line 24, in div
return x / y
ZeroDivisionError: division by zero
2017-01-14 14:06:44,352 DEBUG sub => paramethers: x=8, y=4
</pre></div>
<p>Repare que o traceback da função <cite>div</cite> foi gravado quando a exceção ocorreu.</p>
</div>
<div class="section" id="como-mitigar-excecoes-desconhecidas">
<h2>Como mitigar exceções desconhecidas?</h2>
<p>Um pouco mais acima eu havia dito que se você não sabe o que é importante é legal fazer log de tudo e com o tempo remover o que você achar desnecessário.</p>
<p>Esta prática é muito útil quando você não tem pleno conhecimento do funcionamento da aplicação em que você vai trabalhar (supondo que ela não tenha log de nada).</p>
<p>Podemos logar todas as exceções não tratadas para irmos resolvendo ao longo do tempo, para fazer isso é simples, vamos ver como isso ficaria na nossa função <cite>add</cite>:</p>
<div class="highlight"><pre><span></span><span class="o">...</span><span class="n">linhas</span> <span class="n">omitidas</span><span class="o">...</span>
<span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s1">'paramethers: x=</span><span class="si">{}</span><span class="s1">, y=</span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">))</span>
<span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span>
<span class="k">except</span><span class="p">:</span>
<span class="n">logging</span><span class="o">.</span><span class="n">exception</span><span class="p">(</span><span class="s1">'Unknown exception'</span><span class="p">)</span>
<span class="k">raise</span>
<span class="o">...</span><span class="n">linhas</span> <span class="n">omitidas</span><span class="o">...</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="n">add</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="n">add</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="n">add</span><span class="p">(</span><span class="s1">'a'</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
<span class="o">...</span><span class="n">linhas</span> <span class="n">omitidas</span><span class="o">...</span>
</pre></div>
<p>O que aconteceu agora? Todas as exceções "genéricas" serão gravadas no nosso log para fazermos uma análise do que aconteceu. Vamos observar o efeito disso:</p>
<div class="highlight"><pre><span></span>$ python calc.py
12
Traceback (most recent call last):
File "calc.py", line 33, in <module>
print(add(8, 'a'))
File "calc.py", line 9, in add
return x + y
TypeError: unsupported operand type(s) for +: 'int' and 'str'
$ cat mycalculator.log
2017-01-14 14:15:10,939 DEBUG add => paramethers: x=8, y=4
2017-01-14 14:15:10,939 DEBUG add => paramethers: x=8, y=a
2017-01-14 14:15:10,939 ERROR add => Unknown exception
Traceback (most recent call last):
File "calc.py", line 9, in add
return x + y
TypeError: unsupported operand type(s) for +: 'int' and 'str'
</pre></div>
</div>
<div class="section" id="cuidado-que-voce-deve-ter-ao-logar-as-excecoes-genericamente">
<h2>Cuidado que você deve ter ao logar as exceções genericamente</h2>
<p>Perceba que usamos o <cite>raise</cite> após logar a exceção para propagar a exceção, pois quando ela é uma exceção desconhecida não podemos ignorá-la, o código abaixo é considerado uma má prática:</p>
<div class="highlight"><pre><span></span><span class="c1"># ISSO NÃO DEVE SER FEITO</span>
<span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s1">'paramethers: x=</span><span class="si">{}</span><span class="s1">, y=</span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">))</span>
<span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span>
<span class="k">except</span><span class="p">:</span>
<span class="n">logging</span><span class="o">.</span><span class="n">exception</span><span class="p">(</span><span class="s1">'Unknown exception'</span><span class="p">)</span>
<span class="c1"># ISSO NÃO DEVE SER FEITO</span>
</pre></div>
<p>Desta forma acima quando o nosso código levantar uma exceção irá retornar <cite>None</cite> seja ela qual for! Isso é horrível! Dificulta a programação e a depuração em diversos aspectos.</p>
<p>Se você tomar esta precaução não terá problema você fazer logs para exceções genéricas e isso te ajudará muito (principalmente quando você não conhece o sistema como um todo), e você poderá ir criando exceções mais claras posteriormente quando você já tiver um entendimento legal do código :).</p>
</div>
<div class="section" id="conclusao">
<h2>Conclusão</h2>
<p>Criar logs te ajuda a entender mais sobre o sistema e não é uma tarefa complexa, comece aos poucos e logo você estará viciado em fazer logs pois você verá por si a vantagem que te proporciona no dia a dia.</p>
<p>Dúvidas, sugestões e críticas deixe seu comentário abaixo!</p>
<p>Espero que tenham gostado! ;)</p>
</div>
<div class="section" id="referencias">
<h2>Referências</h2>
<ul class="simple">
<li><a class="reference external" href="https://docs.python.org/3/howto/logging.html#when-to-use-logging">When to use logging</a></li>
<li><a class="reference external" href="https://docs.python.org/3/howto/logging.html">Logging HOWTO</a></li>
</ul>
</div>
Problemas com sequences no PostgreSQL usando Django2016-12-22T10:58:00-02:002016-12-22T10:58:00-02:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2016-12-22:/problemas-com-postgres-django-sequences.html<p class="first last">Sua sequence não incrementa corretamente, este post pode te ajudar</p>
<p>Tive um problema bem estranho estes dias, fui criar um novo objeto de model Django e persistí-lo no banco com o método <cite>.save()</cite> (como todos vocês devem fazer milhares de vezes por dia), porém isso não aconteceu e recebi um erro deste tipo:</p>
<div class="highlight"><pre><span></span>IntegrityError: duplicate key value violates unique constraint "core_pessoa_pkey"
DETAIL: Key (id)=(2) already exists.
</pre></div>
<p>Vamos entender por que isso aconteceu.</p>
<div class="section" id="qual-a-causa-para-isso-ter-acontecido">
<h2>Qual a causa para isso ter acontecido?</h2>
<p>Você sempre se faz essa pergunta quando se depara com uma situação bizarra? Bem, eu também. A uns dias atrás precisei rodar um script no Django que inseria vários objetos no banco de dados, porém como esta operação já havia sido testada e retestada em outra máquina os objetos já tinham uma pk associada, então imagine um model do Django hipotético com esta estrutura:</p>
<div class="highlight"><pre><span></span>class Pessoa(models.Model):
nome = models.CharField(max_length=50)
idade = models.IntegerField()
</pre></div>
<p>E para persistir um novo objeto no banco você faz:</p>
<div class="highlight"><pre><span></span>>>> from django-test.core.models import Pessoa
>>> p = Pessoa(id=1, nome="Rafael", idade=28)
>>> p.save()
</pre></div>
<p>Desta maneira iríamos persistir esta pessoa na nossa base de dados PostgreSQL tranquilamente. Porém!!! Porém!!! Porém!!! Isso tem um problema. Quando criamos um objeto novo já com uma pk (o id do nosso exemplo) associada e persistimos este objeto a sequence dele NÃO É INCREMENTADA! Ou seja, quando você for persistir um outro objeto sem passar a pk o Django te mandará esta exception abaixo bem no meio da sua cara:</p>
<div class="highlight"><pre><span></span>IntegrityError: duplicate key value violates unique constraint "core_pessoa_pkey"
DETAIL: Key (id)=(1) already exists.
</pre></div>
<p>Como consigo ver este efeito acontecer? Simples, basta tentar persistir uma pessoa sem passar a pk:</p>
<div class="highlight"><pre><span></span>>>> from pessoa.models import Pessoa
>>> p1 = Pessoa(id=2, nome="Rafael", idade=28)
>>> p1.save()
>>> p2 = Pessoa(nome="Rafael", idade=28)
>>> p2.save()
django.db.utils.IntegrityError: duplicate key value violates unique constraint "core_pessoa_pkey"
DETAIL: Key (id)=(1) already exists.
</pre></div>
<p>Este efeito foi observado no PostgreSQL e não sei se aconteceria usando MySQL ou outros SGBDs por exemplo... é questão de experimentar e ver o efeito.</p>
</div>
<div class="section" id="como-eu-resolvo-isso">
<h2>Como eu resolvo isso?</h2>
<p>Você conseguirá resolver este problema setando um valor NA MÃO para a sua sequence que não foi incrementada. No PostgreSQL até que foi simples, primeiro fiz uma query para descobrir quais eram TODAS as minhas sequences:</p>
<div class="highlight"><pre><span></span>SELECT c.relname FROM pg_class c WHERE c.relkind = 'S';
relname
-----------------------------------
django_migrations_id_seq
django_content_type_id_seq
auth_permission_id_seq
auth_group_id_seq
auth_group_permissions_id_seq
auth_user_id_seq
auth_user_groups_id_seq
auth_user_user_permissions_id_seq
django_admin_log_id_seq
core_pessoa_id_seq
(10 registros)
</pre></div>
<p>Neste caso a sequence que queremos é a do <cite>id</cite> que está na app chamada <cite>core</cite> e no model <cite>Pessoa</cite>, que se chama <cite>core_pessoa_id_seq</cite> no meu exemplo. Vamos dar uma olhada como esta sequence está no momento:</p>
<div class="highlight"><pre><span></span>SELECT * FROM core_pessoa_id_seq;
sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called
--------------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
core_pessoa_id_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t
(1 registro)
</pre></div>
<p>Perceba que nossa sequence possui o valor <cite>last_value</cite> igual a 1, o que não é verdade pois já temos 2 objetos na nossa tabela <cite>pessoa</cite>, como podemos ver na query abaixo:</p>
<div class="highlight"><pre><span></span>SELECT * FROM core_pessoa;
id | nome | idade
----+--------+-------
1 | Rafael | 28
2 | Rafael | 28
(2 registros)
</pre></div>
<p>Para verificar isso mais facilmente (em caso de muitos registros) também podemos usar um count nesta query:</p>
<div class="highlight"><pre><span></span>SELECT count(*) FROM core_pessoa;
count
-------
2
(1 registro)
</pre></div>
<p>Agora o próximo passo é setar o <cite>last_value</cite> de maneira correta, para isso vamos executar o comando abaixo:</p>
<div class="highlight"><pre><span></span>SELECT setval('core_pessoa_id_seq', 2);
setval
--------
2
(1 registro)
</pre></div>
<p>Depois podemos consultar o valor da nossa sequence de novo:</p>
<div class="highlight"><pre><span></span>SELECT * FROM core_pessoa_id_seq;
sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called
--------------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
core_pessoa_id_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | t
(1 registro)
</pre></div>
<p>Podemos ver que foi alterado de fato! Agora vamos tentar inserir um objeto sem id novamente:</p>
<div class="highlight"><pre><span></span>>>> from pessoa.models import Pessoa
>>> p = Pessoa(nome="Rafael", idade=28)
>>> p.save()
</pre></div>
<p>E tudo funcionou normalmente de novo como deveria ser!! Dúvidas/sugestões e críticas?? Use essa caixinha mágica ai em baixo :)</p>
</div>
Resolução exercício: 03 - Blackjack parte 01 - Grupo de Estudos Python2016-04-10T23:53:00-03:002016-04-10T23:53:00-03:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2016-04-10:/resolucao-03-blackjack-parte-01-grupo-de-estudos-python.html<p class="first last">Resolução do exercício blackjack parte 1 passado no Grupo de Estudos Python</p>
<p>Como este exercício foi complexo, a resolução é em forma de vídeo!</p>
<div class="section" id="para-voce-meu-amigo-leitor">
<h2>Para você meu amigo leitor ...</h2>
<p>Para quem só lê os posts e não vê os vídeos do grupo de estudos, resolvi fazer uma repostagem dos vídeos neste post para ficar indexado de forma que as pessoas que só acompanham pelo blog consigam achar a resolução e enteder o exercício também.</p>
<p>Se você quiser ver os 4 vídeos de maneira passo-a-passo como eu resolvi o exercício continue até o final do post. Caso você seja mais do tipo...</p>
<img alt="show me the code" src="images/resolucao-03-blackjack-parte-01-grupo-de-estudos-python.png" />
<p>... temos um repositório no github com a resolução do exercício de várias pessoas (não só a minha)...</p>
</div>
<div class="section" id="nosso-repositorio">
<h2>Nosso repositório</h2>
<p>Neste repositório contêm todas as resoluções para o exercício, e não somente a minha:</p>
<p><a class="reference external" href="https://github.com/rafaelhenrique/python_study_group/tree/master/blackjack">https://github.com/rafaelhenrique/python_study_group/tree/master/blackjack</a></p>
<p>Quer colocar a sua resolução lá? Solicite acesso ao repositório que eu libero.</p>
</div>
<div class="section" id="videos">
<h2>Vídeos</h2>
</div>
<div class="section" id="aula-06-parte-1">
<h2>Aula 06 - Parte 1</h2>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/L48rYYEH8_Q" allowfullscreen seamless frameBorder="0"></iframe></div></div>
<div class="section" id="aula-06-parte-2">
<h2>Aula 06 - Parte 2</h2>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/FsgFZDpxHWk" allowfullscreen seamless frameBorder="0"></iframe></div></div>
<div class="section" id="aula-07">
<h2>Aula 07</h2>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/nXHh9yiBi_I" allowfullscreen seamless frameBorder="0"></iframe></div></div>
<div class="section" id="aula-07-finalizando-o-exercicio-blackjack">
<h2>Aula 07 - Finalizando o exercício blackjack</h2>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/amulYdX9XHM" allowfullscreen seamless frameBorder="0"></iframe></div></div>
<div class="section" id="palavras-finais">
<h2>Palavras finais</h2>
<p>Dúvidas? Críticas? Sugestões? Sempre são bem vindas! Comenta ai!</p>
<p>That's all folks!</p>
</div>
Exercício: 04 - Agenda Pythônica - Grupo de Estudos Python2016-04-10T23:14:00-03:002016-04-10T23:14:00-03:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2016-04-10:/exercicio-04-agenda-pythonica-grupo-de-estudos-python.html<p class="first last">Exercício para resolução</p>
<p>Vamos fazer uma agenda!</p>
<div class="section" id="passo-a-passo">
<h2>Passo a passo...</h2>
<ul class="simple">
<li><dl class="first docutils">
<dt>Passo 1: Primeiro receberemos os campos que o usuário vai querer na agenda. O usuário poderá digitar uma quantidade variável de campos. Exemplo de campos: data, nome, compromisso, observação, prioridade e etc.</dt>
<dd><ul class="first last">
<li>Após receber estes campos você deverá criar as chaves respectivas de um dicionário usando estes nomes;</li>
</ul>
</dd>
</dl>
</li>
<li>Passo 2: Salve o formato (as keys do seu dicionário) da sua agenda em um arquivo texto (se quiser inovar pode ser um banco de dados também);</li>
<li>Passo 3: Faça seu sistema ler o formato do dicionário e criar as keys quando ele rodar, caso ele não ache o arquivo com o formato ele deverá executar o passo 1 e 2 de novo;</li>
<li>Passo 4: Depois que seu sistema leu o formato, receba as informações de compromisso (respeitando as keys do dicionário) do usuário e salve essas informações em outro arquivo (ou pode ser no mesmo apesar de eu achar isso mais complexo);</li>
<li>Passo 5: Crie uma opção para que o usuário possa ler os compromissos cadastrados.</li>
</ul>
<p>Para facilitar a resolução pense em dicionários, listas, tuplas e estruturas de repetição (for e while).</p>
</div>
<div class="section" id="importante">
<h2>Importante</h2>
<p><strong>TODOS os passos devem ser feitos utilizando TDD, sempre pensando no teste primeiro e codificar depois! Esse exercício serve para ajudar a fixar a prática do TDD!</strong></p>
<p>Pode ser usado doctest e/ou unittest ou outra ferramenta qualquer que vocês queiram estudar/aprender. Entre doctest e unittest prefiro que vocês desenvolvam com unittest, pois é uma ferramenta melhor/mais usada (vocês conseguem vagas de emprego com isso) para vocês aplicarem no "mundo real".</p>
<p>Se ainda não se sentem confortáveis com o unittest é melhor entender bem o funcionamento dele antes de estudar novas ferramentas para teste.</p>
<p>Se vocês quiserem "inovar" e aprender ferramentas novas para teste recomendo algumas:</p>
<ul class="simple">
<li><a class="reference external" href="http://pytest.org/latest/">Pytest</a></li>
<li><a class="reference external" href="http://nose.readthedocs.org/en/latest/">Nose</a></li>
<li><a class="reference external" href="http://pythonhosted.org/behave/tutorial.html">Behave</a></li>
<li><a class="reference external" href="http://lettuce.it/tutorial/simple.html">Lettuce</a></li>
</ul>
<p>Não estou tirando o crédito do unittest, pelo contrário, acho uma ótima ferramenta, porém caso queiram estudar MAIS ou caso estejam com tempo sobrando convêm dar uma olhada nessas outras ferramentas.</p>
</div>
<div class="section" id="dicas-extras">
<h2>Dicas extras</h2>
<p>Você poderá estudar o módulo <a class="reference external" href="https://docs.python.org/3/library/json.html">json</a> e o <a class="reference external" href="https://docs.python.org/3/tutorial/inputoutput.html#methods-of-file-objects">file object</a> do Python 3 para te ajudar na resolução.</p>
<p>A saída do seu sistema deverá ser algo similar a esta abaixo:</p>
<div class="highlight"><pre><span></span><span class="gp">$ </span>ls
<span class="go">agenda.py</span>
<span class="gp">$ </span>python agenda.py
<span class="go">Digite um campo novo [c=cancelar]: nome</span>
<span class="go">Digite um campo novo [c=cancelar]: dia</span>
<span class="go">Digite um campo novo [c=cancelar]: compromisso</span>
<span class="go">Digite um campo novo [c=cancelar]: ano</span>
<span class="go">Digite um campo novo [c=cancelar]: mes</span>
<span class="go">Digite um campo novo [c=cancelar]: c</span>
<span class="gp">$ </span>ls
<span class="go">agenda.py structure.json</span>
<span class="gp">$ </span>python agenda.py
<span class="go">O que deseja fazer?</span>
<span class="go">1. Acrescentar compromissos</span>
<span class="go">2. Ler compromissos</span>
<span class="go">3. Sair</span>
<span class="go">2</span>
<span class="go">Nenhum compromisso cadastrado!</span>
<span class="go">O que deseja fazer?</span>
<span class="go">1. Acrescentar compromissos</span>
<span class="go">2. Ler compromissos</span>
<span class="go">3. Sair</span>
<span class="go">1</span>
<span class="go">Digite um novo valor para nome: Rafael</span>
<span class="go">Digite um novo valor para mes: Abril</span>
<span class="go">Digite um novo valor para ano: 2016</span>
<span class="go">Digite um novo valor para compromisso: Ir dormir</span>
<span class="go">Digite um novo valor para dia: 10</span>
<span class="go">O que deseja fazer?</span>
<span class="go">1. Acrescentar compromissos</span>
<span class="go">2. Ler compromissos</span>
<span class="go">3. Sair</span>
<span class="go">2</span>
<span class="go">nome: Rafael</span>
<span class="go">mes: Abril</span>
<span class="go">ano: 2016</span>
<span class="go">compromisso: Ir dormir</span>
<span class="go">dia: 10</span>
<span class="go">-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~</span>
<span class="go">O que deseja fazer?</span>
<span class="go">1. Acrescentar compromissos</span>
<span class="go">2. Ler compromissos</span>
<span class="go">3. Sair</span>
<span class="go">3</span>
<span class="gp">$ </span>ls
<span class="go">agenda.py structure.json tasks.json</span>
</pre></div>
<p>** Preste atenção ao <tt class="docutils literal">ls</tt> que executei, em um primeiro momento criei somente o arquivo de estrutura (structure.json) e depois criei o arquivo contendo as tarefas (tasks.json)</p>
<p>Semana que vem posto a resolução!</p>
<p>That's all folks!</p>
</div>
O que são métodos/funções in-place no Python?2016-04-04T16:36:00-03:002016-04-04T16:36:00-03:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2016-04-04:/o-que-sao-metodos-funcoes-inplace-no-python.html<p class="first last">O que são estes métodos in-place? Onde vivem? Do que se alimentam?</p>
<p>Métodos/funções in-place no Python são carinhas que alteram o objeto original ao executar determinada funcionalidade.</p>
<div class="section" id="in-place">
<h2>IN PLACE</h2>
<p>Vamos a um exemplo prático:</p>
<div class="highlight"><pre><span></span><span class="gp">$ </span>ipython
<span class="go">Python 3.5.1 (default, Feb 10 2016, 10:59:15)</span>
<span class="go">Type "copyright", "credits" or "license" for more information.</span>
<span class="go">IPython 4.1.2 -- An enhanced Interactive Python.</span>
<span class="go">? -> Introduction and overview of IPython's features.</span>
<span class="gp">%</span>quickref -> Quick reference.
<span class="go">help -> Python's own help system.</span>
<span class="go">object? -> Details about 'object', use 'object??' for extra details.</span>
<span class="go">In [1]: teste = ['a', 'b', 'c']</span>
<span class="go">In [2]: teste.reverse</span>
<span class="go">teste.reverse</span>
<span class="go">In [2]: teste.reverse?</span>
<span class="go">Docstring: L.reverse() -- reverse *IN PLACE*</span>
<span class="go">Type: builtin_function_or_method</span>
<span class="go">In [3]: reversed?</span>
<span class="go">Init signature: reversed(self, /, *args, **kwargs)</span>
<span class="go">Docstring:</span>
<span class="go">reversed(sequence) -> reverse iterator over values of the sequence</span>
<span class="go">Return a reverse iterator</span>
<span class="go">Type: type</span>
</pre></div>
<p>** Neste exemplo (utilizando o ipython) usei um recurso bem bacana do ipython que mostra a docstring de um método/função python utilizando o caractere <tt class="docutils literal">?</tt>, usem isso é bem legal :)</p>
<p>Podemos ver que de acordo com a docstring do método <a class="reference external" href="https://docs.python.org/3.5/tutorial/datastructures.html#more-on-lists">reverse</a> de uma lista chamada <tt class="docutils literal">teste</tt> o método <tt class="docutils literal">reverse</tt> é IN PLACE, ao contrário da função <a class="reference external" href="https://docs.python.org/3/library/functions.html#reversed">reversed</a> (builtin do Python) que não é IN PLACE. Bem ai já temos uma ideia de como começar nossos estudos. Vamos ver o que o método <tt class="docutils literal">reverse</tt> da lista <tt class="docutils literal">teste</tt> é capaz de fazer:</p>
<div class="highlight"><pre><span></span><span class="go">In [6]: id(teste)</span>
<span class="go">Out[6]: 140323076029960</span>
<span class="go">In [7]: teste</span>
<span class="go">Out[7]: ['a', 'b', 'c']</span>
<span class="go">In [8]: teste.reverse()</span>
<span class="go">In [9]: id(teste)</span>
<span class="go">Out[9]: 140323076029960</span>
<span class="go">In [10]: teste</span>
<span class="go">Out[10]: ['c', 'b', 'a']</span>
</pre></div>
<ul class="simple">
<li>execução [6]: eu usei uma função também builtin do Python chamada <a class="reference external" href="https://docs.python.org/3/library/functions.html#id">id</a> que retorna a identidade de um objeto Python, o endereço retornado é o endereço do objeto em memória, um objeto diferente não possui o mesmo endereço de id NUNCA;</li>
<li>execução [7]: podemos visualizar quais dados temos na lista <tt class="docutils literal">teste</tt>;</li>
<li>execução [8]: aplico o reverse na lista <tt class="docutils literal">teste</tt> e podemos ver que este método não retorna dados pois não temos a linha Out após sua execução;</li>
<li>execução [9]: podemos ver que o id continua o mesmo, logo podemos concluir que nosso objeto ainda é o mesmo objeto (em memória);</li>
<li>execução [10]: nossa lista chamada <tt class="docutils literal">teste</tt> foi modificada IN PLACE, ou seja, no mesmo objeto em que foi chamado o método reverse.</li>
</ul>
<p>Observação importante: Nem todo IN PLACE mantêm o id do objeto original, a regra é o método/função alterar o objeto dentro dele mesmo, mas isso não significa que o id será sempre o mesmo, pois ele poderá criar um novo objeto e depositar esse objeto SOBRESCREVENDO sua variável antiga.</p>
</div>
<div class="section" id="o-que-nao-e-in-place">
<h2>O que não é IN PLACE</h2>
<p>Um método/função não IN PLACE não modifica o objeto original para aplicar suas funcionalidades, mas sim cria um novo objeto! Vamos ao exemplo, pegando o gancho do exemplo acima:</p>
<div class="highlight"><pre><span></span><span class="go">In [15]: teste</span>
<span class="go">Out[15]: ['c', 'b', 'a']</span>
<span class="go">In [16]: id(teste)</span>
<span class="go">Out[16]: 140323076029960</span>
<span class="go">In [17]: reversed(teste)</span>
<span class="go">Out[17]: <list_reverseiterator at 0x7f9f8313c9b0></span>
<span class="go">In [18]: teste</span>
<span class="go">Out[18]: ['c', 'b', 'a']</span>
<span class="go">In [19]: id(teste)</span>
<span class="go">Out[19]: 140323076029960</span>
<span class="go">In [20]: teste2 = list(reversed(teste))</span>
<span class="go">In [21]: teste2</span>
<span class="go">Out[21]: ['a', 'b', 'c']</span>
<span class="go">In [22]: id(teste)</span>
<span class="go">Out[22]: 140323076029960</span>
<span class="go">In [23]: id(teste2)</span>
<span class="go">Out[23]: 140323075691656</span>
<span class="go">In [24]: teste</span>
<span class="go">Out[24]: ['c', 'b', 'a']</span>
<span class="go">In [25]: teste2</span>
<span class="go">Out[25]: ['a', 'b', 'c']</span>
</pre></div>
<ul class="simple">
<li>execução [15]: temos a nossa mesma lista com os mesmos elementos, na mesma ordem;</li>
<li>execução [16]: temos o mesmo id da nossa lista original;</li>
<li>execução [17]: executamos o <tt class="docutils literal">reversed</tt> na lista <tt class="docutils literal">teste</tt> e o resultado de Out é outro tipo de objeto chamado <tt class="docutils literal">list_reverseiterator</tt>, um objeto em diferente do que tínhamos anteriormente;</li>
<li>execução [18]: nossa lista <tt class="docutils literal">teste</tt> continua com os elementos na mesma posição após a execução da função;</li>
<li>execução [19]: o id da nossa lista <tt class="docutils literal">teste</tt> também continua sendo o mesmo;</li>
<li>execução [20]: agora vou guardar o resultado da função <tt class="docutils literal">reversed</tt> aplicar outra função <tt class="docutils literal">list</tt> para criar um novo objeto <tt class="docutils literal">teste2</tt> a partir da lista <tt class="docutils literal">teste</tt>;</li>
<li>execução [21]: aqui temos uma nova lista ordenada de forma diferente da <tt class="docutils literal">teste</tt>;</li>
<li>execução [22]: o id da lista <tt class="docutils literal">teste</tt> ainda permanece o mesmo;</li>
<li>execução [23]: o id da lista <tt class="docutils literal">teste2</tt> é outro, pois ele é outro objeto;</li>
<li>execução [24][25]: por fim vemos que os resultados das duas listas são diferentes.</li>
</ul>
</div>
<div class="section" id="conclusao">
<h2>Conclusão</h2>
<p>Entender o que é um método/função IN PLACE e como isso funciona no Python é muito importante para o aprendizado da linguagem e também evita o aparecimento de bugs :P.</p>
<p>That's all folks</p>
</div>
Exercício: 03 - Blackjack parte 01 - Grupo de Estudos Python2016-03-22T20:49:00-03:002016-03-22T20:49:00-03:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2016-03-22:/exercicio-03-blackjack-parte-01-grupo-de-estudos-python.html<p class="first last">Exercício para resolução passado no Grupo de Estudos Python</p>
<p>Como exercício desta semana gostaria que vocês começassem a implementação de um jogo de Blackjack, também conhecido como 21 (aqui no Brasil).</p>
<p>As regras do Blackjack que usaremos como base são essas da <a class="reference external" href="https://pt.wikipedia.org/wiki/Blackjack">Wikipédia</a>. Também tem um joguinho pronto nos jogos da UOL que pode ser usado para entender melhor como é o jogo, <a class="reference external" href="http://jogosonline.uol.com.br/blackjack_2654.html">clique aqui</a> pra ver o jogo.</p>
<div class="section" id="recomendacoes">
<h2>Recomendações</h2>
<p>Como o jogo é complexo vamos fazer algumas regras primeiro, e em outro momento escrevemos outras, até terminar o jogo todo, BABY STEPS (passos de bebê, pé ante pé, bem devagar porém com segurança).</p>
<p>Lembre-se de usar a técnica de <a class="reference external" href="https://pt.wikipedia.org/wiki/Test_Driven_Development">TDD</a> para exercitar sua cabeça a pensar em testes, escreva sempre os testes primeiro para depois começar a programar algo realmente útil.</p>
<p>Utilize o módulo <tt class="docutils literal">doctest</tt> do Python conforme mencionei na <a class="reference external" href="https://www.youtube.com/watch?v=OLTUoD4jaGA">Aula 05</a> para escrever seus testes de maneira básica.</p>
<p>Ressalto aqui, o jogo não precisa estar pronto até o final! Escrevam testes para as funções mencionadas abaixo e as implemente. Nem precisa ter nada abaixo do <tt class="docutils literal">if __name__ == "__main__"</tt>.</p>
</div>
<div class="section" id="o-exercicio">
<h2>O exercício</h2>
<p>Na <a class="reference external" href="https://www.youtube.com/watch?v=OLTUoD4jaGA">aula 05</a> nós já implementamos um baralho simples, conforme vemos o código abaixo:</p>
<div class="highlight"><pre><span></span><span class="c1"># -*- coding: utf-8 -*-</span>
<span class="sd">"""</span>
<span class="sd">Doctest deck</span>
<span class="sd">>>> deck = create_deck() # Test create_deck</span>
<span class="sd">>>> len(deck)</span>
<span class="sd">52</span>
<span class="sd">>>> ten_firsts = deck[0:10] # Test shuffle</span>
<span class="sd">>>> shuffle(deck)</span>
<span class="sd">>>> deck[0:10] != ten_firsts</span>
<span class="sd">True</span>
<span class="sd">"""</span>
<span class="kn">import</span> <span class="nn">random</span>
<span class="k">def</span> <span class="nf">create_deck</span><span class="p">():</span>
<span class="sd">"""This function create an deck with 52 cards"""</span>
<span class="n">numbers</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"A"</span><span class="p">,</span> <span class="s2">"2"</span><span class="p">,</span> <span class="s2">"3"</span><span class="p">,</span> <span class="s2">"4"</span><span class="p">,</span> <span class="s2">"5"</span><span class="p">,</span> <span class="s2">"6"</span><span class="p">,</span>
<span class="s2">"7"</span><span class="p">,</span> <span class="s2">"8"</span><span class="p">,</span> <span class="s2">"9"</span><span class="p">,</span> <span class="s2">"10"</span><span class="p">,</span> <span class="s2">"Q"</span><span class="p">,</span> <span class="s2">"J"</span><span class="p">,</span>
<span class="s2">"K"</span><span class="p">]</span>
<span class="n">suits</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"♣"</span><span class="p">,</span> <span class="s2">"♦"</span><span class="p">,</span> <span class="s2">"♥"</span><span class="p">,</span> <span class="s2">"♠"</span><span class="p">]</span>
<span class="n">deck</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">suit</span> <span class="ow">in</span> <span class="n">suits</span><span class="p">:</span>
<span class="k">for</span> <span class="n">number</span> <span class="ow">in</span> <span class="n">numbers</span><span class="p">:</span>
<span class="n">deck</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">"</span><span class="si">{}{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">number</span><span class="p">,</span> <span class="n">suit</span><span class="p">))</span>
<span class="k">return</span> <span class="n">deck</span>
<span class="k">def</span> <span class="nf">shuffle</span><span class="p">(</span><span class="n">deck</span><span class="p">):</span>
<span class="sd">"""This function shuffle one deck"""</span>
<span class="n">deck</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">shuffle</span><span class="p">(</span><span class="n">deck</span><span class="p">)</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="n">deck</span> <span class="o">=</span> <span class="n">create_deck</span><span class="p">()</span>
<span class="n">shuffle</span><span class="p">(</span><span class="n">deck</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">deck</span><span class="p">)</span>
</pre></div>
<p>** Também serve de exercício dar uma olhada nessas funções acima e refatorá-las, sempre da pra melhorar!</p>
<p>Como exercício implementem as funções abaixo:</p>
<ol class="arabic simple">
<li>função <tt class="docutils literal">hit</tt>: Nesta função você comprará cartas e irá sempre remover uma carta do baralho original, pois no momento que você compra uma carta a mesma não poderá existir mais no mesmo baralho (pista: dê uma lida na documentação do módulo <a class="reference external" href="https://docs.python.org/3/library/random.html">random</a>);</li>
<li>função <tt class="docutils literal">show_hand</tt>: Nesta função você deverá mostrar quantas cartas você já comprou e quais são elas;</li>
<li><dl class="first docutils">
<dt>função <tt class="docutils literal">show_points</tt>: Nesta função você deverá mostrar quantos pontos acumulados você tem com as cartas que você já comprou;</dt>
<dd><ul class="first last">
<li>Ás vale 1 ponto;</li>
<li>Cartas de números valem a mesma quantidade do número;</li>
<li>Cartas de figuras (K, J, Q) valem 10.</li>
</ul>
</dd>
</dl>
</li>
<li>função <tt class="docutils literal">surrender</tt>: Mostra os pontos que você conseguiu até agora e finaliza a execução do jogo.</li>
<li>função <tt class="docutils literal">show_money</tt>: Mostrar seu saldo atual (em dinheiro), o saldo inicial deverá ser 2000 reais, que pode ser representado por uma variável simples que será lida como uma variável global ao longo de todo sistema;</li>
<li><dl class="first docutils">
<dt>função <tt class="docutils literal">bet</tt>: Esta é uma das funções mais complexas e legais do jogo, nela você deverá ver o seu saldo atual (em dinheiro) e comprar o número de fichas correspondentes com a aposta em dinheiro. Imagine assim:</dt>
<dd><ul class="first last">
<li>Eu tenho 2000 reais iniciais;</li>
<li>Eu posso comprar fichas de 1, 5, 25 e 100 reais;</li>
<li>Eu posos comprar fichas (por enquanto) a hora que eu quiser e bem entender;</li>
<li>Se meu dinheiro for menor do que o valor em fichas que eu quero comprar, eu não posso comprar.</li>
</ul>
</dd>
</dl>
</li>
</ol>
<p>Acho que por enquanto essas 5 funções serão suficientes para o divertimento garantido hehehe. E não se esqueça: ESCREVA OS TESTES PRIMEIRO, ANTES DE SAIR PROGRAMANDO IGUAL UM MALUCO!</p>
<p>Senão...</p>
<img alt="TDD" src="images/exercicio-03-blackjack-parte-01-grupo-de-estudos-python.png" />
<p>** Sobre TDD aqui tem um texto muito bacana intitulado <a class="reference external" href="http://tableless.com.br/tdd-por-que-usar/">TDD, por que usar?</a></p>
<p>Em caso de dúvidas estou aqui para lhes auxiliar no processo, não deixem de me perguntar caso fiquem perdidos em algum ponto.</p>
<p>Gostaria MUITO que vocês terminassem este exercício até dia 28/03/2016 que vai ser a nossa próxima aula, sem fazer este exercício vocês não irão conseguir medir o quanto aprenderam até agora sobre Python.</p>
<p>Vamos considerar que esse é o primeiro exercício IMPORTANTE que passei pra vocês pois com ele vocês terão a possibilidade de usar TODOS os conhecimentos vistos até agora: Tipos básicos, Dicionários, Listas, Estrutura condicional, Estruturas de repetição, Testes (com doctest) e Funções.</p>
<p>Então... mãos a obra.</p>
<p>That's all folks.</p>
</div>
Resolução exercício: 02 - Média de alunos - Grupo de Estudos Python2016-03-16T21:56:00-03:002016-03-16T21:56:00-03:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2016-03-16:/resolucao-02-media-de-alunos-grupo-de-estudos-python.html<p class="first last">Resolução do exercício média de alunos passado no Grupo de Estudos Python</p>
<p>Dia 07/03 postei um exercício para resolução, fazer um cálculo de média ponderada de notas de alunos, o post contendo o enunciado pode ser <a class="reference external" href="http://blog.abraseucodigo.com.br/exercicio-02-media-de-alunos-grupo-de-estudos-python.html">visto aqui</a>.</p>
<div class="section" id="o-codigo-lixo-que-eu-fiz-no-hangout">
<h2>O código lixo que eu fiz no Hangout</h2>
<p>Lixo é um nome muito forte, vamos chamar de RUIM somente por favor. Quando brincamos de live coding as coisas todas são diferentes do seu mundo real, devido a pressão, ou alguma coisa que passa na sua cabeça.</p>
<p>No live coding que fiz no Hangout tentei focar em muitas técnicas para manipulação de dicionários e listas, espero que isso tenha ficado claro no vídeo da <a class="reference external" href="https://www.youtube.com/watch?v=kZbIeqcycPU">aula 04 disponível neste post</a>.</p>
<p>Então vamos ao código que mencionei:</p>
<div class="highlight"><pre><span></span><span class="c1"># -*- coding: utf-8 -*-</span>
<span class="n">bimestre_final</span> <span class="o">=</span> <span class="mi">6</span>
<span class="n">notas</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">"aluno1"</span><span class="p">:</span> <span class="p">[],</span>
<span class="s2">"aluno2"</span><span class="p">:</span> <span class="p">[],</span>
<span class="p">}</span>
<span class="n">notas</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">alunos</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"Alexsandro"</span><span class="p">,</span> <span class="s2">"Arieh"</span><span class="p">]</span>
<span class="n">bimestres</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"</span><span class="si">{}</span><span class="s2"> Bim"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">numero</span><span class="p">)</span> <span class="k">for</span> <span class="n">numero</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">7</span><span class="p">)]</span>
<span class="c1"># esse é o for sem a list comprehenssion ai de cima</span>
<span class="c1">#</span>
<span class="c1"># bimestres = []</span>
<span class="c1"># for numero in range(1, 7):</span>
<span class="c1"># bimestres.append("{} Bim".format(numero))</span>
<span class="c1">#</span>
<span class="n">pesos</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">"1 Bim"</span><span class="p">:</span> <span class="mf">2.0</span><span class="p">,</span>
<span class="s2">"2 Bim"</span><span class="p">:</span> <span class="mf">3.0</span><span class="p">,</span>
<span class="s2">"3 Bim"</span><span class="p">:</span> <span class="mf">3.0</span><span class="p">,</span>
<span class="s2">"4 Bim"</span><span class="p">:</span> <span class="mf">4.0</span><span class="p">,</span>
<span class="s2">"5 Bim"</span><span class="p">:</span> <span class="mf">5.0</span><span class="p">,</span>
<span class="s2">"6 Bim"</span><span class="p">:</span> <span class="mf">5.0</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">for</span> <span class="n">aluno</span> <span class="ow">in</span> <span class="n">alunos</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Aluno: "</span><span class="p">,</span> <span class="n">aluno</span><span class="p">)</span>
<span class="n">notas</span><span class="p">[</span><span class="n">aluno</span><span class="p">]</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">bimestre</span> <span class="ow">in</span> <span class="n">bimestres</span><span class="p">:</span>
<span class="n">nota</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s2">"Digite a nota do </span><span class="si">{}</span><span class="s2">: "</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">bimestre</span><span class="p">))</span>
<span class="n">nota</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">nota</span><span class="p">)</span>
<span class="n">notas</span><span class="p">[</span><span class="n">aluno</span><span class="p">][</span><span class="n">bimestre</span><span class="p">]</span> <span class="o">=</span> <span class="n">nota</span>
<span class="c1"># (7*2 + 5*3 + 3*3 + 10*4 + 9*5 + 8*5) /</span>
<span class="c1"># (2 + 3 + 3 + 4 + 5 + 5) = 7.40</span>
<span class="n">numerador</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">denominador</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">aluno</span> <span class="ow">in</span> <span class="n">alunos</span><span class="p">:</span>
<span class="n">numerador</span><span class="p">[</span><span class="n">aluno</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">denominador</span><span class="p">[</span><span class="n">aluno</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">bimestre</span><span class="p">,</span> <span class="n">nota</span> <span class="ow">in</span> <span class="n">notas</span><span class="p">[</span><span class="n">aluno</span><span class="p">]</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="n">peso</span> <span class="o">=</span> <span class="n">pesos</span><span class="p">[</span><span class="n">bimestre</span><span class="p">]</span>
<span class="n">numerador</span><span class="p">[</span><span class="n">aluno</span><span class="p">]</span> <span class="o">+=</span> <span class="n">nota</span> <span class="o">*</span> <span class="n">peso</span>
<span class="n">denominador</span><span class="p">[</span><span class="n">aluno</span><span class="p">]</span> <span class="o">+=</span> <span class="n">peso</span>
<span class="n">resultado_final</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">aluno</span> <span class="ow">in</span> <span class="n">alunos</span><span class="p">:</span>
<span class="n">resultado_final</span><span class="p">[</span><span class="n">aluno</span><span class="p">]</span> <span class="o">=</span> <span class="n">numerador</span><span class="p">[</span><span class="n">aluno</span><span class="p">]</span> <span class="o">/</span> <span class="n">denominador</span><span class="p">[</span><span class="n">aluno</span><span class="p">]</span>
<span class="c1"># Nota maior que 9 teve menção A</span>
<span class="c1"># Nota maior que 7 e menor ou igual a 9 teve menção B</span>
<span class="c1"># Nota maior que 5 e menor ou igual a 7 teve menção C</span>
<span class="c1"># Nota menor que 5 teve menção D</span>
<span class="k">for</span> <span class="n">nome</span><span class="p">,</span> <span class="n">nota</span> <span class="ow">in</span> <span class="n">resultado_final</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Nota final </span><span class="si">{}</span><span class="s2">: </span><span class="si">{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">nome</span><span class="p">,</span> <span class="n">nota</span><span class="p">))</span>
<span class="k">if</span> <span class="n">nota</span> <span class="o">></span> <span class="mi">9</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Nota A"</span><span class="p">)</span>
<span class="k">elif</span> <span class="mi">9</span> <span class="o">>=</span> <span class="n">nota</span> <span class="o">></span> <span class="mi">7</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Nota B"</span><span class="p">)</span>
<span class="k">elif</span> <span class="mi">7</span> <span class="o">>=</span> <span class="n">nota</span> <span class="o">></span> <span class="mi">5</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Nota C"</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Nota D"</span><span class="p">)</span>
</pre></div>
<p>Vamos analisar os problemas deste código acima:</p>
<ol class="arabic simple">
<li>Tem muitos fors ai galera, isso vai ficar lento, bem lento mesmo;</li>
<li>As exceptions não estão tratadas de forma legal, mas você poderá estudar mais sobre isso lendo <a class="reference external" href="http://blog.abraseucodigo.com.br/resolucao-01-calculadora-basica-grupo-de-estudos.html">este post</a>, considere isso um exercício;</li>
<li>O código não está pythônico, limpo e claro, tem muita coisa que faz muita coisa.</li>
</ol>
</div>
<div class="section" id="refatorando-as-variaveis-e-dicionarios">
<h2>Refatorando as variáveis e dicionários</h2>
<p>Começando do início:</p>
<div class="highlight"><pre><span></span><span class="c1"># -*- coding: utf-8 -*-</span>
<span class="n">bimestre_final</span> <span class="o">=</span> <span class="mi">6</span>
<span class="n">notas</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">"aluno1"</span><span class="p">:</span> <span class="p">[],</span>
<span class="s2">"aluno2"</span><span class="p">:</span> <span class="p">[],</span>
<span class="p">}</span>
<span class="o">...</span> <span class="n">linhas</span> <span class="n">omitidas</span> <span class="o">...</span>
</pre></div>
<p>Este dicionário de <tt class="docutils literal">notas</tt> não é muito necessário, pois quando eu recebo a nota do aluno eu já posso ir calculando durante a execução do primeiro for, isso é legal pois vai fazer com que a gente ganhe velocidade.</p>
<p>A variável <tt class="docutils literal">bimestre_final</tt> é inútil também, pois como tenho uma lista com todos os bimestres abaixo deste trecho eu sei o número de bimestres que eu tenho a partir do dicionário intitulado como <tt class="docutils literal">bimestres</tt>.</p>
<p>Só ai já eliminei duas coisas inúteis no script. Continuando:</p>
<div class="highlight"><pre><span></span><span class="o">...</span> <span class="n">linhas</span> <span class="n">omitidas</span> <span class="o">...</span>
<span class="n">bimestres</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"</span><span class="si">{}</span><span class="s2"> Bim"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">numero</span><span class="p">)</span> <span class="k">for</span> <span class="n">numero</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">7</span><span class="p">)]</span>
<span class="o">...</span> <span class="n">linhas</span> <span class="n">omitidas</span> <span class="o">...</span>
</pre></div>
<p>O que acham deste <a class="reference external" href="https://docs.python.org/3.5/tutorial/datastructures.html#list-comprehensions">list comprehenssion</a>? Este cara está mais ou menos bacana. Existem outras formas mais simples de escrever este cara, justamente porque são somente 6 bimestres. Soluções aceitáveis:</p>
<ul class="simple">
<li>Primeira forma: Mais legível</li>
</ul>
<div class="highlight"><pre><span></span><span class="o">...</span> <span class="n">linhas</span> <span class="n">omitidas</span> <span class="o">...</span>
<span class="n">bimestres</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">"1 Bim"</span><span class="p">,</span>
<span class="s2">"2 Bim"</span><span class="p">,</span>
<span class="s2">"3 Bim"</span><span class="p">,</span>
<span class="s2">"4 Bim"</span><span class="p">,</span>
<span class="s2">"5 Bim"</span><span class="p">,</span>
<span class="s2">"6 Bim"</span><span class="p">,</span>
<span class="p">]</span>
<span class="o">...</span> <span class="n">linhas</span> <span class="n">omitidas</span> <span class="o">...</span>
</pre></div>
<ul class="simple">
<li>Segunda forma: Mais "chata"</li>
</ul>
<div class="highlight"><pre><span></span><span class="o">...</span> <span class="n">linhas</span> <span class="n">omitidas</span> <span class="o">...</span>
<span class="n">bimestres</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">numero</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">7</span><span class="p">):</span>
<span class="n">bimestres</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2"> Bim"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">numero</span><span class="p">))</span>
<span class="o">...</span> <span class="n">linhas</span> <span class="n">omitidas</span> <span class="o">...</span>
</pre></div>
<ul class="simple">
<li>Terceira forma: "Splitada"</li>
</ul>
<div class="highlight"><pre><span></span><span class="o">...</span> <span class="n">linhas</span> <span class="n">omitidas</span> <span class="o">...</span>
<span class="n">bimestres</span> <span class="o">=</span> <span class="s2">"1-Bim 2-Bim 3-Bim 4-Bim 5-Bim 6-Bim"</span><span class="o">.</span><span class="n">split</span><span class="p">()</span>
<span class="o">...</span> <span class="n">linhas</span> <span class="n">omitidas</span> <span class="o">...</span>
</pre></div>
<p>Eu particularmente opto pela <tt class="docutils literal">Primeira forma</tt> ou pela <tt class="docutils literal">Terceira forma</tt> como estou optando por legibilidade e sem muita complexidade para este post vou escolher a primeira.</p>
<p>A <tt class="docutils literal">Terceira forma</tt> achei legal mostrar, pois o bacana disso é que o <a class="reference external" href="https://docs.python.org/3/library/stdtypes.html#str.split">split</a> (que pode ser usado com strings) irá dividir a string onde tem os espaços, transformando assim essa string comprida em uma lista, que praticamente daria o mesmo resultado também (exceto pelo sinal do hífen).</p>
<p>O caso do dicionário <tt class="docutils literal">pesos</tt> achei bacana, não vou mexer, mas aceito sugestões, se acharem uma solução mais legal me contem por favor.</p>
<p>Depois dessa primeira refatorada, comecei a criar um novo script, que ficou com o começo deste jeito:</p>
<div class="highlight"><pre><span></span><span class="c1"># -*- coding: utf-8 -*-</span>
<span class="n">alunos</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">"Rafael1"</span><span class="p">,</span>
<span class="s2">"Rafael2"</span>
<span class="p">]</span>
<span class="n">bimestres</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">"1 Bim"</span><span class="p">,</span>
<span class="s2">"2 Bim"</span><span class="p">,</span>
<span class="s2">"3 Bim"</span><span class="p">,</span>
<span class="s2">"4 Bim"</span><span class="p">,</span>
<span class="s2">"5 Bim"</span><span class="p">,</span>
<span class="s2">"6 Bim"</span><span class="p">,</span>
<span class="p">]</span>
<span class="n">pesos</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">"1 Bim"</span><span class="p">:</span> <span class="mf">2.0</span><span class="p">,</span>
<span class="s2">"2 Bim"</span><span class="p">:</span> <span class="mf">3.0</span><span class="p">,</span>
<span class="s2">"3 Bim"</span><span class="p">:</span> <span class="mf">3.0</span><span class="p">,</span>
<span class="s2">"4 Bim"</span><span class="p">:</span> <span class="mf">4.0</span><span class="p">,</span>
<span class="s2">"5 Bim"</span><span class="p">:</span> <span class="mf">5.0</span><span class="p">,</span>
<span class="s2">"6 Bim"</span><span class="p">:</span> <span class="mf">5.0</span><span class="p">,</span>
<span class="p">}</span>
<span class="n">numerador</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">denominador</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">pesos</span><span class="o">.</span><span class="n">values</span><span class="p">())</span>
</pre></div>
<p>Eu particularmente achei bem mais bonito e claro de entender as coisas. Só tem uma coisa diferente ai, que a é função <tt class="docutils literal">sum</tt> que já é padrão do Python e faz soma de valores de um iterador, vamos ver como isso funciona:</p>
<div class="highlight"><pre><span></span><span class="gp">$ </span>python
<span class="go">Python 3.5.1 (default, Feb 22 2016, 23:22:06)</span>
<span class="go">[GCC 4.9.2] on linux</span>
<span class="go">Type "help", "copyright", "credits" or "license" for more information.</span>
<span class="go">>>> pesos = {</span>
<span class="go">... "1 Bim": 2.0,</span>
<span class="go">... "2 Bim": 3.0,</span>
<span class="go">... "3 Bim": 3.0,</span>
<span class="go">... "4 Bim": 4.0,</span>
<span class="go">... "5 Bim": 5.0,</span>
<span class="go">... "6 Bim": 5.0,</span>
<span class="go">... }</span>
<span class="go">>>> pesos.values()</span>
<span class="go">dict_values([5.0, 2.0, 4.0, 5.0, 3.0, 3.0])</span>
<span class="go">>>> type(pesos.values())</span>
<span class="go"><class 'dict_values'></span>
<span class="go">>>> sum(pesos.values())</span>
<span class="go">22.0</span>
<span class="go">>>> lista = [1, 2, 3, 4, 5, 6]</span>
<span class="go">>>> sum(lista)</span>
<span class="go">21</span>
<span class="go">>>> sum(range(1,100))</span>
<span class="go">4950</span>
<span class="go">>>> pesos.keys()</span>
<span class="go">dict_keys(['5 Bim', '1 Bim', '4 Bim', '6 Bim', '3 Bim', '2 Bim'])</span>
<span class="go">>>> sum(pesos.keys())</span>
<span class="go">Traceback (most recent call last):</span>
<span class="go"> File "<stdin>", line 1, in <module></span>
<span class="go">TypeError: unsupported operand type(s) for +: 'int' and 'str'</span>
</pre></div>
<p>Olhe que curioso esse teste acima, quando uso <tt class="docutils literal">pesos.values()</tt> pego todos os valores do dicionário pesos em forma de iterável (assunto pra outra hora o que é um iterável), ai quando uso <tt class="docutils literal">sum</tt> somo todos esses valores e o resultado é 22.</p>
<p>Consigo somar também listas, e também consigo somar um range de números gerados a partir da função <tt class="docutils literal">range</tt>. Porém quando tento somar strings (que são minhas keys do dicionário pesos) isso me retorna um erro dizendo que não consigo somar int e strings, essa soma não é possível. Espero que com esse exemplo tenha ficado claro o uso da função <tt class="docutils literal">sum</tt>. Mais informações na <a class="reference external" href="https://docs.python.org/3/library/functions.html#sum">documentação do Python</a>.</p>
</div>
<div class="section" id="refatorando-os-loops">
<h2>Refatorando os loops</h2>
<p>Depois de organizar melhor as variáveis/dicionários e listas que vamos trabalhar fica mais fácil organizar os nossos loops, vamos ver como ficou o script completo:</p>
<div class="highlight"><pre><span></span><span class="c1"># -*- coding: utf-8 -*-</span>
<span class="n">alunos</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">"Rafael1"</span><span class="p">,</span>
<span class="s2">"Rafael2"</span>
<span class="p">]</span>
<span class="n">bimestres</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">"1 Bim"</span><span class="p">,</span>
<span class="s2">"2 Bim"</span><span class="p">,</span>
<span class="s2">"3 Bim"</span><span class="p">,</span>
<span class="s2">"4 Bim"</span><span class="p">,</span>
<span class="s2">"5 Bim"</span><span class="p">,</span>
<span class="s2">"6 Bim"</span><span class="p">,</span>
<span class="p">]</span>
<span class="n">pesos</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">"1 Bim"</span><span class="p">:</span> <span class="mf">2.0</span><span class="p">,</span>
<span class="s2">"2 Bim"</span><span class="p">:</span> <span class="mf">3.0</span><span class="p">,</span>
<span class="s2">"3 Bim"</span><span class="p">:</span> <span class="mf">3.0</span><span class="p">,</span>
<span class="s2">"4 Bim"</span><span class="p">:</span> <span class="mf">4.0</span><span class="p">,</span>
<span class="s2">"5 Bim"</span><span class="p">:</span> <span class="mf">5.0</span><span class="p">,</span>
<span class="s2">"6 Bim"</span><span class="p">:</span> <span class="mf">5.0</span><span class="p">,</span>
<span class="p">}</span>
<span class="n">numerador</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">denominador</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">pesos</span><span class="o">.</span><span class="n">values</span><span class="p">())</span>
<span class="k">for</span> <span class="n">aluno</span> <span class="ow">in</span> <span class="n">alunos</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Aluno: "</span><span class="p">,</span> <span class="n">aluno</span><span class="p">)</span>
<span class="n">numerador</span><span class="p">[</span><span class="n">aluno</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">bimestre</span> <span class="ow">in</span> <span class="n">bimestres</span><span class="p">:</span>
<span class="n">nota</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s2">"Digite a nota do </span><span class="si">{}</span><span class="s2">: "</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">bimestre</span><span class="p">))</span>
<span class="n">nota</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">nota</span><span class="p">)</span>
<span class="n">numerador</span><span class="p">[</span><span class="n">aluno</span><span class="p">]</span> <span class="o">+=</span> <span class="n">nota</span> <span class="o">*</span> <span class="n">pesos</span><span class="p">[</span><span class="n">bimestre</span><span class="p">]</span>
<span class="n">media</span> <span class="o">=</span> <span class="n">numerador</span><span class="p">[</span><span class="n">aluno</span><span class="p">]</span> <span class="o">/</span> <span class="n">denominador</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Nota final </span><span class="si">{}</span><span class="s2">: </span><span class="si">{:.2f}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">aluno</span><span class="p">,</span> <span class="n">media</span><span class="p">))</span>
<span class="k">if</span> <span class="n">media</span> <span class="o">></span> <span class="mi">9</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Nota A"</span><span class="p">)</span>
<span class="k">elif</span> <span class="mi">9</span> <span class="o">>=</span> <span class="n">media</span> <span class="o">></span> <span class="mi">7</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Nota B"</span><span class="p">)</span>
<span class="k">elif</span> <span class="mi">7</span> <span class="o">>=</span> <span class="n">media</span> <span class="o">></span> <span class="mi">5</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Nota C"</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Nota D"</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"*"</span><span class="o">*</span><span class="mi">20</span><span class="p">)</span>
</pre></div>
<p>Agora sim! Ficou um script clean, mais pythônico do que era antes. De 6 loops reduzi para 2 loops somente.</p>
<p>A criação de um dicionário dinamicamente ainda é usada na linha <tt class="docutils literal">numerador[aluno] = 0</tt> porém de forma bem mais simples, somente é inicializado com 0 para poder ir somando as notas do aluno no numerador da nossa equação. Para entender como ficou a estrutura do nosso dicionário numerador veja o exemplo abaixo:</p>
<div class="highlight"><pre><span></span><span class="gp">$ </span>python media_alunos_dict.py
<span class="go">Aluno: Rafael1</span>
<span class="go">Digite a nota do 1 Bim: 7</span>
<span class="go">Digite a nota do 2 Bim: 5</span>
<span class="go">Digite a nota do 3 Bim: 3</span>
<span class="go">Digite a nota do 4 Bim: 10</span>
<span class="go">Digite a nota do 5 Bim: 9</span>
<span class="go">Digite a nota do 6 Bim: 8</span>
<span class="go">> /home/rafael/Dropbox/Grupo de estudos Python/nivel_mediano/media_ponderada/media_alunos_dict.py(35)<module>()</span>
<span class="go"> 34 import ipdb; ipdb.set_trace()</span>
<span class="go">---> 35 media = numerador[aluno] / denominador</span>
<span class="go"> 36 print("Nota final {}: {:.2f}".format(aluno, media))</span>
<span class="go">ipdb> numerador</span>
<span class="go">{'Rafael1': 163.0}</span>
</pre></div>
<p>O numerador terá uma chave para cada nome de aluno contendo a soma total realizada. Neste exemplo foi realizado o seguinte cálculo no numerador do aluno <tt class="docutils literal">Rafael1</tt>: <tt class="docutils literal">(7*2 + 5*3 + 3*3 + 10*4 + 9*5 + 8*5) = 163.0</tt></p>
<p>Quando continuo a execução do script temos:</p>
<div class="highlight"><pre><span></span>ipdb> numerador
{'Rafael1': 163.0, 'Rafael2': 158.0}
</pre></div>
<p>Neste caso cálculo realizado para o aluno <tt class="docutils literal">Rafael2</tt> é: <tt class="docutils literal">(5*2 + 4*3 + 6*3 + 7*4 + 8*5 + 10*5) = 158.0</tt></p>
</div>
<div class="section" id="conclusao">
<h2>Conclusão</h2>
<p>Espero que ao final dessa solução refatorada do live coding que rolou na <a class="reference external" href="https://www.youtube.com/watch?v=kZbIeqcycPU">aula 04</a>, você tenha entendido mais coisas sobre listas, dicionários e estruturas de repetição. Caso tenha alguma dúvida/sugestão/crítica comenta ai.</p>
<p>That's all folks!</p>
</div>
Resolução exercício: 01 - Calculadora básica - Grupo de Estudos Python2016-03-16T20:54:00-03:002016-03-16T20:54:00-03:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2016-03-16:/resolucao-01-calculadora-basica-grupo-de-estudos.html<p class="first last">Resolução do exercício calculadora básica usando um pouco de tratamento de exceções</p>
<p>Dia 29/02 postei um exercício para resolução, uma calculadora básica, o post contendo o enunciado pode ser <a class="reference external" href="http://blog.abraseucodigo.com.br/exercicio-01-calculadora-basica-grupo-de-estudos-python.html">visto aqui</a>.</p>
<p>Resolução simples do exercício:</p>
<div class="highlight"><pre><span></span><span class="c1"># -*- coding: utf-8 -*-</span>
<span class="n">primeiro</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s2">"Digite o primeiro número: "</span><span class="p">)</span>
<span class="n">segundo</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s2">"Digite o segundo número: "</span><span class="p">)</span>
<span class="n">operacao</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s2">"Digite a operação: "</span><span class="p">)</span>
<span class="n">resultado</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="n">operacao</span> <span class="o">==</span> <span class="s2">"+"</span><span class="p">:</span>
<span class="n">resultado</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">primeiro</span><span class="p">)</span> <span class="o">+</span> <span class="nb">float</span><span class="p">(</span><span class="n">segundo</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">operacao</span> <span class="o">==</span> <span class="s2">"-"</span><span class="p">:</span>
<span class="n">resultado</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">primeiro</span><span class="p">)</span> <span class="o">-</span> <span class="nb">float</span><span class="p">(</span><span class="n">segundo</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">operacao</span> <span class="o">==</span> <span class="s2">"*"</span><span class="p">:</span>
<span class="n">resultado</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">primeiro</span><span class="p">)</span> <span class="o">*</span> <span class="nb">float</span><span class="p">(</span><span class="n">segundo</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">operacao</span> <span class="o">==</span> <span class="s2">"/"</span><span class="p">:</span>
<span class="n">resultado</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">primeiro</span><span class="p">)</span> <span class="o">/</span> <span class="nb">float</span><span class="p">(</span><span class="n">segundo</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Operação não existente!"</span><span class="p">)</span>
<span class="k">if</span> <span class="n">resultado</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Resultado: </span><span class="si">{0}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">resultado</span><span class="p">))</span>
</pre></div>
<p>Se executarmos este exercício passo a passo poderemos ver alguns problemas e curiosidades a respeito dele que podem ser aprimorados em relação ao código simples que escrevi acima. Problemas encontrados:</p>
<ol class="arabic simple">
<li>Se o usuário digitar uma letra isso irá causar uma exceção do tipo ValueError onde o Python informará que é impossível converter uma string em float;</li>
<li>A divisão por 0 também causará uma exceção, mas desta vez do tipo ZeroDivisionError.</li>
</ol>
<p>Vamos visualizar estes erros na prática:</p>
<div class="highlight"><pre><span></span><span class="gp">$ </span>python calculadora.py
<span class="go">Qual operação deseja fazer? ( * / - +)</span>
<span class="go">*</span>
<span class="go">Digite operando1:</span>
<span class="go">a</span>
<span class="go">Digite operando2:</span>
<span class="go">a</span>
<span class="go">Traceback (most recent call last):</span>
<span class="go"> File "calculadora00.py", line 16, in <module></span>
<span class="go"> resultado = float(operando1) * float(operando2)</span>
<span class="go">ValueError: could not convert string to float: 'a'</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">$ </span>python calculadora.py
<span class="go">Qual operação deseja fazer? ( * / - +)</span>
<span class="go">/</span>
<span class="go">Digite operando1:</span>
<span class="go">1</span>
<span class="go">Digite operando2:</span>
<span class="go">0</span>
<span class="go">Traceback (most recent call last):</span>
<span class="go"> File "calculadora00.py", line 18, in <module></span>
<span class="go"> resultado = float(operando1) / float(operando2)</span>
<span class="go">ZeroDivisionError: float division by zero</span>
</pre></div>
<p>Como tratar estes erros de forma bem simples? Basta fazer algumas modificações, vejamos esta resolução que utiliza try / except:</p>
<div class="highlight"><pre><span></span><span class="c1"># -*- coding: utf-8 -*-</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="n">operacao</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s2">"Qual operação deseja fazer? ( * / - +)</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">operando1</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="s2">"Digite operando1:</span><span class="se">\n</span><span class="s2">"</span><span class="p">))</span>
<span class="n">operando2</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="s2">"Digite operando2:</span><span class="se">\n</span><span class="s2">"</span><span class="p">))</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Digite somente números nos operandos 1 e 2"</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">resultado</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="n">operacao</span> <span class="o">==</span> <span class="s2">"+"</span><span class="p">:</span>
<span class="n">resultado</span> <span class="o">=</span> <span class="n">operando1</span> <span class="o">+</span> <span class="n">operando2</span>
<span class="k">elif</span> <span class="n">operacao</span> <span class="o">==</span> <span class="s2">"-"</span><span class="p">:</span>
<span class="n">resultado</span> <span class="o">=</span> <span class="n">operando1</span> <span class="o">-</span> <span class="n">operando2</span>
<span class="k">elif</span> <span class="n">operacao</span> <span class="o">==</span> <span class="s2">"*"</span><span class="p">:</span>
<span class="n">resultado</span> <span class="o">=</span> <span class="n">operando1</span> <span class="o">*</span> <span class="n">operando2</span>
<span class="k">elif</span> <span class="n">operacao</span> <span class="o">==</span> <span class="s2">"/"</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">resultado</span> <span class="o">=</span> <span class="n">operando1</span> <span class="o">/</span> <span class="n">operando2</span>
<span class="k">except</span> <span class="ne">ZeroDivisionError</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Operação inválida: Divisão por 0"</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Operação não existente!"</span><span class="p">)</span>
<span class="k">if</span> <span class="n">resultado</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Resultado: </span><span class="si">{0}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">resultado</span><span class="p">))</span>
</pre></div>
<p>Neste código foram acrescentadas as estruturas try / except, focando especificamente nas exceptions ValueError e ZeroDivisionError.</p>
<p>Todo código entre o bloco try ao levantar uma exceção esta será verificada na chamada except, caso ela tenha o mesmo nome da exceção levantada o bloco except correspondente será executado.</p>
<p>Acrescentei também a este código o import do módulo <tt class="docutils literal">sys</tt> para usar o método <tt class="docutils literal">sys.exit(1)</tt> que finaliza o script com um retorno de erro (1).</p>
<p>No Python temos uma infinidade de exceções para tratamento dos mais variados erros, porém esses dias "passeando" pelo Twitter encontrei uma imagem bem interessante que desenha um fluxograma explicando as principais exceptions que acontecem no Python (enquanto você está programando, geralmente), veja a imagem:</p>
<img alt="exceções comuns do Python" src="images/resolucao-01-calculadora-basica-grupo-de-estudos/01.png" />
<p>Esta imagem da uma noção bem bacana de como analisar erros comuns no Python. Abaixo deixo outra versão do código que também funcionaria tranquilamente, mas desta vez verificando se o operando 2 é um número 0:</p>
<div class="highlight"><pre><span></span><span class="c1"># -*- coding: utf-8 -*-</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="n">operacao</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s2">"Qual operação deseja fazer? ( * / - +)</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">operando1</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="s2">"Digite operando1:</span><span class="se">\n</span><span class="s2">"</span><span class="p">))</span>
<span class="n">operando2</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="s2">"Digite operando2:</span><span class="se">\n</span><span class="s2">"</span><span class="p">))</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Digite somente números nos operandos 1 e 2"</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">resultado</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="n">operacao</span> <span class="o">==</span> <span class="s2">"+"</span><span class="p">:</span>
<span class="n">resultado</span> <span class="o">=</span> <span class="n">operando1</span> <span class="o">+</span> <span class="n">operando2</span>
<span class="k">elif</span> <span class="n">operacao</span> <span class="o">==</span> <span class="s2">"-"</span><span class="p">:</span>
<span class="n">resultado</span> <span class="o">=</span> <span class="n">operando1</span> <span class="o">-</span> <span class="n">operando2</span>
<span class="k">elif</span> <span class="n">operacao</span> <span class="o">==</span> <span class="s2">"*"</span><span class="p">:</span>
<span class="n">resultado</span> <span class="o">=</span> <span class="n">operando1</span> <span class="o">*</span> <span class="n">operando2</span>
<span class="k">elif</span> <span class="n">operacao</span> <span class="o">==</span> <span class="s2">"/"</span> <span class="ow">and</span> <span class="n">operando2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Operação inválida: Divisão por 0"</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">operacao</span> <span class="o">==</span> <span class="s2">"/"</span><span class="p">:</span>
<span class="n">resultado</span> <span class="o">=</span> <span class="n">operando1</span> <span class="o">/</span> <span class="n">operando2</span>
<span class="k">else</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Operação não existente!"</span><span class="p">)</span>
<span class="k">if</span> <span class="n">resultado</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Resultado: </span><span class="si">{0}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">resultado</span><span class="p">))</span>
</pre></div>
<div class="section" id="conclusao">
<h2>Conclusão</h2>
<p>Informação impotantíssima: Todo erro se torna um problema desde que não seja tratado adequadamente, então, sempre tenha em mente quando usar try / except em seu código.</p>
<p>E ai gostaram? Deixem suas críticas/sugestões e dúvidas abaixo do post e lembre-se que nenhuma dúvida é ruim, o ruim é você permanecer com ela.</p>
<p>That's all folks!</p>
</div>
Exercício: 02 - Média de alunos - Grupo de Estudos Python2016-03-07T23:43:00-03:002016-03-07T23:43:00-03:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2016-03-07:/exercicio-02-media-de-alunos-grupo-de-estudos-python.html<p class="first last">Exercício para resolução passado no Grupo de Estudos Python</p>
<p>Receba a nota (de 0 a 10) de 6 bimestres de 2 alunos, realize uma média ponderada de notas para cada aluno, sabendo que cada bimestre tem pesos diferentes:</p>
<ul class="simple">
<li>Primeiro bimestre: peso 2</li>
<li>Segundo bimestre: peso 3</li>
<li>Terceiro bimestre: peso 3</li>
<li>Quarto bimestre: peso 4</li>
<li>Quinto bimestre: peso 5</li>
<li>Sexto bimestre: peso 5</li>
</ul>
<p>Exemplo de cálculo:</p>
<ul class="simple">
<li>Aluno 1 tirou as notas: 7, 5, 3, 10, 9 e 8</li>
<li>(7*2 + 5*3 + 3*3 + 10*4 + 9*5 + 8*5) / (2 + 3 + 3 + 4 + 5 + 5) = 7.40</li>
<li>Aluno 1 tirou a nota B</li>
</ul>
<p>Feito o cálculo exiba a nota nos intervalos abaixo:</p>
<ul class="simple">
<li>Nota maior que 9 teve menção A</li>
<li>Nota maior que 7 e menor ou igual a 9 teve menção B</li>
<li>Nota maior que 5 e menor ou igual a 7 teve menção C</li>
<li>Nota menor que 5 teve menção D</li>
</ul>
<p>Para facilitar a resolução pense em dicionários, listas, tuplas e estruturas de repetição (for e while).</p>
<p>Semana que vem posto a resolução (todas as resoluções que conseguirmos).</p>
Você conhece o Grupy-SP ??2016-03-01T19:45:00-03:002016-03-01T19:45:00-03:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2016-03-01:/voce-conhece-o-grupy-sp.html<p class="first last">Explicando um brevemente o funcionamento do Grupy-SP.</p>
<p>O Grupy-SP é um Grupo de Python que se reune em São Paulo (capital e interior também) mensalmente (geralmente) para unir as pessoas da comunidade Python, mas não só de Python, pois essa galera é bem receptiva.</p>
<div class="section" id="como-sei-quando-acontecera-um-encontros">
<h2>Como sei quando acontecerá um encontros?</h2>
<p>Você precisará se cadastrar no <a class="reference external" href="http://www.meetup.com/pt-BR/Grupy-SP/">Meetup do Grupy-SP</a> para receber mais novidades sobre os encontros, eles geralmente são mensais porém não tem data definida.</p>
</div>
<div class="section" id="onde-acontecem-os-encontros">
<h2>Onde acontecem os encontros?</h2>
<p>Podemos chamar o Grupy-SP de um evento "itinerante" pois precisamos de um lugar e precisamos de um coordenador, quando acontece a junção destes dois elementos acontece um encontro!</p>
<p>O Grupy-SP nunca é no mesmo lugar, o grupo precisa de colaboradores e de lugares para acontecer, você conhece um lugar bacana? Quer nos ajudar? Divulgue isso para nós, quem sabe você não poderá sediar um encontro na sua cidade!</p>
</div>
<div class="section" id="quem-pode-participar-do-grupy-sp">
<h2>Quem pode participar do Grupy-SP?</h2>
<p>Todas as pessoas podem participar do Grupy-SP.</p>
</div>
<div class="section" id="qual-o-nivel-das-palestras-participantes">
<h2>Qual o nível das palestras/participantes?</h2>
<p>As palestras e participantes são desde quem está começando no mundo Python até quem é <a class="reference external" href="https://twitter.com/ramalhoorg">Luciano Ramalho</a> (brincadeira hehehe).... mais experiente no mundo Python.</p>
</div>
<div class="section" id="quanto-paga-para-participar-do-grupy-sp">
<h2>Quanto paga para participar do Grupy-SP?</h2>
<p>Não paga nada! Até hoje nunca foi cobrado nem um centavo de ninguém.</p>
</div>
<div class="section" id="qual-a-dinamica-dos-encontros">
<h2>Qual a dinâmica dos encontros?</h2>
<p>Geralmente sempre tem um espaço para a galera bater papo (o que eu acho sensacional) e também tem palestras voluntárias e lightining talks (palestras menores de 5 a 15min). Quando existem doações ou patrocínio também tem coffe-breaks.... você trabalha com coffe-breaks e quer ajudar? Divulgue isso para a galera, também seria bacana!</p>
</div>
<div class="section" id="quando-e-o-proximo-encontro">
<h2>Quando é o próximo encontro?</h2>
<p>O ideal é sempre ficar de olho no <a class="reference external" href="http://www.meetup.com/pt-BR/Grupy-SP/">Meetup do Grupy-SP</a> o próximo encontro agora é dia 12/03/2016 e acontecerá na USP Leste em SP.</p>
</div>
<div class="section" id="onde-consigo-o-material-das-palestras-anteriores">
<h2>Onde consigo o material das palestras anteriores?</h2>
<p>O Grupy-SP tem um repositório no <a class="reference external" href="https://github.com/grupy-sp/">Github</a> onde tem um material bem bacana. Se quiser ir direto verificar o material de encontros anteriores <a class="reference external" href="https://github.com/grupy-sp/encontros">clique aqui</a>.</p>
</div>
<div class="section" id="como-entrar-em-contato-com-os-membros-do-grupy-sp">
<h2>Como entrar em contato com os membros do Grupy-SP?</h2>
<ul class="simple">
<li>Github - <a class="reference external" href="https://github.com/grupy-sp/encontros">https://github.com/grupy-sp/encontros</a></li>
<li>Facebook - <a class="reference external" href="https://www.facebook.com/grupysp">https://www.facebook.com/grupysp</a></li>
<li>Twitter - <a class="reference external" href="https://twitter.com/grupysp">https://twitter.com/grupysp</a></li>
<li>Slack - <a class="reference external" href="https://grupysp.herokuapp.com">https://grupysp.herokuapp.com</a></li>
<li>Meetup - <a class="reference external" href="http://www.meetup.com/pt/Grupy-SP/">http://www.meetup.com/pt/Grupy-SP/</a></li>
<li>Google Groups - <a class="reference external" href="https://groups.google.com/forum/#!forum/grupy-sp">https://groups.google.com/forum/#!forum/grupy-sp</a></li>
<li>Site - <a class="reference external" href="http://grupy-sp.github.io/">http://grupy-sp.github.io/</a></li>
</ul>
</div>
<div class="section" id="concluindo">
<h2>Concluindo</h2>
<p>Eu já palestrei diversas vezes no Grupy-SP e pretendo continuar palestrando, como não tenho um espaço fácil para sediar o evento minha forma de contribuição é palestrando, se vocês se interessarem, abaixo deixo o link das palestras que fiz no Grupy-SP:</p>
<ul class="simple">
<li><a class="reference external" href="https://speakerdeck.com/rafaelhenrique/nao-trabalho-com-python-como-posso-aprender">Não trabalho com Python, como posso aprender?</a></li>
<li><a class="reference external" href="https://speakerdeck.com/rafaelhenrique/mongodb-crud-create-read-update-e-delete-com-pymongo">MongoDB CRUD (Create, Read, Update e Delete) com Pymongo</a></li>
<li><a class="reference external" href="https://www.youtube.com/watch?v=bd3T8ORvWXY">Vídeo: MongoDB CRUD (Create, Read, Update e Delete) com Pymongo</a></li>
<li><a class="reference external" href="https://speakerdeck.com/rafaelhenrique/raspagem-de-dados-com-scrapy">Raspagem de dados com Scrapy</a></li>
</ul>
<p>That's all folks!</p>
</div>
Exercício: 01 - Calculadora básica - Grupo de Estudos Python2016-02-29T23:43:00-03:002016-02-29T23:43:00-03:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2016-02-29:/exercicio-01-calculadora-basica-grupo-de-estudos-python.html<p class="first last">Exercício para resolução passado no Grupo de Estudos Python</p>
<p>Baseado no script abaixo:</p>
<div class="highlight"><pre><span></span><span class="c1"># -*- coding: utf-8 -*-</span>
<span class="n">primeiro</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s2">"Digite o primeiro número: "</span><span class="p">)</span>
<span class="n">segundo</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s2">"Digite o segundo número: "</span><span class="p">)</span>
<span class="n">operacao</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s2">"Digite a operação: "</span><span class="p">)</span>
<span class="k">if</span> <span class="n">operacao</span> <span class="o">==</span> <span class="s2">"+"</span><span class="p">:</span>
<span class="n">primeiro</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">primeiro</span><span class="p">)</span>
<span class="n">segundo</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">segundo</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Resultado: </span><span class="si">{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">primeiro</span> <span class="o">+</span> <span class="n">segundo</span><span class="p">))</span>
<span class="k">else</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Você digitou a operação errada! Digite: + - / *"</span><span class="p">)</span>
</pre></div>
<p>Vamos terminar a calculadora básica (com as 4 operações pelo menos) em Python?
Se precisar de ajuda para resolver o exercício me contate ou assista o vídeo (que está upando neste momento) da aula 02 do Grupo de Estudos Python.</p>
<p>Se você quiser estudar um pouquinho mais e não somente resolver o exercício você poderá acrescentar novas funcionalidades a nossa calculadora, tais como: fazer exponenciação, calcular seno, calcular coseno ... etc</p>
<p>Para facilitar seu divertimento use a biblioteca <a class="reference external" href="https://docs.python.org/3.5/library/math.html">math</a> padrão do Python.</p>
<p>Você poderá ver a <a class="reference external" href="http://blog.abraseucodigo.com.br/resolucao-01-calculadora-basica-grupo-de-estudos.html">resolução aqui</a>.</p>
Instalando qualquer versão do Python no Linux/Mac OSX utilizando pyenv2016-02-24T21:21:00-03:002016-02-24T21:21:00-03:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2016-02-24:/instalando-qualquer-versao-do-python-no-linux-macosx-utilizando-pyenv.html<p class="first last">Muitos tem a dúvida de como instalar Python no Linux e Mac OSX, hoje vou postar aqui um passo a passo bem básico pra que você consiga instalar Python 3.5 (e qualquer outra) no Linux e com poucas modificações também no Mac OSX</p>
<p>Muitos tem a dúvida de como instalar Python no Linux e Mac OSX, hoje vou postar aqui um passo a passo bem básico pra que você consiga instalar Python 3.5 (e qualquer outra) no Linux e com poucas modificações também no Mac OSX.</p>
<p>Não abordarei especificamente o passo a passo para realizar a instalação no Mac OSX, pois no atual momento não disponho de um computador com tal Sistema operacional em mãos. Porém já ajudei alguns amigos a realizar o procedimento mostrado neste post também no Mac OSX, e sem muitas dores extras conseguimos realizar estes mesmos procedimentos no Mac OSX.</p>
<p>Vou usar como Linux base a distribuição Debian (a qual gosto muito) na versão Jessie 8.3.0. Estou especificando a versão do Debian e a versão do Python (estável) pois como ambos evoluem com o tempo (e muito rapidamente) então creio eu que logo esse post irá ficar velho.</p>
<div class="section" id="baixar-instalar-e-compilar">
<h2>Baixar, instalar e compilar?</h2>
<p>Para os usuários mais "roots" do sistema operacional Linux a primeira coisa que vem em mente é <tt class="docutils literal">"vou baixar o tar.gz, descompactar, configurar e compilar e o negócio vai sair funcionando lindamente"</tt>, e realmente este processo funciona. Porém somos programadores, e programadores pensam em fazer a coisa da forma mais fácil possível e mais durável possível.</p>
<p>Agora imagina se você pudesse instalar diversas versões do Python digitando apenas um comando e que ainda você pudesse trocar de versão facilmente entre todas as instaladas no seu computador.... pensou?</p>
<p>Pois é os caras que criaram o <tt class="docutils literal">pyenv</tt> também!</p>
<p>Porém não confunda, <tt class="docutils literal">virtualenv</tt> é uma coisa e <tt class="docutils literal">pyenv</tt> é outra. Abordei o assunto <tt class="docutils literal">virtualenv</tt> em outro post, para <a class="reference external" href="http://blog.abraseucodigo.com.br/virtualenv-pip-pra-que-servem.html">entender melhor sobre o virtualenv clique aqui</a> .</p>
</div>
<div class="section" id="o-que-e-o-pyenv">
<h2>O que é o pyenv?</h2>
<p><tt class="docutils literal">Pyenv</tt> é um projeto muito legal que faz com que você consiga ter várias versões do Python na sua máquina e que eles coexistam em paz. Também torna mais fácil (absurdamente fácil) a instalação de QUALQUER distribuição do Python, inclusive as mais "exóticas" como o Anaconda, Miniconda, IronPython, Jython e o Pypy. Dentre estas versões possíveis também é possível instalar o Python 3.5.1 muito facilmente.</p>
</div>
<div class="section" id="instalando-pyenv">
<h2>Instalando pyenv</h2>
<p>Se você usa Mac OSX pode começar com este link:</p>
<ul class="simple">
<li><a class="reference external" href="https://github.com/yyuu/pyenv#homebrew-on-mac-os-x">Homebrew on Mac OS X</a></li>
</ul>
<p>Se você usa Linux você vai começar instalando as dependências de um script que facilita a instalação do <tt class="docutils literal">pyenv</tt> pra você. Este script depende de dois comandos <tt class="docutils literal">git</tt> e <tt class="docutils literal">curl</tt>, caso você não os tenha instalados, execute o comando abaixo (este comando você precisa executar como root ou com sudo):</p>
<div class="highlight"><pre><span></span># apt-get install curl git
</pre></div>
<p>Feito isso vamos prosseguir com instalação usando um script automatizado que a galera do <tt class="docutils literal">pyenv</tt> fez. Execute como usuário normal (não root e sem sudo) o seguinte script abaixo:</p>
<div class="highlight"><pre><span></span>$ curl -L https://raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | bash
</pre></div>
<p>Ao terminar a execução deste script uma saída assim será exibida:</p>
<div class="highlight"><pre><span></span>WARNING: seems you still have not added 'pyenv' to the load path.
# Load pyenv automatically by adding
# the following to ~/.bash_profile:
export PATH="/home/rafael/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"
</pre></div>
<p>Obviamente que o caminho dentro da sua variável PATH estará diferente do meu, pois o meu usuário se chama <tt class="docutils literal">rafael</tt>.</p>
<p>Basicamente ao final da instalação conforme mostrado acima o <tt class="docutils literal"><span class="pre">pyenv-installer</span></tt> manda você adicionar as linhas que ele te mostra no seu arquivo <tt class="docutils literal"><span class="pre">~/.bash_profile</span></tt>, eu sinceramente não gosto de adicionar essas coisas no <tt class="docutils literal"><span class="pre">~/.bash_profile</span></tt> ao invés disso eu sempre adiciono ao <tt class="docutils literal"><span class="pre">~/.bashrc</span></tt>.</p>
<p>Então vamos fazer isso, abra seu arquivo <tt class="docutils literal"><span class="pre">~/.bashrc</span></tt> com seu editor favorito e adicione essas linhas no final do arquivo, conforme mostrado nesta imagem abaixo:</p>
<img alt=".bashrc" src="images/instalando-qualquer-versao-do-python-no-linux-macosx-utilizando-pyenv/01.png" />
<p>Fiz um comentário (linha em azul) apenas para informar que esta é a configuração do <tt class="docutils literal">pyenv</tt> e manter as coisas organizadas. Feita esta configuração basta executar (como usuário normal não root nem sudo) o comando abaixo, para que seu ambiente seja "populado" com as novas variáveis da configuração que você fez:</p>
<div class="highlight"><pre><span></span>$ source ~/.bashrc
</pre></div>
<p>Desta forma você poderá chamar o comando <tt class="docutils literal">pyenv</tt> no seu terminal:</p>
<div class="highlight"><pre><span></span>$ pyenv
pyenv 20160202-10-ga6f1f48
Usage: pyenv <command> [<args>]
Some useful pyenv commands are:
commands List all available pyenv commands
local Set or show the local application-specific Python version
global Set or show the global Python version
shell Set or show the shell-specific Python version
install Install a Python version using python-build
uninstall Uninstall a specific Python version
rehash Rehash pyenv shims (run this after installing executables)
version Show the current Python version and its origin
versions List all Python versions available to pyenv
which Display the full path to an executable
whence List all Python versions that contain the given executable
See `pyenv help <command>' for information on a specific command.
For full documentation, see: https://github.com/yyuu/pyenv#readme
</pre></div>
<p>De cara você vai ter um help bem bacana para entender os comandos. Se quando você executou <tt class="docutils literal">pyenv</tt> a saída mostrada foi parecida, sua instalação correu perfeitamente bem! Caso tenha sido diferente e precisar de alguma ajuda, pode comentar abaixo do post ou me chamar am alguns dos contatos ao lado esquerdo do blog.</p>
</div>
<div class="section" id="verificando-as-versoes-disponiveis-para-instalacao">
<h2>Verificando as versões disponíveis para instalação</h2>
<div class="highlight"><pre><span></span>$ pyenv install -l
Available versions:
.... linhas omitidas ....
3.0.1
3.1
3.1.1
3.1.2
3.1.3
3.1.4
3.1.5
3.2-dev
3.2
3.2.1
3.2.2
3.2.3
3.2.4
3.2.5
3.2.6
3.3.0
3.3-dev
3.3.1
3.3.2
3.3.3
3.3.4
3.3.5
3.3.6
3.4.0
3.4-dev
3.4.1
3.4.2
3.4.3
3.4.4
3.5.0
3.5-dev
3.5.1
3.6-dev
.... linhas omitidas ....
</pre></div>
<p>Omiti algumas linhas para que a saída não ficasse tão grande, pois realmente são muitas as versões.</p>
</div>
<div class="section" id="instalando-o-python-3-5-1">
<h2>Instalando o Python 3.5.1</h2>
<p>No Debian (Ubuntu também usa os mesmos pacotes) você precisará instalar algumas dependências para que o <tt class="docutils literal">pyenv</tt> compile o Python para você, como root ou usando sudo execute o comando abaixo:</p>
<div class="highlight"><pre><span></span># apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev
</pre></div>
<p>Caso você esteja usando outro sistema operacional que não seja o Debian, você poderá consultar as dependências que precisará instalar neste link:</p>
<ul class="simple">
<li><a class="reference external" href="https://github.com/yyuu/pyenv/wiki/Common-build-problems">https://github.com/yyuu/pyenv/wiki/Common-build-problems</a></li>
</ul>
<p>Depois de instalar as dependências necessárias, execute (como usuário normal sem root e sem sudo) o comando para instalação do Python 3.5.1 conforme mostrado abaixo:</p>
<div class="highlight"><pre><span></span>$ pyenv install 3.5.1
</pre></div>
<p>A saída será similar a esta:</p>
<div class="highlight"><pre><span></span>Downloading Python-3.5.1.tgz...
-> https://www.python.org/ftp/python/3.5.1/Python-3.5.1.tgz
Installing Python-3.5.1...
Installed Python-3.5.1 to /home/rafael/.pyenv/versions/3.5.1
</pre></div>
<p>Se a sua saída foi similar a esta o Python 3.5.1 foi instalado com sucesso! Caso tenha sido diferente e precisar de alguma ajuda, pode comentar abaixo do post ou me chamar am alguns dos contatos ao lado esquerdo do blog.</p>
</div>
<div class="section" id="como-usar-o-s-python-s-instalado-s">
<h2>Como usar o(s) Python(s) instalado(s)?</h2>
<p>Todo sistema Linux/Mac OSX geralmente já vem com alguma versão do Python instalada (2.6, 2.7, 3 ou 3.4 geralmente), o <tt class="docutils literal">pyenv</tt> vai isolar a versão do Python do sistema operacional caso você precise dele posteriormente você conseguirá usá-lo sem problemas.</p>
<p>Para ver as versões disponíveis para uso execute o comando abaixo:</p>
<div class="highlight"><pre><span></span>$ pyenv versions
* system (set by /home/rafael/.pyenv/version)
3.5.1
</pre></div>
<p>No caso agora no meu sistema eu tenho a versão <tt class="docutils literal">system</tt> que é a que estava instalada anteriormente com meu sistema operacional e também a versão 3.5.1. O símbolo <tt class="docutils literal">*</tt> especifica qual a versão você está utilizando neste momento.</p>
<p>Você poderá mudar a versão utilizada usando o parâmetro <tt class="docutils literal">global</tt> do <tt class="docutils literal">pyenv</tt> desta maneira:</p>
<div class="highlight"><pre><span></span>$ pyenv global 3.5.1
$ pyenv versions
system
* 3.5.1 (set by /home/rafael/.pyenv/version)
</pre></div>
<p>Observe que ao executar <tt class="docutils literal">pyenv versions</tt> novamente ele mostrou que a versão a ser utilizada agora é 3.5.1, vamos executar o interpretador Python para ver se tudo correu bem:</p>
<div class="highlight"><pre><span></span>$ python
Python 3.5.1 (default, Feb 24 2016, 21:38:32)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
</pre></div>
<p>Perfeitamente bem! Agora vamos mudar para a versão <tt class="docutils literal">system</tt> e chamar o interpretador novamente para ver o que acontece:</p>
<div class="highlight"><pre><span></span>$ pyenv global system
$ pyenv versions
* system (set by /home/rafael/.pyenv/version)
3.5.1
$ python
$ python
Python 2.7.9 (default, Mar 1 2015, 12:57:24)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
</pre></div>
<p>Funcionou perfeitamente bem novamente!</p>
<p>Então agora você possui um <tt class="docutils literal">pyenv</tt> funcional juntamente com uma versão 3.5.1 do Python já instalada e apto a instalar qualquer outra disponível!</p>
</div>
<div class="section" id="voce-e-um-cara-preocupado-eu-tambem">
<h2>Você é um cara preocupado? Eu também...</h2>
<p>Outra coisa que você não precisa se preocupar é que o <tt class="docutils literal">pyenv</tt> instala e faz modificações no seu sistema a nível de usuário, ou seja, nada do que você faça com ele vai desgraçar seu sistema operacional inteiro, no máximo vai dar uma zuada na configuração do Python do seu usuário somente, em casos extremos você poderá desinstalar o <tt class="docutils literal">pyenv</tt> de forma fácil simplesmente apagando a pasta dele que fica em sua home:</p>
<div class="highlight"><pre><span></span>$ rm -fr ~/.pyenv
</pre></div>
<p>E posteriormente remover as configurações de variáveis que colocamos no <tt class="docutils literal"><span class="pre">~/.bashrc</span></tt>.</p>
</div>
<div class="section" id="conclusao">
<h2>Conclusão</h2>
<p>O <tt class="docutils literal">pyenv</tt> facilita muito as coisas pra nós, com ele podemos testar distribuições do Python das quais nunca ouvimos falar antes, sem o mínimo esforço para instalar e também para desinstalar (utilizando o parâmetro <tt class="docutils literal">uninstall</tt> do pyenv). Espero que tenham gostado do post! Deixem seus comentários!</p>
</div>
<div class="section" id="referencias">
<h2>Referências</h2>
<p>Links (ambos com documentações em seus próprios repositórios):</p>
<ul class="simple">
<li><a class="reference external" href="https://github.com/yyuu/pyenv">Repositório oficial do pyenv</a></li>
<li><a class="reference external" href="https://github.com/yyuu/pyenv-installer">Repositório oficial do pyenv-installer</a></li>
<li><a class="reference external" href="https://github.com/yyuu/pyenv/wiki/Common-build-problems">Problemas que você pode vir a encarar</a></li>
</ul>
</div>
Python uma linguagem de tipagem dinâmica e forte2016-02-23T22:43:00-03:002016-02-23T22:43:00-03:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2016-02-23:/python-uma-linguagem-de-tipagem-dinamica-e-forte.html<p class="first last">O que significa tipagem dinâmica? E tipagem forte?</p>
<p>Todos que começam a estudar Python lêem que "Python é uma linguagem de tipagem dinâmica e forte" mas o que isso significa?</p>
<div class="section" id="tipagem-dinamica">
<h2>Tipagem dinâmica</h2>
<p>Assim como Python hoje existem inúmeras linguagens no mercado que são dinamicamente tipadas.</p>
<p>Referenciando especificamente o Python para explicar a questão: Tipagem dinâmica significa que o próprio interpretador do Python infere o tipo dos dados que uma variável recebe, sem a necessidade que você, o usuário da linguagem diga de que tipo determinada variável é. Exemplo de um script Python:</p>
<div class="highlight"><pre><span></span><span class="n">i</span><span class="p">,</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">20</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"O resultado é: "</span><span class="p">,</span> <span class="n">i</span> <span class="o">+</span> <span class="n">j</span><span class="p">)</span>
</pre></div>
<p>Vamos melhorar esta explicação com um exemplo de tipagem estática, a linguagem C. A linguagem C é estaticamente tipada, isso significa que deveremos sempre determinar qual o tipo de dados uma variável irá receber, exemplo de um programa em C:</p>
<div class="highlight"><pre><span></span><span class="c1">#include <stdio.h></span>
<span class="nb">int</span> <span class="n">main</span><span class="p">(){</span>
<span class="nb">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">20</span><span class="p">;</span>
<span class="n">printf</span><span class="p">(</span><span class="s2">"O resultado é: </span><span class="si">%d</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="n">i</span><span class="o">+</span><span class="n">j</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
<p>Ambos os programas acima fazem a mesma coisa, somam 10 e 20 e mostram um resultado na tela, a diferença é que em C os tipos são estáticos então é necessário que o programador diga quais os tipos que <tt class="docutils literal">i</tt> e <tt class="docutils literal">j</tt> receberão como valores aceitáveis.</p>
</div>
<div class="section" id="tipagem-forte">
<h2>Tipagem forte</h2>
<p>Assim como Python hoje existem inúmeras linguagens no mercado que são fortemente tipadas.</p>
<p>Referenciando especificamente o Python para explicar a questão: Tipagem forte significa que o interpretador do Python avalia as expressões (evaluate) e não faz coerções automáticas entre tipos não compatíveis (conversões de valores), ou seja:</p>
<div class="highlight"><pre><span></span><span class="n">i</span><span class="p">,</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span> <span class="s2">"Rafael"</span>
<span class="nb">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">j</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"O resultado é: "</span><span class="p">,</span> <span class="n">i</span> <span class="o">+</span> <span class="n">j</span><span class="p">)</span>
</pre></div>
<p>Neste código acima definimos <tt class="docutils literal">i</tt> como <tt class="docutils literal">10</tt> e <tt class="docutils literal">j</tt> como <tt class="docutils literal">"Rafael"</tt>, desta forma temos um inteiro em <tt class="docutils literal">i</tt> e uma string em <tt class="docutils literal">j</tt>, ao executar este código veja o que acontece:</p>
<div class="highlight"><pre><span></span>$ python3 teste.py
10
Rafael
Traceback (most recent call last):
File "teste", line 4, in <module>
print("O resultado é: ", i + j)
TypeError: unsupported operand type(s) for +: 'int' and 'str'
</pre></div>
<p>Recebo uma exception chamada <tt class="docutils literal">TypeError</tt> ou seja, ao fazer operações com tipos incompatíveis, o Python não converte automaticamente esses tipos pra você, ele vai dar erro! Isso é bom, pois assim você terá a certeza que o seu resultado é mais consistente.</p>
<p>Agora assim como temos linguagens com tipagem forte, temos também linguagens com tipagem fraca como por exemplo Javascript, vejamos o código Javascript a seguir:</p>
<div class="highlight"><pre><span></span><span class="n">var</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
<span class="n">var</span> <span class="n">j</span> <span class="o">=</span> <span class="s2">"Rafael"</span><span class="p">;</span>
<span class="n">console</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="n">j</span><span class="p">);</span>
</pre></div>
<p>Executando o código anterior obteríamos o resultado <tt class="docutils literal">10Rafael</tt>. Isso prova que o Javascript diferente do Python converte (faz coerção de tipos) ao executar operações de forma automática, isso faz com que seja uma linguagem de tipagem fraca.</p>
</div>
<div class="section" id="conclusao">
<h2>Conclusão</h2>
<p>Espero que tenha ficado claro para você como é essa questão da tipagem do Python. Em caso de dúvidas/sugestão não deixe de fazer seu comentário abaixo do post.</p>
</div>
<div class="section" id="referencias">
<h2>Referências</h2>
<p>Livro: Python para desenvolvedores 2.a edição.
Autor: Luiz Eduardo Borges
Distribuição gratuita no <a class="reference external" href="http://ark4n.wordpress.com/python/">site</a>.</p>
<p>Também tem um <a class="reference external" href="https://www.destroyallsoftware.com/talks/wat">vídeo</a> muito interessante (e bem curtinho) que mostra mais coisas a respeito de como outras linguagens tratam alguns casos bem bizarros (alguns envolvendo tipagem fraca/forte estática/dinâmica).</p>
</div>
Segunda chamada para o curso introdutório de Python2016-02-20T22:25:00-02:002016-02-20T22:25:00-02:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2016-02-20:/segunda-chamada-para-o-curso-introdutorio-de-python.html<p class="first last">Começa nesta segunda-feira 22/02/2016 um novo curso online introdutório de Python e totalmente gratuito</p>
<p>Começa nesta segunda-feira 22/02/2016 um novo curso online introdutório de Python e totalmente gratuito. Neste curso serão abordados aspectos básicos da linguagem de programação mais descolada do mercado <img style="margin-bottom: -0.25em;height:1.5em; display:inline-block;" src="/emojis/wink.png"></p>
<div class="section" id="como-me-inscrevo">
<h2>Como me inscrevo?</h2>
<p>Não tem inscrição basta entrar no grupo do google python-sorocaba e antes das aulas será enviado um link para acesso ao Hangout. Link para o grupo:</p>
<p><a class="reference external" href="https://groups.google.com/forum/#!forum/python-sorocaba">https://groups.google.com/forum/#!forum/python-sorocaba</a></p>
</div>
<div class="section" id="que-horas-sera">
<h2>Que horas será?</h2>
<p>Como trabalho fora da minha cidade o curso tem prazo mínimo e máximo para começar em termos de horário:</p>
<ul class="simple">
<li>Prazo mínimo para iniciar a aula: a partir das 20hrs</li>
<li>Término mínimo para terminar a aula: até 22hrs</li>
<li>Prazo máximo para iniciar a aula: a partir das 21hrs</li>
<li>Término máximo para finalizar a aula: até 23hrs</li>
</ul>
<p>O horário de início está ligado ao horário que meu ônibus chega na minha casa.</p>
</div>
<div class="section" id="que-dia-sera">
<h2>Que dia será?</h2>
<p>As segundas-feiras a noite.</p>
</div>
<div class="section" id="quanto-eu-pago">
<h2>Quanto eu pago?</h2>
<p>NADA! Em dinheiro nada! Você paga em dedicação e força de vontade para estudar. Não pretendo ensinar nada a quem não quiser aprender.</p>
</div>
<div class="section" id="como-surgiu-essa-ideia">
<h2>Como surgiu essa idéia?</h2>
<p>Pode ler este <a class="reference external" href="http://blog.abraseucodigo.com.br/grupo-de-estudos-python-sorocaba.html">post</a> para saber mais sobre a história do Grupo de Estudos Python Sorocaba.</p>
</div>
<div class="section" id="com-quem-eu-falo-para-obter-informacoes-adicionais">
<h2>Com quem eu falo para obter informações adicionais?</h2>
<p>Comigo... o autor deste blog. Meus contatos estão a esquerda desta página. Mas você também pode fazer comentários abaixo deste post caso ache mais legal.</p>
</div>
<div class="section" id="por-que-as-segundas-foram-escolhidas-para-o-curso">
<h2>Por que as segundas foram escolhidas para o curso?</h2>
<p>Por meio de votação, divulguei os resultados da <a class="reference external" href="http://blog.abraseucodigo.com.br/pesquisa-grupo-de-estudo-python-sorocaba.html">votação aqui</a>.</p>
</div>
<div class="section" id="conclusao">
<h2>Conclusão</h2>
<p>Espero você lá no Hangout.</p>
</div>
Pesquisa Grupo de Estudo Python Sorocaba2016-02-20T13:02:00-02:002016-02-20T13:02:00-02:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2016-02-20:/pesquisa-grupo-de-estudo-python-sorocaba.html<p class="first last">Semana passada criei um formulário do Google para fazer uma pesquisa com as pessoas interessadas no Grupo de Estudos Python Sorocaba, agora divulgo o resultado da pesquisa aqui no blog</p>
<p>Semana passada criei um formulário do Google para fazer uma pesquisa com as pessoas interessadas no Grupo de Estudos Python Sorocaba, agora divulgo o resultado da pesquisa aqui no blog. Ao todo tivemos 28 pessoas interessadas no grupo que responderam a pesquisa.</p>
<p>Com base nesses resultados vou iniciar na próxima segunda-feira um novo curso de Python para iniciantes.</p>
<div class="section" id="para-novos-membros-do-grupo">
<h2>Para novos membros do Grupo</h2>
<p>Você ficou interessado em participar e não sabe como? Entre no grupo do Google <a class="reference external" href="https://groups.google.com/forum/#!forum/python-sorocaba">python-sorocaba</a> estou divulgando as novidades por lá.</p>
<p>Mas basicamente não tem mistério para poder participar, basta estar presente segunda-feira no Hangout (o link para acesso vou mandar no grupo python-sorocaba perto deste horário) a partir das 21hrs.</p>
<p>Não é necessário conhecimento prévio em Python, porém é necessário já conhecer alguma linguagem de programação ou pelo menos ter uma boa lógica em Algoritmos.</p>
</div>
<div class="section" id="resultado-da-pesquisa">
<h2>Resultado da pesquisa</h2>
<img alt="resultado da pesquisa" src="images/pesquisa-grupo-de-estudo-python-sorocaba.png" />
<p>That's all folks!</p>
</div>
Django assertFormErrorCode com py.test e com TestCase2016-02-18T21:16:00-02:002016-02-18T21:16:00-02:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2016-02-18:/django-assertformerrorcode-com-pytest-e-com-testcase.html<p class="first last">Pra que serve assertFormErrorCode do Django? Como utilizá-lo no py.test de forma tranquila?</p>
<p>Estive trabalhando em um teste hoje utilizando py.test e queria usar assertFormErrorCode padrão da suite de testes do Django, porém não queria extender meu teste de TestCase, como fazer?</p>
<p>Ta legal, se perdeu? Continue lendo abaixo que vou explicar o que é o assertFormErrorCode e também como utilizá-lo sem depender da suite de testes do Django.</p>
<div class="section" id="introducao">
<h2>Introdução</h2>
<p>Aprendi a usar recentemente vários asserts poderosos que o Django já te dá de presente na sua suite de testes. Quem me mostrou isso foi o <a class="reference external" href="https://twitter.com/henriquebastos">Henrique Bastos</a> no curso <a class="reference external" href="http://welcometothedjango.com.br/">Welcome to the Django</a> o qual recomendo a todos que façam, pois é um ótimo curso, e gostaria de fazer um agradecimento especial neste post! Parabéns Henrique pelo ótimo curso!</p>
</div>
<div class="section" id="codigos-postados-aqui">
<h2>Códigos postados aqui</h2>
<p>Os códigos de exemplo deste post em sua maioria podem ser visualizados e estudados no github em um commit anterior ao atual do projeto wttd, clique <a class="reference external" href="https://github.com/rafaelhenrique/wttd/tree/85c702631923cd3b47ec992c4cfcb06d9f172827">aqui para visualizar o projeto como um todo.</a></p>
</div>
<div class="section" id="mas-o-que-e-assertformerrorcode">
<h2>Mas o que é assertFormErrorCode?</h2>
<p>Este assert é mais um dos asserts interessantíssimos que o Django tem na sua suite de testes. Vejamos um exemplo de como esse cara pode ser usado nos seus testes:</p>
<p><tt class="docutils literal">Formulário básico de inscrição</tt></p>
<p><tt class="docutils literal">wttd/eventex/subscriptions/forms.py</tt></p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django</span> <span class="kn">import</span> <span class="n">forms</span>
<span class="kn">from</span> <span class="nn">django.core.exceptions</span> <span class="kn">import</span> <span class="n">ValidationError</span>
<span class="k">def</span> <span class="nf">validate_cpf</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">value</span><span class="o">.</span><span class="n">isdigit</span><span class="p">():</span>
<span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="s1">'CPF deve conter apenas números'</span><span class="p">,</span> <span class="s1">'digits'</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">11</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="s1">'CPF deve ter 11 números'</span><span class="p">,</span> <span class="s1">'length'</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">SubscriptionForm</span><span class="p">(</span><span class="n">forms</span><span class="o">.</span><span class="n">Form</span><span class="p">):</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s1">'Nome'</span><span class="p">)</span>
<span class="n">cpf</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s1">'CPF'</span><span class="p">,</span>
<span class="n">validators</span><span class="o">=</span><span class="p">[</span><span class="n">validate_cpf</span><span class="p">])</span>
<span class="n">email</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">EmailField</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s1">'Email'</span><span class="p">,</span> <span class="n">required</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">phone</span> <span class="o">=</span> <span class="n">forms</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s1">'Telefone'</span><span class="p">,</span> <span class="n">required</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="o">...</span> <span class="n">linhas</span> <span class="n">omitidas</span> <span class="o">...</span>
</pre></div>
<p>Perceba que este é um formulário bem básico onde podemos ver claramente que o campo <tt class="docutils literal">cpf</tt> é validado com regras bem básicas onde somente verificamos 2 coisas:</p>
<ul class="simple">
<li>se o cpf digitado não contem caracteres que não sejam números</li>
<li>se o cpf tem 11 dígitos</li>
</ul>
<p>O que eu acho mais interessante deste código é a questão do <tt class="docutils literal">ErrorCode</tt>! Em todo <tt class="docutils literal">ValidationError</tt> de podemos passar um <tt class="docutils literal">ErrorCode</tt> como parâmetro, que no caso das duas validações acima, podemos visualizar os códigos de erro <tt class="docutils literal">digits</tt> e <tt class="docutils literal">length</tt>.</p>
<p>Ta legal, você não curtiu? É eu também não via sentido nisso antes de ver o que era possível fazer com isso.</p>
</div>
<div class="section" id="pra-que-especificar-errorcodes-nos-seus-validationerror">
<h2>Pra que especificar <tt class="docutils literal">ErrorCodes</tt> nos seus <tt class="docutils literal">ValidationError</tt>?</h2>
<p>Quando você cria um teste comumente você vai querer saber quais as mensagens de erro o seu formulário está exibindo para os usuários, porém ai vem a questão, se você tem milhares de formulários com milhares de mensagens de erro, como é o trabalho que você terá?</p>
<p>Se você mudar uma mensagem de erro você terá que sair garimpando no seu código onde você estava usando aquela mensagem para poder modificar para a nova e isso gasta TEMPO e consequentemente DINHEIRO! Fora que é chato pra caramba!</p>
<p>Pensando nisso você pode criar seus testes apontando para o <tt class="docutils literal">ErrorCode</tt> assim se a mensagem mudar o <tt class="docutils literal">ErrorCode</tt> permanecerá o mesmo e você não quebra nenhum teste! Sensacional isso!</p>
</div>
<div class="section" id="show-me-the-code">
<h2>Show me the code</h2>
<p>Vejamos abaixo um teste criado no Django utilizando a verificação por <tt class="docutils literal">ErrorCode</tt>:</p>
<p><tt class="docutils literal">Teste do formulário básico de inscrição</tt></p>
<p><tt class="docutils literal">wttd/eventex/subscriptions/tests/test_form_subscription.py</tt></p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.test</span> <span class="kn">import</span> <span class="n">TestCase</span>
<span class="kn">from</span> <span class="nn">eventex.subscriptions.forms</span> <span class="kn">import</span> <span class="n">SubscriptionForm</span>
<span class="k">class</span> <span class="nc">SubscriptionFormTest</span><span class="p">(</span><span class="n">TestCase</span><span class="p">):</span>
<span class="o">...</span> <span class="n">linhas</span> <span class="n">omitidas</span> <span class="o">...</span>
<span class="k">def</span> <span class="nf">test_cpf_is_digit</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""CPF must only accept digits"""</span>
<span class="n">form</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">make_validated_form</span><span class="p">(</span><span class="n">cpf</span><span class="o">=</span><span class="s1">'ABCD5678901'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertFormErrorCode</span><span class="p">(</span><span class="n">form</span><span class="p">,</span> <span class="s1">'cpf'</span><span class="p">,</span> <span class="s1">'digits'</span><span class="p">)</span>
<span class="o">...</span> <span class="n">linhas</span> <span class="n">omitidas</span> <span class="o">...</span>
</pre></div>
<p>Olha ai que bacana esse teste! Limpo! Se você mudar a mensagem de erro no form o teste independe da mensagem, ele sempre irá olhar para o <tt class="docutils literal">ErrorCode</tt>.</p>
</div>
<div class="section" id="tudo-bonito-e-maravilhoso-mas">
<h2>Tudo bonito e maravilhoso mas...</h2>
<p>Mas... se você não usar a suite de testes do Django (herdando a classe TestCase) você não ganha esse assert maravilhoso chamado <tt class="docutils literal">assertFormErrorCode</tt>. E agora?</p>
</div>
<div class="section" id="procurando-uma-solucao-que-funciona-independente-do-testcase">
<h2>Procurando uma solução que funciona independente do TestCase</h2>
<p>Depois de garimpar algum tempo na internet (e amolar um amigo do trabalho) achei este post <a class="reference external" href="http://stackoverflow.com/questions/18781492/forms-validationerror-and-error-code">aqui</a> no stack overflow, o padroeiro dos desenvolvedores de software.</p>
<p>Neste post o cara colocou uma forma diferente de conseguir pegar o <tt class="docutils literal">ErrorCode</tt> que está na exception do <tt class="docutils literal">ValidationError</tt>, ele usou:</p>
<div class="highlight"><pre><span></span>>>> print(form.errors.as_json())
{"__all__": [
{"message": "Your account has not been activated.", "code": "inactive"}
]}
</pre></div>
<p>Desta forma o cara trouxe o erro do form em json e depois desta maneira ele consegue pegar uma key chamada "code" onde está o <tt class="docutils literal">ErrorCode</tt>. Achei bacana isso ai, porém não é tão simples como o cara postou (nunca é).</p>
</div>
<div class="section" id="minha-solucao">
<h2>Minha solução</h2>
<p>Criei uma classe de teste base minha chamada <tt class="docutils literal">BaseTest</tt>:</p>
<p><tt class="docutils literal">Classe base de testes</tt></p>
<p><tt class="docutils literal">este cara não está no github</tt></p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.test</span> <span class="kn">import</span> <span class="n">Client</span>
<span class="k">class</span> <span class="nc">BaseTest</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">setup</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">client</span> <span class="o">=</span> <span class="n">Client</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">endpoint</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">field_name</span><span class="p">):</span>
<span class="sd">"""</span>
<span class="sd"> Send post to form.</span>
<span class="sd"> endpoint: url for endpoint to test</span>
<span class="sd"> data: data to send to form</span>
<span class="sd"> field_name: name of form field on Django.</span>
<span class="sd"> """</span>
<span class="n">resp</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">client</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">data</span><span class="p">)</span>
<span class="n">context</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">resp</span><span class="p">,</span> <span class="s1">'context'</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="n">errors</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">if</span> <span class="n">context</span><span class="p">:</span>
<span class="n">errors</span> <span class="o">=</span> <span class="n">resp</span><span class="o">.</span><span class="n">context</span><span class="p">[</span><span class="s1">'form'</span><span class="p">]</span><span class="o">.</span><span class="n">errors</span><span class="o">.</span><span class="n">as_data</span><span class="p">()[</span><span class="n">field_name</span><span class="p">]</span>
<span class="n">errors</span> <span class="o">=</span> <span class="p">[</span><span class="n">error</span><span class="o">.</span><span class="n">code</span> <span class="k">for</span> <span class="n">error</span> <span class="ow">in</span> <span class="n">errors</span><span class="p">]</span>
<span class="k">return</span> <span class="n">resp</span><span class="p">,</span> <span class="n">errors</span>
</pre></div>
<p>Meu problema era fazer um post retornar os <tt class="docutils literal">ErrorCodes</tt> de forma fácil, com essa classe <tt class="docutils literal">BaseTest</tt> consegui atingir meu objetivo. Explicando de forma detalhada:</p>
<ul class="simple">
<li>Primeiro importei o <tt class="docutils literal">Client</tt> do Django que é um cara que eu realmente queria usar para fazer meus requests;</li>
<li>O <tt class="docutils literal">setup</tt> do py.test é tudo em minúsculo mesmo (diferente do Django);</li>
<li>Ao colocar criar o <tt class="docutils literal">self.client</tt> consigo usar o mesmo client para o resto da classe em forma de atributo;</li>
<li>Criei um método de post para ser usado pela classe filha, onde eu retorno resp que é o response do request que o <tt class="docutils literal">self.client.post</tt> executa e também retorno uma lista contendo os meus <tt class="docutils literal">ErrorCodes</tt> todos bonitos em uma única lista.</li>
</ul>
<p>Bem feito isso é só usar esse cara! Código de exemplo de uso:</p>
<p><tt class="docutils literal">Teste que usa a classe BaseTest, baseada na classe SubscriptionFormTest</tt></p>
<p><tt class="docutils literal">este cara não está no github</tt></p>
<div class="highlight"><pre><span></span><span class="o">...</span><span class="n">linhas</span> <span class="n">omitidas</span><span class="o">...</span>
<span class="k">class</span> <span class="nc">TestSubscriptionForm</span><span class="p">(</span><span class="n">BaseTest</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">test_post_error</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">resp</span><span class="p">,</span> <span class="n">errors</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="n">ENDPOINT</span><span class="p">,</span> <span class="p">{</span><span class="s2">"cpf"</span> <span class="p">:</span> <span class="s2">"ABC"</span><span class="p">},</span> <span class="s1">'cpf'</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">resp</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">200</span>
<span class="k">assert</span> <span class="s1">'digits'</span> <span class="ow">in</span> <span class="n">errors</span>
<span class="o">...</span><span class="n">linhas</span> <span class="n">omitidas</span><span class="o">...</span>
</pre></div>
<p>Desta forma este teste só vai passar caso o <tt class="docutils literal">ErrorCode</tt> <tt class="docutils literal">digits</tt> sejá levantado como exceção do <tt class="docutils literal">ValidationError</tt>.</p>
<p>Basicamente foi essa a brincadeira de hoje, eu achei bem legal, mesmo sem extender o TestCase padrão do Django conseguimos fazer a mesma coisa. Neste caso foi um pouco diferente pois o meu teste era pra validar um post e não propriamente um form, mas creio que a idéia tenha ficado clara, caso não, se não entenderem alguma coisa não deixem de me contatar.</p>
</div>
<div class="section" id="conclusao">
<h2>Conclusão</h2>
<p>Quando você não consegue usar um recurso padrão Django dentro do Django para fazer alguma coisa, nunca se esqueça que o Django é Python, e sempre você conseguirá fazer algo para contornar a situação de uma forma bacana.</p>
<p>Bom é isso espero que tenham gostado do post! Deixem seus comentários!</p>
</div>
virtualenv/pip pra que servem?2016-02-17T21:18:00-02:002016-02-17T21:18:00-02:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2016-02-17:/virtualenv-pip-pra-que-servem.html<p class="first last">Existem muitos posts pela internet afora dizendo como usar virtualenv e pip, e realmente é muito fácil. Mas para um iniciante a pergunta que vem a mente é... pra que serve virtualenv? Pra que serve pip?</p>
<p>Existem muitos posts pela internet afora dizendo como usar virtualenv e pip, e realmente é muito fácil. Mas para um iniciante a pergunta que vem a mente é... pra que serve virtualenv? Pra que serve pip?</p>
<div class="section" id="explicando-para-quem-nunca-desenvolveu-em-python">
<h2>Explicando para quem nunca desenvolveu em Python</h2>
<p>Imagine que você tem um programa na sua máquina em uma versão 1.0.0 e em dado momento você quer instalar a versão 1.0.1, porém, você também precisa da versão 1.0.0 pois você tem algum arquivo que não abre na versão mais nova.</p>
<p>Pois é, este problema também acontece com os desenvolvedores Python, e a solução criada para este problema foi o nosso amigo <tt class="docutils literal">virtualenv</tt>.</p>
</div>
<div class="section" id="o-virtualenv">
<h2>O virtualenv</h2>
<p>Uilizando o <tt class="docutils literal">virtualenv</tt> você consegue isolar as bibliotecas do projeto A em relação ao projeto B.</p>
<p>Imagine que em determinado momento que o projeto A começou a ser desenvolvido você ainda não tinha Python3 e optou por usar Python2 e mais um monte de bibliotecas do Python2, porém o projeto B usa Python3 e obviamente você não vai querer usar as bibliotecas do Python2 pois você deu uma evoluída nesse projeto e quer usar tudo que existe de mais novo no mundo Python.</p>
<p>Já imaginou a bagunça que seu sistema operacional vai fazer? O S.O. vai colocar a versão antiga e a nova no mesmo diretório e quando você chamar o comando <cite>import</cite> do Python para importar sua biblioteca você sempre ficará em dúvida de qual a versão você importou. Isso vira um caos com o passar do tempo, principalmente depois de você instalar muitas bibliotecas.</p>
<p>Apenas fazendo um comentário pertinente, biblioteca também pode ser chamadas de módulos em Python e vice-versa. Estou chamando de biblioteca pois fica mais fácil para as pessoas que vem de outras linguagens entenderem.</p>
</div>
<div class="section" id="o-pip">
<h2>O pip</h2>
<p>Falei um pouco sobre <tt class="docutils literal">virtualenv</tt>, mas e o <tt class="docutils literal">pip</tt>?</p>
<p>O <tt class="docutils literal">pip</tt> é o "gerenciador de pacotes" do Python. Assim como o <a class="reference external" href="https://www.debian.org/doc/manuals/apt-howto/ch-apt-get.pt-br.html">apt-get</a> e o <a class="reference external" href="http://brew.sh/">homebrew</a> que são os gerenciadores de pacotes do Ubuntu/Mac OSX o <a class="reference external" href="https://pypi.python.org/pypi/pip">pip</a> é o instalador de bibliotecas de terceiros padrão do Python.</p>
<p>Com o pip você poderá instalar qualquer biblioteca disponível no famoso site de bibliotecas da Python Software Foundation o <a class="reference external" href="https://pypi.python.org/pypi">PyPi</a>. Eu não tenho ideia de como verificar quantas bibliotecas Python existem hoje no PyPi, mas posso dizer que são centenas, ou milhares! São muitos MESMO, para os mais variados fins.</p>
</div>
<div class="section" id="hands-on">
<h2>Hands-on</h2>
<p>Vamos criar um projeto chamado <tt class="docutils literal">myprojectA</tt> e outro chamado <tt class="docutils literal">myprojectB</tt> para demonstrar o uso do isolamento de ambientes que <tt class="docutils literal">virtualenv</tt> nos ajuda a fazer, também iremos instalar bibliotecas com o <tt class="docutils literal">pip</tt> durante o processo.</p>
<p>Para executar este passo a passo estou usando Python 3.5.X:</p>
<ul class="simple">
<li>Se você não tem Python 3.5.X instalado e usar Windows clique <a class="reference external" href="http://blog.abraseucodigo.com.br/instalando-python35-no-windows-7.html">aqui</a> para ir ao post que escrevi ontem descrevendo este procedimento;</li>
<li>Se você não tem Python 3.5.X instalado e usar Linux ou Mac OSX você poderá usar o apt-get, yum ou homebrew para instalar Python3.5.X no seu computador de forma absolutamente simples, porém ainda não fiz posts sobre isso.</li>
</ul>
<p>No Windows vou abrir o <tt class="docutils literal">Prompt de comandos</tt> que terá essa cara:</p>
<img alt="prompt de comandos" src="images/prompt-windows-7.png" />
<p>Onde <tt class="docutils literal">rafael</tt> é meu nome de usuário e o caminho padrão que usarei é <tt class="docutils literal"><span class="pre">C:\Users\rafael</span></tt>.</p>
<p>No Linux/Mac OSX vamos abrir um terminal que terá esta aparência:</p>
<img alt="terminal linux mac osx" src="images/terminal.png" />
<p>Onde <tt class="docutils literal">rafael</tt> também é meu nome de usuário e o caminho padrão que usarei é <tt class="docutils literal">/home/rafael</tt>.</p>
<p>Clique abaixo para continuar o procedimento relativo ao seu sistema operacional:</p>
<ul class="simple">
<li><a class="reference internal" href="#criando-virtualenv-e-utilizando-o-pip-no-windows">Criando virtualenv e utilizando o pip no Windows</a></li>
<li><a class="reference internal" href="#criando-virtualenv-e-utilizando-o-pip-no-linux-e-mac-osx">Criando virtualenv e utilizando o pip no Linux e Mac OSX</a></li>
</ul>
</div>
<div class="section" id="criando-virtualenv-e-utilizando-o-pip-no-windows">
<h2>Criando virtualenv e utilizando o pip no Windows</h2>
<p>Vamos criar o diretório do projeto <tt class="docutils literal">myprojectA</tt>:</p>
<div class="highlight"><pre><span></span>C:\Users\rafael> mkdir myprojectA
</pre></div>
<p>Vamos entrar nesta pasta:</p>
<div class="highlight"><pre><span></span>C:\Users\rafael> cd myprojectA
C:\Users\rafael\myprojectA>
</pre></div>
<p>Posteriormente vamos criar um virtualenv:</p>
<div class="highlight"><pre><span></span>C:\Users\rafael\myprojectA> python -m venv .venv
</pre></div>
<p>Pronto criamos um virtualenv onde este cara ficará armazenado em uma pasta nomeado como <cite>.venv</cite>, como podemos ver abaixo:</p>
<div class="highlight"><pre><span></span>C:\Users\rafael\myprojectA> dir
... linhas omitidas ...
02/16/2016 10:36 PM <DIR> .venv
... linhas omitidas ...
</pre></div>
<p>Para começar a usar esse virtualenv temos que ativá-lo para isso execute:</p>
<div class="highlight"><pre><span></span>C:\Users\rafael\myprojectA> .venv\Scripts\activate.bat
(.venv) C:\Users\rafael\myprojectA>
</pre></div>
<p>Perceba que o seu prompt vai ser modificado e o prefixo (.venv) será acrescentado no começo dele. Agora vamos instalar uma biblioteca, o ipython:</p>
<div class="highlight"><pre><span></span>(.venv) C:\Users\rafael\myprojectA> pip install ipython
</pre></div>
<p>O <tt class="docutils literal">ipython</tt> tem várias dependências (que irão ser instaladas juntamente com ele, não se assuste) e sua função é seu um interpretador do Python com mais recursos. Quando eu não passo uma versão específica ao <tt class="docutils literal">pip</tt> ele vai instalar sempre a última versão estável da biblioteca, no momento que instalei no meu computador a versão corrente era a 4.1.1. Então neste projeto que chamei de <tt class="docutils literal">myprojectA</tt> teremos as seguintes bibliotecas:</p>
<div class="highlight"><pre><span></span>(.venv) C:\Users\rafael\myprojectA> pip freeze
decorator==4.0.9
ipython==4.1.1
ipython-genutils==0.1.0
path.py==8.1.2
pickeshare==0.6
simplegeneric==0.8.1
traitlets==4.1.0
</pre></div>
<p>Com o comando acima <tt class="docutils literal">pip freeze</tt> veremos todas as bibliotecas instaladas nesse <tt class="docutils literal">virtualenv</tt> e suas respectivas versões. Não vou detalhar neste post todos os poderes do <tt class="docutils literal">pip</tt>, mas o parâmetro <tt class="docutils literal">freeze</tt> é extremamente importante para o desenvolvimento de sistemas em Python, futuramente posso fazer um post só explicando setup de projetos Python, se quiserem esse post rápido me dêem um toque ai nos comentários.</p>
<p>Para não ser muito repetitivo (<a class="reference external" href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a>) no post e para aplicar um exercício de fixação ao mesmo tempo, refaça os passos com o diretório <tt class="docutils literal">myprojectB</tt> mas antes disso desative o <tt class="docutils literal">virtualenv</tt> atual:</p>
<div class="highlight"><pre><span></span>(.venv) C:\Users\rafael\myprojectA> .venv\Scripts\deactivate.bat
</pre></div>
<p>Ao chegar no passo em que você instala o <tt class="docutils literal">ipython</tt> pelo <tt class="docutils literal">pip</tt> não faça isso desta vez, quando chegar nesta etapa execute o comando abaixo:</p>
<div class="highlight"><pre><span></span>(.venv) C:\Users\rafael\myprojectB> pip install ipython==3.0.0
</pre></div>
<p>Desta forma vamos testemunhar o isolamento de ambientes! Ao terminar a instalaçào do <tt class="docutils literal">ipython</tt> (versão 3.0.0), verifique o que você acabou de instalar:</p>
<div class="highlight"><pre><span></span>(.venv) C:\Users\rafael\myprojectB> pip freeze
ipython=3.0.0
</pre></div>
<p>Ai você vai me perguntar "ué!? cadê os requisitos!?" pois é... nesta versão do <tt class="docutils literal">ipython</tt> ele não tinha tantos requisitos, só por essa demonstração já conseguimos ver o isolamento entre os ambientes.</p>
</div>
<div class="section" id="criando-virtualenv-e-utilizando-o-pip-no-linux-e-mac-osx">
<h2>Criando virtualenv e utilizando o pip no Linux e Mac OSX</h2>
<p>Vamos criar o diretório do projeto <tt class="docutils literal">myprojectA</tt>:</p>
<div class="highlight"><pre><span></span>rafael@bloodmary:~$ mkdir myprojectA
</pre></div>
<p>Vamos entrar nesta pasta:</p>
<div class="highlight"><pre><span></span>rafael@bloodmary:~$ cd myprojectA
rafael@bloodmary:myprojectA$
</pre></div>
<p>Posteriormente vamos criar um virtualenv:</p>
<div class="highlight"><pre><span></span>rafael@bloodmary:myprojectA$ python3.5 -m venv .venv
</pre></div>
<p>Pronto criamos um virtualenv onde este cara ficará armazenado em uma pasta nomeado como <cite>.venv</cite>, como podemos ver abaixo:</p>
<div class="highlight"><pre><span></span>rafael@bloodmary:myprojectA$ ls -a
. .. .python-version .venv
</pre></div>
<p>Para começar a usar esse virtualenv temos que ativá-lo para isso execute:</p>
<div class="highlight"><pre><span></span>rafael@bloodmary:myprojectA$ source .venv/bin/activate
(.venv)rafael@bloodmary:myprojectA$
</pre></div>
<p>Perceba que o seu prompt vai ser modificado e o prefixo (.venv) será acrescentado no começo dele. Agora vamos instalar uma biblioteca, o ipython:</p>
<div class="highlight"><pre><span></span>(.venv)rafael@bloodmary:myprojectA$ pip install ipython
</pre></div>
<p>O <tt class="docutils literal">ipython</tt> tem várias dependências (que irão ser instaladas juntamente com ele, não se assuste) e sua função é seu um interpretador do Python com mais recursos. Quando eu não passo uma versão específica ao <tt class="docutils literal">pip</tt> ele vai instalar sempre a última versão estável da biblioteca, no momento que instalei no meu computador a versão corrente era a 4.1.1. Então neste projeto que chamei de <tt class="docutils literal">myprojectA</tt> teremos as seguintes bibliotecas:</p>
<div class="highlight"><pre><span></span>(.venv)rafael@bloodmary:myprojectA$ pip freeze
decorator==4.0.9
ipython==4.1.1
ipython-genutils==0.1.0
path.py==8.1.2
pexpect==4.0.1
pickleshare==0.6
ptyprocess==0.5.1
simplegeneric==0.8.1
traitlets==4.1.0
wheel==0.24.0
</pre></div>
<p>Com o comando acima <tt class="docutils literal">pip freeze</tt> veremos todas as bibliotecas instaladas nesse <tt class="docutils literal">virtualenv</tt> e suas respectivas versões. Não vou detalhar neste post todos os poderes do <tt class="docutils literal">pip</tt>, mas o parâmetro <tt class="docutils literal">freeze</tt> é extremamente importante para o desenvolvimento de sistemas em Python, futuramente posso fazer um post só explicando setup de projetos Python, se quiserem esse post rápido me dêem um toque ai nos comentários.</p>
<p>Para não ser muito repetitivo (<a class="reference external" href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a>) no post e para aplicar um exercício de fixação ao mesmo tempo, refaça os passos com o diretório <tt class="docutils literal">myprojectB</tt> mas antes disso desative o <tt class="docutils literal">virtualenv</tt> atual:</p>
<div class="highlight"><pre><span></span>(.venv)rafael@bloodmary:myprojectA$ deactivate
</pre></div>
<p>Ao chegar no passo em que você instala o <tt class="docutils literal">ipython</tt> pelo <tt class="docutils literal">pip</tt> não faça isso desta vez, quando chegar nesta etapa execute o comando abaixo:</p>
<div class="highlight"><pre><span></span>(.venv)rafael@bloodmary:myprojectB$ pip install ipython==3.0.0
</pre></div>
<p>Desta forma vamos testemunhar o isolamento de ambientes! Ao terminar a instalaçào do <tt class="docutils literal">ipython</tt> (versão 3.0.0), verifique o que você acabou de instalar:</p>
<div class="highlight"><pre><span></span>(.venv) rafael@bloodmary:myprojectB$ pip freeze
ipython==3.0.0
</pre></div>
<p>Ai você vai me perguntar "ué!? cadê os requisitos!?" pois é... nesta versão do <tt class="docutils literal">ipython</tt> ele não tinha tantos requisitos, só por essa demonstração já conseguimos ver o isolamento entre os ambientes.</p>
</div>
<div class="section" id="conclusao">
<h2>Conclusão</h2>
<p><tt class="docutils literal">pip</tt> e <tt class="docutils literal">virtualenv</tt> são dois "utilitários" muito bacanas do Python, eles te ajudam muito na hora de criar qualquer projeto em Python, espero que esse post seja útil, apesar de ter ficado bem grandinho, mas é que tinha muita coisa pra escrever mesmo e ainda ficou faltando coisa.</p>
</div>
<div class="section" id="referencias-bacanas">
<h2>Referências bacanas</h2>
<ul class="simple">
<li><a class="reference external" href="https://garoa.net.br/wiki/Python/Ambiente_Virtual">Explicação sobre Ambiente Virtual contida na página do Garoa Hacker Clube de SP</a></li>
<li><a class="reference external" href="http://virtualenv.readthedocs.org/en/latest/index.html">Documentação oficial do virtualenv</a></li>
<li><a class="reference external" href="https://virtualenvwrapper.readthedocs.org/en/latest/">Documentação oficial do virtualenvwrapper</a></li>
<li><a class="reference external" href="https://docs.python.org/3/library/venv.html">Documentação oficial do Python explicando sobre virtualenv</a></li>
</ul>
</div>
Instalando Python 3.5 no Windows 72016-02-16T21:09:00-02:002016-02-16T21:09:00-02:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2016-02-16:/instalando-python35-no-windows-7.html<p class="first last">Muitos tem a dúvida de como instalar Python no Windows, hoje vou postar aqui um passo a passo bem básico pra que você consiga instalar Python3 no Windows 7</p>
<p>Muitos tem a dúvida de como instalar Python no Windows, hoje vou postar aqui um passo a passo bem básico pra que você consiga instalar Python3 no Windows 7.</p>
<p>Estou especificando as versões do Windows e a versão do Python pois como ambos evoluem com o tempo (e muito rapidamente) então creio eu que logo esse post irá ficar velho.</p>
<p>Ao escrever este post também tenho conhecimento que o Windows 10 é o Windows "da moda" porém neste momento só tenho uma máquina virtual de Windows 7 e estou sem tempo para criar uma máquina virtual de Windows 10, mas pelas pesquisas e googladas na internet creio que este post também será útil para outras versões do Windows.</p>
<div class="section" id="baixando-o-python">
<h2>Baixando o Python</h2>
<p>Você poderá baixar a versão oficial do Python neste site:</p>
<p><a class="reference external" href="https://www.python.org/downloads">https://www.python.org/downloads</a></p>
<p>A página acessada será semelhante a esta:</p>
<img alt="python download" src="images/instalando-python35-no-windows-7/01.png" />
<p>Baixe a release 3.5.1 (disponível atualmente), se quiser baixar diretamente clique:</p>
<ul class="simple">
<li><a class="reference external" href="https://www.python.org/ftp/python/3.5.1/python-3.5.1.exe">aqui para baixar a versão 32 bits</a>.</li>
<li><a class="reference external" href="https://www.python.org/ftp/python/3.5.1/python-3.5.1-amd64.exe">aqui para baixar a versão 64 bits</a>.</li>
</ul>
<p>Recomendo verificar qual a arquitetura do seu sistema operacional para isso pressione as teclas <tt class="docutils literal">Windows + Pause Break</tt> do seu teclado ou <tt class="docutils literal">clique com o direito no ícone do Computador e posteriormente clique em propriedades</tt>, a seguinte tela será apresentada:</p>
<img alt="system" src="images/instalando-python35-no-windows-7/02.png" />
<p>Observe que eu grifei em vermelho a arquitetura do meu Windows instalado que é de 32 bits.</p>
</div>
<div class="section" id="instalando-o-python">
<h2>Instalando o Python</h2>
<p>Basicamente para instalar o Python depois de baixado clique duas vezes no executável baixado. A instalação é bem tranquila e bem next.. next.. finish.</p>
<p>Ao clicar duas vezes no executável a seguinte tela será aberta:</p>
<img alt="python wizard 01" src="images/instalando-python35-no-windows-7/03.png" />
<p>Repare que aqui nesta tela inicial o que é mais importante é marcar a opção <tt class="docutils literal">Add Python3.5 to PATH</tt> desta forma você não precisará configurar as variáveis de ambiente do seu Windows posteriormente (pois isso é meio chato de se fazer manualmente). Marcada essa opção clique em <tt class="docutils literal">Install Now</tt>, ao clicar nesta opção o processo de instalação será iniciado:</p>
<img alt="python wizard 02" src="images/instalando-python35-no-windows-7/04.png" />
<p>Aguarde alguns instantes e esta outra tela será apresentada:</p>
<img alt="python wizard 03" src="images/instalando-python35-no-windows-7/05.png" />
<p>Feito isso clique em <tt class="docutils literal">Close</tt>, caso tenha algum problema nesta parte por favor me contate por email ou nos comentários abaixo deste post.</p>
</div>
<div class="section" id="conferindo-quais-programas-voce-ganhou">
<h2>Conferindo quais programas você "ganhou"</h2>
<p>Vejamos inicialmente o que instalamos:</p>
<img alt="iniciar" src="images/instalando-python35-no-windows-7/06.png" />
<ul class="simple">
<li><tt class="docutils literal">IDLE</tt>: Uma IDE de linha de comando muito simples para começar suas aventuras no mundo Python;</li>
<li><tt class="docutils literal">Python 3.5</tt>: Irá abrir o interpretador do Python no seu <tt class="docutils literal">Prompt de comandos</tt>;</li>
<li><tt class="docutils literal">Python 3.5 Manuals</tt>: Toda a documentação do Python que está online <a class="reference external" href="https://docs.python.org/3/">aqui</a> ficará no seu computador para você acessar offline (para casos extremos onde você ficou sem internet).</li>
<li><tt class="docutils literal">Python 3.5 Module Docs</tt>: Uma referência sobre os módulos Python, ao clicar neste carinha será iniciado um servidorzinho que te servirá essa documentação, quando quiser sair basta ir ao console aberto e digitar <tt class="docutils literal">q</tt> seguido de enter que o servidorzinho será desligado.</li>
</ul>
<p>Para começar seus estudos em Python é importante saber que a partir desta instalação você acaba de "ganhar" alguns programas que te ajudarão na sua jornada pelo conhecimento, alguns merecer destaque:</p>
<ul class="simple">
<li><tt class="docutils literal">pip</tt>: Um poderoso gerenciador de pacotes/bibliotecas do Python, com ele você pode instalar muitas e muitas bibliotecas de terceiros;</li>
<li><tt class="docutils literal">virtualenv</tt>: Um cara "separador" de ambientes, não faz muito tempo este cara foi incorporado ao Python (antes ele era um cara fora do Python), ele serve para que você tenha diversas versões da mesma biblioteca para projetos diferentes sem que elas colidam e briguem entre si para ver qual a versão vai assumir e ficar a frente quando você chamar um <tt class="docutils literal">import</tt>.</li>
</ul>
</div>
<div class="section" id="referencia">
<h2>Referência</h2>
<ul class="simple">
<li><a class="reference external" href="https://docs.python.org/3/using/windows.html">Documentação oficial do uso do Python no Windows</a></li>
</ul>
</div>
<div class="section" id="conclusao">
<h2>Conclusão</h2>
<p>Não é difícil instalar Python no Windows, mas tem que se atentar a alguns detalhes bem simples. No próximo post pretendo descrever como e pra que serve o <tt class="docutils literal">virtualenv</tt> e <tt class="docutils literal">pip</tt> que você acabou de instalar.</p>
</div>
Como criar seu PaaS com Tsuru2015-09-22T22:48:00-03:002015-09-22T22:48:00-03:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2015-09-22:/como-criar-seu-paas-com-tsuru.html<p>Como criar um PaaS simples para deploy de aplicações com Tsuru</p><p>Começo este post dizendo que gostei bastante da proposta do Tsuru. </p>
<h2>Mas o que é o Tsuru?</h2>
<p>Tsuru é um software que você pode criar seu próprio <a href="https://en.wikipedia.org/wiki/Platform_as_a_service">PaaS</a> explicando de forma simples, você tem um <a href="https://www.heroku.com/">Heroku</a>/<a href="https://www.openshift.com">Openshift</a> pessoal onde você tem controle completo na máquina e você pode fazer o que bem entender.</p>
<h2>E pra que serve esse tal Tsuru?</h2>
<p>Serve para você fazer deploy de suas aplicações de forma BRUTALMENTE RÁPIDA, com a vantagem de não pagar nada para o Openshift nem para o Heroku, mesmo que seja uma aplicação grande e pesada, quem vai gerenciar os recursos é você mesmo, então se sua máquina "explodir" a culpa será sua, pois você tem controle absoluto sobre os recursos da máquina em que você irá instalar o Tsuru.</p>
<h2>Quem é o pai do Tsuru?</h2>
<p>O pai do Tsuru (pelo que eu sei pelo menos) é o Andrews Medina, e este cara começou a desenvolver o Tsuru dentro da Globo.com. Podemos ver até na doc do Tsuru o Copyright da Globo.com, isso fica bem evidente que a Globo.com tem um dedinho ai.</p>
<p>O Andrews Medina também fez inúmeros vídeos no YouTube muito bons explicando mais sobre o Tsuru, recomendo TODOS.. principalmente os três abaixo:</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=YD3iNEBb4t0">tsuru - fazendo deploys de forma simples e divertida</a></li>
<li><a href="https://www.youtube.com/watch?v=h68DCFFXGW0">Garoa Hacker | Entrevista: Andrews Medina (possui 3 partes)</a></li>
<li><a href="https://www.youtube.com/watch?v=dC79RpifEQI">Garoa Hacker Club | Andrews Medina - Tsuru Live Code</a></li>
</ul>
<h2>Por que raios eu estou falando do Tsuru?</h2>
<p>Volto a dizer... achei SENSACIONAL o deploy realizado pelo Tsuru, extremamente rápido e seguro. Então ontem eu subi o meu PaaS utilizando o Tsuru <img style="margin-bottom: -0.25em;height:1.5em; display:inline-block;" src="/emojis/raised_hands.png">.</p>
<h2>Procedimentos básicos</h2>
<p>A doc do Tsuru é excelente e pode ser lida <a href="http://docs.tsuru.io/en/stable/understanding/index.html">aqui</a>, mas tem um porém, caso você seja apenas um xereta (assim como eu), não recomendo fazer a instalação através dessa doc, pois você irá travar assim como eu no passo <a href="http://docs.tsuru.io/en/stable/installing/adding-nodes.html">Adding Nodes</a> muito provavelmente. </p>
<p>Neste "Adding Nodes" é o passo onde você terá que adicionar uma *instância de IaaS da <a href="https://aws.amazon.com/pt/ec2/">Amazon EC2</a> ou de <a href="https://cloudstack.apache.org/">Apache CloudStack</a>, por isso eu disse que você provavelmente vai travar se for um curioso, por que?</p>
<ul>
<li>pois no EC2 você terá que pagar a instância, que é uma coisa que provavelmente você não vai querer fazer a princípio</li>
<li>e o CloudStack não é pago, porém extremamente complexo, pelo menos eu achei</li>
</ul>
<p>Portanto minha escolha óbvia foi fazer o "procedimento mais fácil" (dica do @rpedigoni) que é usar o <a href="https://github.com/tsuru/now">Tsuru Now</a>.</p>
<p>* Não sei se o nome certo para o CloudStack é instância, se eu estiver errado me desculpem</p>
<h2>Procedimento "mais fácil"</h2>
<p>Cara por que eu coloquei essas aspas ai? </p>
<p>Meu, o Tsuru Now é um script em Shell bem antiguinho (pelo menos ao meu ver) e ele não é tão intuitivo como promete, creio que na época que ele foi criado ele deveria ser mais fácil de usar, mas agora devido ao tempo e versões mais novas de sistema operacional ele se tornou meio obsoleto.</p>
<p>Mas mesmo obsoleto galera, eu ainda recomendo esse cara, pois se você aprender a domá-lo você consegue subir o Tsuru bem tranquilão.</p>
<h2>Ferramentas utilizadas</h2>
<p>Para subir o meu Tsuru estou utilizando:</p>
<ul>
<li><a href="https://github.com/tsuru/now">Tsuru Now</a></li>
<li><a href="https://www.digitalocean.com/community/tutorials/how-to-create-your-first-digitalocean-droplet-virtual-server">Um droplet da Digital Ocean</a><ul>
<li>S.O. Ubuntu 14.04 x64</li>
<li>512MB Ram </li>
<li>20GB SSD Disk </li>
<li>New York 3</li>
<li>Neste droplet a configuração default da Digital Ocean é vir 2 interfaces de rede, uma eth0 que será o ip externo e a lo que representa o loopback</li>
<li>Valor desse cara $5 por mês</li>
</ul>
</li>
</ul>
<p>Caso você não queira pagar absolutamente NADA também é possível, você só terá o trabalho de configurar as interfaces da sua máquina física (ou virtual, sei lá) e instalar o Ubuntu 14.04 x64 (o x32 eu não testei e não sei se funciona bem!).</p>
<h2>Problemas encontrados logo no começo</h2>
<p>No repositório do <a href="https://github.com/tsuru/now">github do Tsuru Now</a> tem algumas informações faltantes a respeito da compatibilidade com os Sistemas operacionais. Lá no README ele menciona que este script funciona em Debian e Ubuntu, mas não diz a versão <img style="margin-bottom: -0.25em;height:1.5em; display:inline-block;" src="/emojis/pensive.png">.</p>
<p>Eu como um bom paranóico amante de Debian, fui logo criando um droplet na Digital Ocean com o Debian mais novo que encontrei o 8.1~8.2 (Jessie Stable), ai rodei o script conforme manda o README:</p>
<div class="highlight"><pre><span></span># curl -sL https://raw.githubusercontent.com/tsuru/now/master/run.bash | bash
</pre></div>
<p>Coisa linda pra se ver.... massss... não consegui nem ir até 50% da instalação, logo de cara falta o pacote linux-image-extras-* no Debian, e adicionar repositórios começa a ficar meio impraticável pois eu como um bom paranóico, acredito, que se ele não está no repositório main alguma razão deve ter.</p>
<p>Eis que pensei <em>"pooo... se não vai Debian, os caras devem gostar de Ubuntu... vamos de Ubuntu"</em>, ai novamente como um bom paranóico fui logo escolhendo a versão MAIS NOVA do Ubuntu, a versão 15.04 x64.... e adivinhem?? Problema de novo.... se não me engano com o mesmo pacote.</p>
<p>Ai novamente como um bom brasileiro que não desiste nunca fui de novo agora pensei <em>"poooo.... se os caras não gostam desse Ubuntu vou usar o Ubuntu que está referenciado na doc oficial do Tsuru"</em> que vem a ser o 14.04 ! </p>
<p>E desta vez fiz progressos significativos, MUITO SIGNIFICATIVOS e consegui concluir cerca de 80% da instalação e o script foi dar pau lá pelo finalzinho próximo da <code>linha 671</code> quando ele chama a função <code>config_tsuru_post</code>.</p>
<p>Ai começa meu instinto de programador maluco e fui depurando a parada. Pelo que pude ver o script não estava pegando meu ip externo adequadamente.</p>
<h2>Variáveis/adaptação e funcionamento completo</h2>
<p>Como o Tsuru Now não conseguia pegar meu ip corretamente fui lá e comecei a testar coisas de modo que fizesse o filho da mãe funcionar. Eis que por fim eu consegui, e vou dar a receita de bolo pra vocês. Clone o repositório do Tsuru Now inicialmente:</p>
<div class="highlight"><pre><span></span><span class="err">#</span><span class="w"> </span><span class="n">git</span><span class="w"> </span><span class="n">clone</span><span class="w"> </span><span class="n">git</span><span class="nv">@github</span><span class="p">.</span><span class="nl">com</span><span class="p">:</span><span class="n">tsuru</span><span class="o">/</span><span class="n">now</span><span class="p">.</span><span class="n">git</span><span class="w"></span>
</pre></div>
<p>Nas primeiras linhas do script chamado <code>run.bash</code> tem algumas variáveis interessantes:</p>
<div class="highlight"><pre><span></span><span class="mf">1</span><span class="w"> </span><span class="err">#!</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">bash</span><span class="w"> </span><span class="o">-</span><span class="n">ue</span><span class="w"></span>
<span class="mf">2</span><span class="w"> </span>
<span class="mf">3</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="n">Copyright</span><span class="w"> </span><span class="mf">2015</span><span class="w"> </span><span class="n">tsuru</span><span class="o">-</span><span class="n">now</span><span class="w"> </span><span class="n">authors</span><span class="mf">.</span><span class="w"> </span><span class="n">All</span><span class="w"> </span><span class="n">rights</span><span class="w"> </span><span class="n">reserved</span><span class="mf">.</span><span class="w"></span>
<span class="mf">4</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="n">Use</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">this</span><span class="w"> </span><span class="n">source</span><span class="w"> </span><span class="n">code</span><span class="w"> </span><span class="n">is</span><span class="w"> </span><span class="kr">go</span><span class="n">verned</span><span class="w"> </span><span class="n">by</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">BSD</span><span class="o">-</span><span class="n">style</span><span class="w"></span>
<span class="mf">5</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="n">license</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="n">can</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="n">found</span><span class="w"> </span><span class="n">in</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">LICENSE</span><span class="w"> </span><span class="n">file</span><span class="mf">.</span><span class="w"></span>
<span class="mf">6</span><span class="w"> </span>
<span class="mf">7</span><span class="w"> </span><span class="n">set</span><span class="w"> </span><span class="o">-</span><span class="n">eu</span><span class="w"></span>
<span class="mf">8</span><span class="w"></span>
<span class="mf">9</span><span class="w"> </span><span class="n">release</span><span class="o">=</span><span class="s">""</span><span class="w"></span>
<span class="mf">10</span><span class="w"> </span><span class="n">codename</span><span class="o">=</span><span class="s">""</span><span class="w"></span>
<span class="mf">11</span><span class="w"> </span><span class="n">host_ip</span><span class="o">=</span><span class="s">""</span><span class="w"></span>
<span class="mf">12</span><span class="w"> </span><span class="n">private_ip</span><span class="o">=</span><span class="s">""</span><span class="w"></span>
<span class="mf">13</span><span class="w"> </span><span class="n">host_name</span><span class="o">=</span><span class="s">""</span><span class="w"></span>
<span class="mf">14</span><span class="w"> </span><span class="n">set_interface</span><span class="o">=</span><span class="s">""</span><span class="w"></span>
<span class="mf">15</span><span class="w"> </span><span class="n">is_debug</span><span class="o">=</span><span class="s">""</span><span class="w"></span>
<span class="mf">16</span><span class="w"> </span><span class="n">docker_node</span><span class="o">=</span><span class="s">""</span><span class="w"></span>
<span class="mf">17</span><span class="w"> </span><span class="n">set_interface</span><span class="o">=</span><span class="s">""</span><span class="w"></span>
</pre></div>
<p>Destas 17 linhas as variáveis que resolveram minha vida foram <code>host_ip</code> e <code>private_ip</code>, nestas duas eu setei meu ip <strong>EXTERNO (ou seja o ip que a galera de fora da sua rede vê)</strong>, caso você tenha dúvidas qual é seu ip externo recomendo olhar no site <a href="https://www.whatismyip.com/">What Is My Ip?</a> que lá ele vai te mostrar o seu ip externo(mas faz o favor de olhar da máquina que você está instalando o Tsuru <img style="margin-bottom: -0.25em;height:1.5em; display:inline-block;" src="/emojis/joy.png">).</p>
<p>Feito isso rodei o script novamente da seguinte maneira (você precisará executar como root, ou usando sudo):</p>
<div class="highlight"><pre><span></span># bash run.bash --host-ip <meu ip externo> | tee log_run.log
</pre></div>
<p>Troque <meu ip externo> pelo seu ip externo. </p>
<p>Executando esse comando vá tomar banho, comer, tomar café ou outra coisa desse tipo, pois vai demorar um pouco (calculo pelo menos uns 15 minutos caso você não use um hd SSD).</p>
<p>Usei o comando <code>| tee log_run.log</code> para que ele gere um arquivo contendo todo output (erros e não erros) do script, se você quiser analisar depois como foi a instalação ficará mais fácil você ler este arquivo.</p>
<h2>Finish him!</h2>
<p>Se tudo correr bem ao fim dessa execução seu Tsuru já vai estar funcionando de forma básica pronta para testes bacaninhas. É importante em um primeiro momento alterar a senha do admin e rodar <code>source ~/.bashrc</code> conforme manda o próprio Tsuru Now:</p>
<div class="highlight"><pre><span></span><span class="err">########################</span><span class="w"> </span><span class="n">DONE</span><span class="err">!</span><span class="w"> </span><span class="err">########################</span><span class="w"></span>
<span class="ow">Some</span><span class="w"> </span><span class="n">information</span><span class="w"> </span><span class="n">about</span><span class="w"> </span><span class="n">your</span><span class="w"> </span><span class="n">tsuru</span><span class="w"> </span><span class="nl">installation</span><span class="p">:</span><span class="w"></span>
<span class="k">Admin</span><span class="w"> </span><span class="k">user</span><span class="err">:</span><span class="w"> </span><span class="k">admin</span><span class="nv">@example</span><span class="p">.</span><span class="n">com</span><span class="w"></span>
<span class="k">Admin</span><span class="w"> </span><span class="nl">password</span><span class="p">:</span><span class="w"> </span><span class="n">admin123</span><span class="w"> </span><span class="p">(</span><span class="n">PLEASE</span><span class="w"> </span><span class="n">CHANGE</span><span class="w"> </span><span class="nl">RUNNING</span><span class="p">:</span><span class="w"> </span><span class="n">tsuru</span><span class="w"> </span><span class="n">change</span><span class="o">-</span><span class="n">password</span><span class="p">)</span><span class="w"></span>
<span class="n">Target</span><span class="w"> </span><span class="nl">address</span><span class="p">:</span><span class="w"> </span><span class="nl">http</span><span class="p">:</span><span class="o">//<</span><span class="n">meu</span><span class="w"> </span><span class="n">ip</span><span class="w"> </span><span class="n">externo</span><span class="o">></span><span class="err">:</span><span class="mi">8080</span><span class="w"></span>
<span class="n">Dashboard</span><span class="w"> </span><span class="nl">address</span><span class="p">:</span><span class="w"> </span><span class="nl">http</span><span class="p">:</span><span class="o">//<</span><span class="n">meu</span><span class="w"> </span><span class="n">ip</span><span class="w"> </span><span class="n">externo</span><span class="o">></span><span class="err">:</span><span class="mi">32768</span><span class="w"></span>
<span class="n">You</span><span class="w"> </span><span class="n">should</span><span class="w"> </span><span class="n">run</span><span class="w"> </span><span class="err">`</span><span class="n">source</span><span class="w"> </span><span class="o">~/</span><span class="p">.</span><span class="n">bashrc</span><span class="err">`</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">your</span><span class="w"> </span><span class="k">current</span><span class="w"> </span><span class="n">terminal</span><span class="p">.</span><span class="w"></span>
</pre></div>
<p>Para alterar a senha você poderá usar o comando <code>tsuru change-password</code>. Após você ter seu PaaS instalado e funcional recomendo assistir o vídeo do Andrews para dar seus primeiros passinhos utilizando o tsuru-cli, um vídeo ótimo para começar é este <a href="https://www.youtube.com/watch?v=YD3iNEBb4t0">tsuru - fazendo deploys de forma simples e divertida</a>.</p>
<p>Espero que tenha gostado do post, deixe seus comentários ali embaixo <img style="margin-bottom: -0.25em;height:1.5em; display:inline-block;" src="/emojis/point_down.png">.</p>
<p>Flw!</p>Grupo de Estudos Python Sorocaba2015-09-19T23:43:00-03:002015-09-19T23:43:00-03:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2015-09-19:/grupo-de-estudos-python-sorocaba.html<p class="first last">Grupo de estudos Python em Sorocaba aberto ao público em geral</p>
<p>Perto do dia 2 de julho de 2015 eu o Rafael Gottsfritz e o Caio Carrara tivemos a iniciativa de nos misturar ao <a class="reference external" href="http://hacklab.club/">Hacklab Sorocaba</a> para tentar organizar um grupo de estudos sobre variados assuntos.
Nós queríamos trazer mais movimento de comunidades para Sorocaba e regiões próximas, visto que isso é muitíssimo popular em cidades como Campinas, São Paulo e São José dos Campos:</p>
<ul class="simple">
<li><a class="reference external" href="https://garoa.net.br/wiki/P%C3%A1gina_principal">Garoa Hacker Clube SP</a></li>
<li><a class="reference external" href="http://lhc.net.br/wiki/P%C3%A1gina_principal">Laboratório Hacker Campinas</a></li>
<li><a class="reference external" href="http://campinas.rupy.com.br/">RuPy Campinas 2015</a></li>
<li><a class="reference external" href="http://pgdaycampinas.com.br/">PG Day Campinas 2015</a></li>
<li><a class="reference external" href="http://www.meetup.com/pt/Grupy-SP/">GruPy-SP</a></li>
<li><a class="reference external" href="http://www.thedevelopersconference.com.br/tdc/2015/saopaulo/trilhas">TDC São Paulo 2015</a></li>
<li><a class="reference external" href="https://www.facebook.com/pyladiescps">Pyladies Campinas - Facebook</a></li>
<li><a class="reference external" href="https://twitter.com/pyladiescps">Pyladies Campinas - Twitter</a></li>
<li><a class="reference external" href="http://pythonbrasil.github.io/pythonbrasil11-site/">Python Brasil 2015 - SJC</a></li>
</ul>
<p>Esses links acima foram apenas exemplos de eventos de comunidade ou um pouco mais comerciais que rolaram/rolam ainda por este ano (2015).</p>
<div class="section" id="o-inicio">
<h2>O início</h2>
<p>Para tentar começar a trazer coisas legais pra Sorocaba, inicialmente divulguei na lista de <a class="reference external" href="https://groups.google.com/forum/?hl=pt#!searchin/hackerspace-sorocaba/Sesc/hackerspace-sorocaba/ampID5hePrI/w0Jt_vIWSTEJ">email do Hacklab</a> um bate-papo descontraído no SESC Sorocaba, era descontraído, mas para ter foco nessa "descontração" toda eu queria propor objetivos as conversas e que preferencialmente se baseasse em tecnologia (ou não, caso não tivessem muitos geeks na parada).</p>
<p>Porém infelizmente (ou felizmente, ainda não descobri) somente EU coloquei um tema lá para discutir neste evento intitulado de "Quinta-feira Hacker" (o nome era bonito). Olha só que loucura que ficou o call4papers deste dia:</p>
<img alt="call4papers" src="images/grupo-de-estudos-python-sorocaba_01.png" />
<p>Bombando.... com <a class="reference external" href="http://call4paperz.com/events/quinta-feira-hacker">1 call somente</a>!!</p>
<p>Inicialmente fiquei meio puto e imaginei que não ia aparecer ninguém no SESC, por fim foram várias pessoas (no mínimo umas 6 pelo que me recordo) ai eu fiquei mais feliz um pouco. Mas mesmo assim fiquei meio de queixo caído com o desinteresse geral das pessoas, pois eu chamei para o encontro no mínimo 50 e apareceram somente 6.</p>
</div>
<div class="section" id="hacklab-no-sabado-rolando-ainda-quase-sempre">
<h2>Hacklab no sábado (rolando ainda quase sempre)</h2>
<p>Alguns dias após este "big evento" fomos novamente os 6 mosqueteiros (quase todos que foram no SESC) no sábado para nos encontrar no <a class="reference external" href="http://www.macs.org.br/">Museu de Arte Contemporânea de Sorocaba</a>, lá tem uma sala MUITO MUITO BACANA onde o pessoal faz umas reuniões embaixo do Museu (não amigo não é culto satânico.. é parada de hacker mesmo).</p>
<p>Neste dia conheci o Hudson que é o cara que toca o barco do Hacklab junto com o Guilherme que é um membro assíduo também, conversamos bastante e neste mesmo dia o Hudson me falou uma coisa importante que eu estava deixando passar...</p>
<p><tt class="docutils literal">"Cara pra ser um evento precisa de você e mais um"</tt></p>
<p>Resumindo... acho que ele tinha razão, pois bastam 2 interessados para formar um pequeno grupo para fazer algo, se você estiver sozinho OK! Também é LEGAL! Porém você não tem o compromisso de estar na mesma hora no mesmo lugar para estudar algum assunto, imagino que isso seja o princípio da ideia de um <a class="reference external" href="https://en.wikipedia.org/wiki/Hackerspace">hackerspace</a>.</p>
</div>
<div class="section" id="o-grupo-de-estudos">
<h2>O grupo de estudos</h2>
<p>Eis que neste dia um pouco depois surgiu a ideia de montarmos um <a class="reference external" href="https://groups.google.com/forum/#!forum/python-sorocaba">Grupo de Estudos de Python em Sorocaba</a>, um grupo com um caminho simples a trilhar:</p>
<ol class="arabic simple">
<li><dl class="first docutils">
<dt>Nivelar as pessoas em Python, para que discussões futuras sejam mais proveitosas (no ponto que estamos já estão sendo)</dt>
<dd><ul class="first last">
<li>Para isso estou dando aulas eu mesmo via <a class="reference external" href="http://www.google.com/intl/pt-BR_ALL/+/learnmore/hangouts/onair.html">Hangout on Air</a></li>
</ul>
</dd>
</dl>
</li>
<li><dl class="first docutils">
<dt>Todos nós aprendendo juntos</dt>
<dd><ul class="first last">
<li>Existem assuntos bem complexos no mundo Python aos quais eu não domino para esses precisamos já ter entendido o básico e estarmos nivelados a ponto de aprendermos juntos</li>
</ul>
</dd>
</dl>
</li>
<li><dl class="first docutils">
<dt>A parte mais legal!!!</dt>
<dd><ul class="first last">
<li>Começar a contribuir ou criar projetos open source!!! Sem prazo nem cobranças.. fazer algo da legal que queremos pra aprendermos mais e mais</li>
</ul>
</dd>
</dl>
</li>
</ol>
<p>E o valor do curso!? O valor é DEDICAÇÃO E INTERESSE. Se você estiver afim de entrar no Grupo tem que ter no mínimo MUITA DEDICAÇÃO E MUITO INTERESSE pois é caro o curso em termos de valores honrosos hahaha.</p>
</div>
<div class="section" id="quer-entrar-para-o-nosso-grupo">
<h2>Quer entrar para o nosso Grupo??</h2>
<p>Basta se inscrever na lista <a class="reference external" href="https://groups.google.com/forum/#!forum/python-sorocaba">Python-Sorocaba</a> e acompanhar os emails, sempre fazemos nossos encontros nas quartas-feiras a noite próximo das 20hrs.</p>
<p>Porém caso você queira entrar neste atual momento, agora, agorinha, me manda um tweet para que eu alinhe o que você precisa estudar, pois estamos indo para a aula 12 já. Mas podemos fazer novas aulas a partir da aula 1 se tiver mais gente interessada.</p>
<p>E ai curtiram a ideia?</p>
</div>
Git squash?? WTF?2015-03-24T10:16:00-03:002015-03-24T10:16:00-03:00Rafael Henrique da Silva Correiatag:blog.abraseucodigo.com.br,2015-03-24:/git-squash-wtf.html<p>Como fazer e pra que serve esse tal de git squash?</p><p>Galera conforme mencionei no post anterior.. estou contribuindo com o <a href="https://github.com/puppet-community/puppetboard/">puppetboard</a> (mais informações no post abaixo desse :P). E fiz meu primeiro Pull Request e talz resolvendo uma questão que estava na issue para acrescentar uma nova funcionalidade ao projeto. Porém quando eu fiz meu PR (Pull Request) teve um cara que me disse o seguinte:</p>
<p><img alt="comment-github-image" src="/images/git-squash-wtf_01.png" title="comment-github-image">
**<a href="http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Squashing-Commits">squash</a></p>
<p>Ai eu pensei como qualquer ótimo conhecedor do Git que porras é essa de squash?</p>
<p>O squash é uma prática utilizada para combinar mais de um commit em um commit só, desta forma eliminando "lixos" do projeto. Agora aqui neste post explicarei como raios você faz isso, pois não achei uma documentação simples que explicasse a uma pessoa normal como fazer isso, e após aprender, decidi TENTAR explicar de uma forma simples <img style="margin-bottom: -0.25em;height:1.5em; display:inline-block;" src="/emojis/smile.png">.</p>
<h2>O projeto</h2>
<p>Primeiro obviamente você precisa ter um repositório git, para isso eu criei um repositório BEEEM simples: </p>
<div class="highlight"><pre><span></span>$ mkdir teste
$ <span class="nb">cd</span> teste
$ git init
$ <span class="nb">echo</span> <span class="s2">"Rafael"</span> >> README
$ git add README
$ git commit -m <span class="s2">"Adicionado Rafael"</span>
$ <span class="nb">echo</span> <span class="s2">"Henrique"</span> >> README
$ git add README
$ git commit -m <span class="s2">"Adicionado Henrique"</span>
$ <span class="nb">echo</span> <span class="s2">"da"</span> >> README
$ git add README
$ git commit -m <span class="s2">"Adicionado da"</span>
$ <span class="nb">echo</span> <span class="s2">"Silva"</span> >> README
$ git add README
$ git commit -m <span class="s2">"Adicionado Silva"</span>
$ <span class="nb">echo</span> <span class="s2">"Correia"</span> >> README
$ git add README
$ git commit -m <span class="s2">"Adicionado Correia"</span>
</pre></div>
<p>Feito isso terei o seguinte histórico de commits:</p>
<div class="highlight"><pre><span></span>$ git log --oneline
4c03027 Adicionado Correia
e23576b Adicionado Silva
769bda9 Adicionado da
2a71622 Adicionado Henrique
5ebadcd Adicionado Rafael
</pre></div>
<h2>O squash</h2>
<p>Agora vamos imaginar que eu quero fazer um squash a partir de Henrique, ou seja quero que os commits 2a71622, 769bda9, e23576b e 4c03027 sejam um só, pois todos tratam da adição do sobrenome no arquivo README. Para isso precisarei usar o comando rebase do git no commit que eu desejo, no caso a partir do Henrique (como é a partir do Henrique deveremos pegar 1 commit antes):</p>
<div class="highlight"><pre><span></span>$ git rebase -i 5ebadcd
</pre></div>
<p>Após executar este comando será aberto um arquivo temporário para podermos fazer as alterações desejadas, o arquivo será similar a este abaixo:</p>
<div class="highlight"><pre><span></span><span class="nt">pick</span><span class="w"> </span><span class="nt">2a71622</span><span class="w"> </span><span class="nt">Adicionado</span><span class="w"> </span><span class="nt">Henrique</span><span class="w"> </span>
<span class="nt">pick</span><span class="w"> </span><span class="nt">769bda9</span><span class="w"> </span><span class="nt">Adicionado</span><span class="w"> </span><span class="nt">da</span><span class="w"> </span>
<span class="nt">pick</span><span class="w"> </span><span class="nt">e23576b</span><span class="w"> </span><span class="nt">Adicionado</span><span class="w"> </span><span class="nt">Silva</span><span class="w"> </span>
<span class="nt">pick</span><span class="w"> </span><span class="nt">4c03027</span><span class="w"> </span><span class="nt">Adicionado</span><span class="w"> </span><span class="nt">Correia</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">Rebase</span><span class="w"> </span><span class="nt">5ebadcd</span><span class="o">.</span><span class="p">.</span><span class="nc">4c03027</span><span class="w"> </span><span class="nt">onto</span><span class="w"> </span><span class="nt">5ebadcd</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">Commands</span><span class="o">:</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">p</span><span class="o">,</span><span class="w"> </span><span class="nt">pick</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nt">use</span><span class="w"> </span><span class="nt">commit</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">r</span><span class="o">,</span><span class="w"> </span><span class="nt">reword</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nt">use</span><span class="w"> </span><span class="nt">commit</span><span class="o">,</span><span class="w"> </span><span class="nt">but</span><span class="w"> </span><span class="nt">edit</span><span class="w"> </span><span class="nt">the</span><span class="w"> </span><span class="nt">commit</span><span class="w"> </span><span class="nt">message</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">e</span><span class="o">,</span><span class="w"> </span><span class="nt">edit</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nt">use</span><span class="w"> </span><span class="nt">commit</span><span class="o">,</span><span class="w"> </span><span class="nt">but</span><span class="w"> </span><span class="nt">stop</span><span class="w"> </span><span class="nt">for</span><span class="w"> </span><span class="nt">amending</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">s</span><span class="o">,</span><span class="w"> </span><span class="nt">squash</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nt">use</span><span class="w"> </span><span class="nt">commit</span><span class="o">,</span><span class="w"> </span><span class="nt">but</span><span class="w"> </span><span class="nt">meld</span><span class="w"> </span><span class="nt">into</span><span class="w"> </span><span class="nt">previous</span><span class="w"> </span><span class="nt">commit</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">f</span><span class="o">,</span><span class="w"> </span><span class="nt">fixup</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nt">like</span><span class="w"> </span><span class="s2">"squash"</span><span class="o">,</span><span class="w"> </span><span class="nt">but</span><span class="w"> </span><span class="nt">discard</span><span class="w"> </span><span class="nt">this</span><span class="w"> </span><span class="nt">commit</span><span class="err">'</span><span class="nt">s</span><span class="w"> </span><span class="nt">log</span><span class="w"> </span><span class="nt">message</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">x</span><span class="o">,</span><span class="w"> </span><span class="nt">exec</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nt">run</span><span class="w"> </span><span class="nt">command</span><span class="w"> </span><span class="o">(</span><span class="nt">the</span><span class="w"> </span><span class="nt">rest</span><span class="w"> </span><span class="nt">of</span><span class="w"> </span><span class="nt">the</span><span class="w"> </span><span class="nt">line</span><span class="o">)</span><span class="w"> </span><span class="nt">using</span><span class="w"> </span><span class="nt">shell</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">These</span><span class="w"> </span><span class="nt">lines</span><span class="w"> </span><span class="nt">can</span><span class="w"> </span><span class="nt">be</span><span class="w"> </span><span class="nt">re-ordered</span><span class="o">;</span><span class="w"> </span><span class="nt">they</span><span class="w"> </span><span class="nt">are</span><span class="w"> </span><span class="nt">executed</span><span class="w"> </span><span class="nt">from</span><span class="w"> </span><span class="nt">top</span><span class="w"> </span><span class="nt">to</span><span class="w"> </span><span class="nt">bottom</span><span class="o">.</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">If</span><span class="w"> </span><span class="nt">you</span><span class="w"> </span><span class="nt">remove</span><span class="w"> </span><span class="nt">a</span><span class="w"> </span><span class="nt">line</span><span class="w"> </span><span class="nt">here</span><span class="w"> </span><span class="nt">THAT</span><span class="w"> </span><span class="nt">COMMIT</span><span class="w"> </span><span class="nt">WILL</span><span class="w"> </span><span class="nt">BE</span><span class="w"> </span><span class="nt">LOST</span><span class="o">.</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">However</span><span class="o">,</span><span class="w"> </span><span class="nt">if</span><span class="w"> </span><span class="nt">you</span><span class="w"> </span><span class="nt">remove</span><span class="w"> </span><span class="nt">everything</span><span class="o">,</span><span class="w"> </span><span class="nt">the</span><span class="w"> </span><span class="nt">rebase</span><span class="w"> </span><span class="nt">will</span><span class="w"> </span><span class="nt">be</span><span class="w"> </span><span class="nt">aborted</span><span class="o">.</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">Note</span><span class="w"> </span><span class="nt">that</span><span class="w"> </span><span class="nt">empty</span><span class="w"> </span><span class="nt">commits</span><span class="w"> </span><span class="nt">are</span><span class="w"> </span><span class="nt">commented</span><span class="w"> </span><span class="nt">out</span><span class="w"></span>
</pre></div>
<p>Neste mesmo arquivo já podemos visualizar algumas opções do que fazer com esses commits, para este post utilizaremos somente o squash, mas vale a pena conhecer as outras opções descritas abaixo de commands. Agora altere este arquivo da seguinte maneira:</p>
<div class="highlight"><pre><span></span><span class="nt">pick</span><span class="w"> </span><span class="nt">2a71622</span><span class="w"> </span><span class="nt">Adicionado</span><span class="w"> </span><span class="nt">Henrique</span><span class="w"> </span>
<span class="nt">squash</span><span class="w"> </span><span class="nt">769bda9</span><span class="w"> </span><span class="nt">Adicionado</span><span class="w"> </span><span class="nt">da</span><span class="w"> </span>
<span class="nt">squash</span><span class="w"> </span><span class="nt">e23576b</span><span class="w"> </span><span class="nt">Adicionado</span><span class="w"> </span><span class="nt">Silva</span><span class="w"> </span>
<span class="nt">squash</span><span class="w"> </span><span class="nt">4c03027</span><span class="w"> </span><span class="nt">Adicionado</span><span class="w"> </span><span class="nt">Correia</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">Rebase</span><span class="w"> </span><span class="nt">5ebadcd</span><span class="o">.</span><span class="p">.</span><span class="nc">4c03027</span><span class="w"> </span><span class="nt">onto</span><span class="w"> </span><span class="nt">5ebadcd</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">Commands</span><span class="o">:</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">p</span><span class="o">,</span><span class="w"> </span><span class="nt">pick</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nt">use</span><span class="w"> </span><span class="nt">commit</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">r</span><span class="o">,</span><span class="w"> </span><span class="nt">reword</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nt">use</span><span class="w"> </span><span class="nt">commit</span><span class="o">,</span><span class="w"> </span><span class="nt">but</span><span class="w"> </span><span class="nt">edit</span><span class="w"> </span><span class="nt">the</span><span class="w"> </span><span class="nt">commit</span><span class="w"> </span><span class="nt">message</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">e</span><span class="o">,</span><span class="w"> </span><span class="nt">edit</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nt">use</span><span class="w"> </span><span class="nt">commit</span><span class="o">,</span><span class="w"> </span><span class="nt">but</span><span class="w"> </span><span class="nt">stop</span><span class="w"> </span><span class="nt">for</span><span class="w"> </span><span class="nt">amending</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">s</span><span class="o">,</span><span class="w"> </span><span class="nt">squash</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nt">use</span><span class="w"> </span><span class="nt">commit</span><span class="o">,</span><span class="w"> </span><span class="nt">but</span><span class="w"> </span><span class="nt">meld</span><span class="w"> </span><span class="nt">into</span><span class="w"> </span><span class="nt">previous</span><span class="w"> </span><span class="nt">commit</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">f</span><span class="o">,</span><span class="w"> </span><span class="nt">fixup</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nt">like</span><span class="w"> </span><span class="s2">"squash"</span><span class="o">,</span><span class="w"> </span><span class="nt">but</span><span class="w"> </span><span class="nt">discard</span><span class="w"> </span><span class="nt">this</span><span class="w"> </span><span class="nt">commit</span><span class="err">'</span><span class="nt">s</span><span class="w"> </span><span class="nt">log</span><span class="w"> </span><span class="nt">message</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">x</span><span class="o">,</span><span class="w"> </span><span class="nt">exec</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nt">run</span><span class="w"> </span><span class="nt">command</span><span class="w"> </span><span class="o">(</span><span class="nt">the</span><span class="w"> </span><span class="nt">rest</span><span class="w"> </span><span class="nt">of</span><span class="w"> </span><span class="nt">the</span><span class="w"> </span><span class="nt">line</span><span class="o">)</span><span class="w"> </span><span class="nt">using</span><span class="w"> </span><span class="nt">shell</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">These</span><span class="w"> </span><span class="nt">lines</span><span class="w"> </span><span class="nt">can</span><span class="w"> </span><span class="nt">be</span><span class="w"> </span><span class="nt">re-ordered</span><span class="o">;</span><span class="w"> </span><span class="nt">they</span><span class="w"> </span><span class="nt">are</span><span class="w"> </span><span class="nt">executed</span><span class="w"> </span><span class="nt">from</span><span class="w"> </span><span class="nt">top</span><span class="w"> </span><span class="nt">to</span><span class="w"> </span><span class="nt">bottom</span><span class="o">.</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">If</span><span class="w"> </span><span class="nt">you</span><span class="w"> </span><span class="nt">remove</span><span class="w"> </span><span class="nt">a</span><span class="w"> </span><span class="nt">line</span><span class="w"> </span><span class="nt">here</span><span class="w"> </span><span class="nt">THAT</span><span class="w"> </span><span class="nt">COMMIT</span><span class="w"> </span><span class="nt">WILL</span><span class="w"> </span><span class="nt">BE</span><span class="w"> </span><span class="nt">LOST</span><span class="o">.</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">However</span><span class="o">,</span><span class="w"> </span><span class="nt">if</span><span class="w"> </span><span class="nt">you</span><span class="w"> </span><span class="nt">remove</span><span class="w"> </span><span class="nt">everything</span><span class="o">,</span><span class="w"> </span><span class="nt">the</span><span class="w"> </span><span class="nt">rebase</span><span class="w"> </span><span class="nt">will</span><span class="w"> </span><span class="nt">be</span><span class="w"> </span><span class="nt">aborted</span><span class="o">.</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">Note</span><span class="w"> </span><span class="nt">that</span><span class="w"> </span><span class="nt">empty</span><span class="w"> </span><span class="nt">commits</span><span class="w"> </span><span class="nt">are</span><span class="w"> </span><span class="nt">commented</span><span class="w"> </span><span class="nt">out</span><span class="w"></span>
</pre></div>
<p>As linhas dos commits 769bda9, e23576b e 4c03027 foram mudadas para squash, portanto serão combinados ao commit que possui a o nome pick, então o commit 2a71622 agora será unido aos 3 commits abaixo dele. É importante comentar que o squash não remove os logs (mensagens dos commits) conforme a descrição do "Commands" já a opção fixup descarta os logs. Feita esta alteração basta salvar o arquivo e sair. Feito isso será apresentado outro arquivo similar a este abaixo:</p>
<div class="highlight"><pre><span></span><span class="err">#</span><span class="w"> </span><span class="nt">This</span><span class="w"> </span><span class="nt">is</span><span class="w"> </span><span class="nt">a</span><span class="w"> </span><span class="nt">combination</span><span class="w"> </span><span class="nt">of</span><span class="w"> </span><span class="nt">4</span><span class="w"> </span><span class="nt">commits</span><span class="o">.</span><span class="w"> </span>
<span class="err">#</span><span class="w"> </span><span class="nt">The</span><span class="w"> </span><span class="nt">first</span><span class="w"> </span><span class="nt">commit</span><span class="s1">'s message is: </span>
<span class="s1">Adicionado Henrique </span>
<span class="s1"># This is the 2nd commit message: </span>
<span class="s1">Adicionado da </span>
<span class="s1"># This is the 3rd commit message: </span>
<span class="s1">Adicionado Silva </span>
<span class="s1"># This is the 4th commit message: </span>
<span class="s1">Adicionado Correia </span>
<span class="s1"># Please enter the commit message for your changes. Lines starting </span>
<span class="s1"># with '</span><span class="err">#</span><span class="s1">' will be ignored, and an empty message aborts the commit. </span>
<span class="s1"># </span>
<span class="s1"># Date: Tue Mar 24 11:08:29 2015 -0300 </span>
<span class="s1"># </span>
<span class="s1"># rebase in progress; onto 5ebadcd </span>
<span class="s1"># You are currently editing a commit while rebasing branch '</span><span class="nt">master</span><span class="s1">' on '</span><span class="nt">5ebadcd</span><span class="err">'</span><span class="o">.</span><span class="w"> </span>
<span class="err">#</span><span class="w"></span>
</pre></div>
<p>Neste arquivo você poderá mudar as mensagens de log (mensagens dos commits) caso desejar, se desejar mude alguma coisa e salve e saia do arquivo. Terminada a edição/salvamento deste arquivo você acabou o squash! Podemos ver no log como ficou a nova estrutura:</p>
<div class="highlight"><pre><span></span>$ git log --oneline
f4c9d80 Adicionado Henrique
5ebadcd Adicionado Rafael
</pre></div>
<p>Ou: </p>
<div class="highlight"><pre><span></span>$ git log
commit f4c9d80a089ee35fca0eb7c6e5aabcb435fb9bb4
Author: Rafael Henrique da Silva Correia <rafael@abraseucodigo.com.br>
Date: Tue Mar <span class="m">24</span> <span class="m">11</span>:08:29 <span class="m">2015</span> -0300
Adicionado Henrique
Adicionado da
Adicionado Silva
Adicionado Correia
commit 5ebadcde3eb8c006bd33ee4c62f9cbb359822d6e
Author: Rafael Henrique da Silva Correia <rafael@abraseucodigo.com.br>
Date: Tue Mar <span class="m">24</span> <span class="m">11</span>:08:14 <span class="m">2015</span> -0300
Adicionado Rafael
</pre></div>
<h2>Conclusão</h2>
<p>Esta prática é bem interessante e deixa o repositório do git mais limpo, é MUITO válida principalmente em projetos grandes onde se tem muitos contribuidores (principalmente em projetos open source <img style="margin-bottom: -0.25em;height:1.5em; display:inline-block;" src="/emojis/smile.png">).</p>