<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Frontend | AkitaOnRails.com</title><link>https://akitaonrails.github.io/tags/frontend/</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:48 +0000</lastBuildDate><atom:link href="https://akitaonrails.github.io/tags/frontend/index.xml" rel="self" type="application/rss+xml"/><item><title>Estamos no Futuro: Carros voam e não temos mais que fazer HTML/CSS</title><link>https://akitaonrails.github.io/2026/10/16/estamos-no-futuro-carros-voam-e-nao-temos-mais-que-fazer-html-css/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/10/16/estamos-no-futuro-carros-voam-e-nao-temos-mais-que-fazer-html-css/</guid><pubDate>Fri, 16 Oct 2026 02:00:00 -0300</pubDate><description>&lt;p&gt;São mais de duas da manhã e eu ainda estou acordado porque acabei de assistir ao vivo ao novo Tesla Roadster subir uma rampa vertical, parar de cabeça pra baixo e, em outra passagem, girar no ar e pousar inteiro.&lt;/p&gt;
&lt;p&gt;Eu esperava um carro rápido. Não esperava um carro com dez bocais de propulsão a ar comprimido, usando tecnologia derivada da SpaceX, e um sistema de sucção embaixo capaz, &lt;a href="https://paultan.org/2026/10/16/tesla-roadster-revealed-flies-on-spacex-air-thrusters-0-96-km-h-in-0-81s-production-2028/"target="_blank" rel="noopener"&gt;segundo Elon Musk, de puxar até 2 G&lt;/a&gt;. Na demonstração, a Tesla anunciou zero a 60 milhas por hora, quase 97 km/h, em 0,81 segundo. Não divulgou o protocolo da medição.&lt;/p&gt;</description><content:encoded><![CDATA[<p>São mais de duas da manhã e eu ainda estou acordado porque acabei de assistir ao vivo ao novo Tesla Roadster subir uma rampa vertical, parar de cabeça pra baixo e, em outra passagem, girar no ar e pousar inteiro.</p>
<p>Eu esperava um carro rápido. Não esperava um carro com dez bocais de propulsão a ar comprimido, usando tecnologia derivada da SpaceX, e um sistema de sucção embaixo capaz, <a href="https://paultan.org/2026/10/16/tesla-roadster-revealed-flies-on-spacex-air-thrusters-0-96-km-h-in-0-81s-production-2028/"target="_blank" rel="noopener">segundo Elon Musk, de puxar até 2 G</a>. Na demonstração, a Tesla anunciou zero a 60 milhas por hora, quase 97 km/h, em 0,81 segundo. Não divulgou o protocolo da medição.</p>
<p>Era esse o futuro que eu queria ver.</p>
<p>A demonstração inteira está na <a href="https://livestream.tesla.com/"target="_blank" rel="noopener">transmissão oficial da Tesla</a>. A <a href="https://www.roadandtrack.com/news/a74151425/the-air-assisted-tesla-roadster-flies-in-demo-hits-0-60-in-08-seconds/"target="_blank" rel="noopener">Road &amp; Track mediu e descreveu as manobras</a>, e a <a href="https://techcrunch.com/2026/10/15/tesla-reveals-spacex-powered-roadster-that-flies-and-falls-with-style/"target="_blank" rel="noopener">TechCrunch detalhou as três versões</a>: Performance com aerodinâmica ativa e sucção, Thrust com propulsores traseiros, Rocket com sistema de propulsão de dez bocais e apenas um assento.</p>
<p>Claro, o que vimos foi um protótipo numa demonstração controlada. A Tesla prevê iniciar a produção em 2028, e a versão Rocket não está aprovada para uso em vias públicas. Ainda assim, um carro de verdade saiu do chão, girou e pousou. Eu assisti a isso ao vivo do meu escritório. Foi espetacular.</p>
<p>Enquanto Musk faz foguetes pousarem e carros voarem, eu estava fazendo um trabalho bem mais mundano e pequeno: mexendo nos meus sites.</p>
<h2>Seis sites em cerca de um dia<span class="hx:absolute hx:-mt-20" id="seis-sites-em-cerca-de-um-dia"></span>
    <a href="#seis-sites-em-cerca-de-um-dia" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Antes do evento da Tesla, eu passei o dia inteiro redesenhando seis sites:</p>
<ul>
<li><a href="https://ailair.akitaonrails.com/"target="_blank" rel="noopener">Akita&rsquo;s AI Lair</a>, agora combinando com o visual do meu livro novo, <em>Competente o Suficiente</em>;</li>
<li><a href="https://aimemory.sh/"target="_blank" rel="noopener">ai-memory</a>;</li>
<li><a href="https://aijail.io/"target="_blank" rel="noopener">ai-jail</a>;</li>
<li>a newsletter <a href="https://themakitachronicles.com/"target="_blank" rel="noopener">The M.Akita Chronicles</a>;</li>
<li>o <a href="https://blog.themakitachronicles.com/"target="_blank" rel="noopener">arquivo da newsletter</a>;</li>
<li>e este blog, <a href="https://akitaonrails.com/"target="_blank" rel="noopener">AkitaOnRails.com</a>.</li>
</ul>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/10/redesign-seis-sites-stitch.jpg" alt="Os seis sites redesenhados em produção: Akita’s AI Lair, ai-memory, ai-jail, The M.Akita Chronicles, o arquivo da newsletter e AkitaOnRails"  loading="lazy" /></p>
<p>Todos começaram com variações daquele tema padrão que o Claude faz. Fundo escuro, gradiente roxo ou azul, fonte sans enorme, cartões arredondados, brilho atrás de alguma coisa. Funciona. Também ficou tão comum que hoje você bate o olho e reconhece &ldquo;site feito pelo Claude&rdquo; antes de ler o título.</p>
<p>Eu já tinha tentado pedir designs diferentes pro Claude. Ele trocava a cor, movia os cartões e entregava a mesma gramática visual. Publiquei <a href="https://x.com/AkitaOnRails/status/2110809199890300986"target="_blank" rel="noopener">algumas telas e o resultado da mudança no Twitter</a>. Eu não sou web designer e, por um tempo, aceitei o &ldquo;funciona&rdquo;. Ontem cansei.</p>
<p>Alguém me sugeriu o <a href="https://stitch.withgoogle.com/"target="_blank" rel="noopener">Stitch, do Google</a>. É uma ferramenta experimental do Google Labs que transforma instruções e imagens em interfaces editáveis e código de front-end. A <a href="https://developers.googleblog.com/en/stitch-a-new-way-to-design-uis/"target="_blank" rel="noopener">primeira versão foi apresentada em 2025</a> em cima do Gemini; <a href="https://blog.google/innovation-and-ai/models-and-research/google-labs/stitch-ai-ui-design/"target="_blank" rel="noopener">a versão atual</a> virou uma tela de design com agente, protótipos e exportação de um <code>DESIGN.md</code> que outros agentes conseguem seguir.</p>
<p><a href="https://blog.google/innovation-and-ai/models-and-research/google-labs/stitch-ai-ui-design/"target="_blank" rel="noopener"><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/10/stitch-ai-native-canvas-google.webp" alt="O canvas do Google Stitch com várias interfaces, variações visuais e controles do design system"  loading="lazy" /></a></p>
<p>Criei a conta, peguei o token da API e comecei a trabalhar. O Stitch gerava direções visuais. Claude Code e Codex levavam essas direções pros repositórios reais, adaptavam os componentes, corrigiam responsividade, trocavam imagens, ajustavam SEO, testavam e faziam deploy. Quando a cota de um acabava, eu continuava com o outro.</p>
<p>Os seis sites saem de cinco repositórios. Olhando todos lado a lado, uma coisa ficou clara: o Stitch responde muito melhor quando recebe uma metáfora concreta, o público e o que aquele produto precisa comunicar. O ai-memory virou um arquivo de memória. O ai-jail, uma prisão cinematográfica. O AI Lair adotou uma feira mundial dos anos 1950 e 1960, puxada pela capa do meu livro. A newsletter virou uma transmissão noturna, e este blog, um arquivo editorial. Cinco identidades diferentes em vez de cinco variações do mesmo gradiente do Claude.</p>
<p>O protótipo bonito é a parte fácil. O trabalho só termina quando a linguagem visual chega às páginas internas, ao artigo, ao leitor do livro, ao e-mail, às páginas legais, aos estados de erro e ao celular. Foi isso que os agentes fizeram nos repositórios. No ai-memory, por exemplo, a verificação cobriu 102 páginas em seis idiomas, nos temas claro e escuro, em desktop e celular. No The M.Akita Chronicles, o mesmo design precisou funcionar na inscrição, no arquivo, no detalhe de newsletter, no podcast e no template do e-mail. Um mockup do Stitch não resolve nada disso sozinho.</p>
<p>Foram várias rodadas de protótipos em cada site. Algumas ficaram horrorosas. Outras tinham uma ideia boa cercada de decisões ruins. Eu mandava descartar, preservar uma parte, misturar com outra tentativa e recomeçar. Código ficou barato, então não existe motivo pra se apaixonar pela primeira versão.</p>
<p>O resultado não vai ganhar prêmio de design. Um designer profissional ainda encontraria centenas de coisas pra melhorar. Eu mesmo sei que consigo avançar se gastar mais alguns dias. Só que ficou muito melhor do que estava e, por enquanto, está competente o suficiente.</p>
<p>Seis sites existentes redesenhados, responsivos, com SEO, geração e substituição de imagens, ajustes de HTML, CSS e JavaScript, teste e deploy em produção. Uma pessoa. Cerca de um dia. Um ano atrás, esse pacote seria trabalho de semanas ou meses.</p>
<p>Vamos manter a proporção: são sites pequenos, a maioria landing pages e páginas de conteúdo. Ninguém vai recriar o conjunto inteiro de páginas e sistemas da Amazon.com em um dia. O experimento mostra quanto trabalho repetitivo já saiu da frente, dentro de um escopo que uma pessoa experiente consegue entender e revisar. Projetos grandes continuam grandes, cheios de regras privadas, integrações antigas, estados estranhos e decisões que não cabem num prompt.</p>
<h2>Até o primeiro proofreading pode ser automatizado<span class="hx:absolute hx:-mt-20" id="até-o-primeiro-proofreading-pode-ser-automatizado"></span>
    <a href="#at%c3%a9-o-primeiro-proofreading-pode-ser-automatizado" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>No mesmo processo eu mandei agentes revisarem os sites como leitores diferentes. Não era correção gramatical. Eu queria saber o que uma pessoa entenderia nos primeiros segundos, onde ficaria perdida, qual promessa pareceria suspeita e se conseguiria completar uma tarefa sem já conhecer o produto.</p>
<p>No ai-jail, usei duas sessões isoladas. Uma assumiu o papel de um engenheiro cético que aprova todos os prompts e nunca tinha ouvido falar do projeto. A outra era gerente de oito desenvolvedores, sem ser engenheira, tentando decidir se deveria aprovar a ferramenta na empresa. No ai-memory foram três: um engenheiro aprendendo ferramentas de IA, uma gerente avaliando um piloto e um pai sem experiência com agentes, preocupado com AGI e empregos. Cada um leu o site sem ver o relatório dos outros.</p>
<p>Qualquer pessoa consegue fazer o mesmo. Suba a aplicação localmente ou abra um deploy de preview, comece sessões novas e dê a cada agente uma tarefa concreta. Um prompt básico pode ser 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-text" data-lang="text"><span class="line"><span class="cl">Abra este site em desktop e celular: [URL].
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Você é [perfil], conhece [contexto] e nunca viu este produto.
</span></span><span class="line"><span class="cl">Seu objetivo é [decisão ou tarefa concreta].
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Navegue como essa pessoa. Não leia código nem documentação interna antes.
</span></span><span class="line"><span class="cl">Registre:
</span></span><span class="line"><span class="cl">- o que entendeu nos primeiros dez segundos;
</span></span><span class="line"><span class="cl">- o que acha que o produto faz e para quem serve;
</span></span><span class="line"><span class="cl">- onde não soube qual era o próximo passo;
</span></span><span class="line"><span class="cl">- promessas contraditórias, absolutas ou difíceis de acreditar;
</span></span><span class="line"><span class="cl">- termos que o site usa antes de explicar;
</span></span><span class="line"><span class="cl">- problemas de leitura, teclado, contraste, toque e responsividade;
</span></span><span class="line"><span class="cl">- o que impediria você de testar, comprar ou aprovar.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Para cada achado, mostre a página e a evidência visível.
</span></span><span class="line"><span class="cl">Separe bug factual, problema de uso e preferência subjetiva.
</span></span><span class="line"><span class="cl">Não proponha redesign geral. Sugira a menor mudança que resolve o problema.</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 prefiro três sessões independentes a uma mesa-redonda de agentes. Se um lê o relatório do outro, começa a concordar por inércia. Depois uma sessão separada consolida os pontos repetidos, descarta gosto pessoal e confere cada afirmação contra o código e a documentação do produto. A revisão pode apontar que uma frase está errada; ela não tem autoridade pra inventar a frase correta.</p>
<p>Os resultados mais úteis costumam cair em quatro grupos: posicionamento, ordem da informação, caminho até a primeira ação e confiança. Testes automáticos tradicionais continuam cuidando de link quebrado, contraste, acessibilidade, overflow e estado de erro. A persona mostra que o botão tecnicamente acessível apareceu cinco telas depois de onde a pessoa precisava dele.</p>
<p>No <a href="https://github.com/akitaonrails/ai-jail-web/blob/main/docs/research/PROOFREAD-2026-10-15-BRIEF.md"target="_blank" rel="noopener">proofreading do ai-jail</a>, os dois perfis concordaram que o site demorava pra mostrar o risco concreto e falava cedo demais como documentação de engenheiro. A ameaça de uma página envenenada foi puxada pra logo depois do hero. Entraram as seções &ldquo;O que muda no primeiro dia&rdquo; e &ldquo;Para quem precisa aprovar isto&rdquo;, com custo operacional, limites e as chaves que uma empresa realmente expõe. A revisão também encontrou uma afirmação tecnicamente errada repetida em várias páginas: a rede não fica inteiramente desligada por padrão. O host da própria API do agente e, no Linux, os registries continuam acessíveis. Esse tipo de leitura também encontra bug de documentação.</p>
<p>O <a href="https://github.com/akitaonrails/ai-memory-web/blob/main/docs/research/persona-review.md"target="_blank" rel="noopener">relatório do ai-memory</a> encontrou permissões descritas de formas contraditórias, promessas de privacidade absolutas demais e a explicação básica soterrada por features avançadas. Na segunda rodada, os perfis perceberam que a instrução de acesso remoto mandava usar HTTP antes de exigir TLS, que um <code>curl</code> sem autenticação prometia a resposta errada e que escolhas importantes apareciam tarde demais no celular. O guia de coleta saiu de aproximadamente 4.364 pixels do topo pra 1.617; os caminhos pra indivíduo ou equipe, de 5.300 pra 2.489. Isso é uma melhoria objetiva nascida de leitura simulada.</p>
<p>No <a href="https://github.com/akitaonrails/ai-toolkit-web/pull/4"target="_blank" rel="noopener">redesign do AI Lair</a>, a revisão para leitores novos reorganizou o caminho de entrada. Uma <a href="https://github.com/akitaonrails/ai-toolkit-web/commit/c2a5237e876f662a8b53450f23f0162fdc1d4a76"target="_blank" rel="noopener">revisão seguinte</a> deixou explícito que o meu setup com vários agentes e dois monitores é uma configuração madura, não requisito pra começar. Uma pessoa, um projeto e um agente bastam. Também surgiu um roteiro de piloto pra gerente: tarefa reversível, dado não sensível, critério de aceitação, prazo, orçamento e medição. A auditoria seguinte encontrou ARIA inválido e uma animação que escondia o texto de abertura de tecnologias assistivas; a <a href="https://github.com/akitaonrails/ai-toolkit-web/pull/5"target="_blank" rel="noopener">correção</a> levou o axe-core de quatro violações sérias pra zero.</p>
<p>Personas simuladas ainda ficam muito abaixo de feedback humano. Carregam os mesmos vícios dos modelos, podem concordar com o prompt e nunca sentem a irritação de quem perdeu vinte minutos, nem assumem o risco de colocar o próprio cartão de crédito. Os relatórios servem como hipóteses. Um usuário real sempre encontra o caso que eu, o agente e a suíte de testes não imaginaram.</p>
<p>Só que antes do primeiro usuário existir, a alternativa costuma ser nenhuma revisão. Duas ou três leituras independentes já pegam promessa contraditória, jargão sem explicação, informação enterrada e fluxo que só faz sentido pra quem escreveu. Rodar essas leituras custa pouco e deixa o produto menos burro antes de chegar em gente de verdade.</p>
<h2>HTML nunca foi programação<span class="hx:absolute hx:-mt-20" id="html-nunca-foi-programação"></span>
    <a href="#html-nunca-foi-programa%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Durante anos eu vi gente fazendo uma ginástica danada pra defender que &ldquo;HTML é linguagem de programação&rdquo; e, portanto, quem escreve HTML é programador.</p>
<p>HTML é marcação: descreve a estrutura de um documento. CSS descreve apresentação. Isso tem valor, exige conhecimento e pode ficar terrivelmente complexo. Complexidade acidental não transforma marcação em programação.</p>
<p>E não há vergonha nenhuma nisso. Comecei a fazer sites nos anos 1990. Recebia um layout feito no Photoshop e transformava aquilo em HTML. Recortava imagens, montava tabela, ajustava pixel, abria em vários navegadores, voltava e tentava de novo. Foi trabalho honesto e pagou minhas contas.</p>
<p>Também era um trabalho basicamente mecânico: copiar, colar, ajustar, olhar, repetir. Exatamente a categoria que computadores deveriam automatizar.</p>
<p>Hoje eu ainda escolhi direção visual, descartei propostas ruins, decidi o que cada site precisava comunicar e reconheci quando o resultado estava bom o suficiente. Os agentes fizeram o serviço braçal. É assim que deveria ter sido há anos.</p>
<h2>A bolha atrasou a automação<span class="hx:absolute hx:-mt-20" id="a-bolha-atrasou-a-automação"></span>
    <a href="#a-bolha-atrasou-a-automa%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Durante boa parte dos anos 2010, <a href="https://fred.stlouisfed.org/series/EFFR"target="_blank" rel="noopener">os juros americanos ficaram perto de zero</a>. Dinheiro barato irrigou fundos, startups e empresas grandes. Contratar mais gente era fácil, e o mercado absorvia qualquer quantidade de trabalho manual disfarçado de engenharia.</p>
<p>Juro baixo não explica sozinho a expansão da tecnologia, mas uma área em recorde de contratação tem pouco incentivo pra eliminar o próprio trabalho repetitivo. Se você consegue colocar mais cinquenta pessoas pra montar tela, coloca mais cinquenta pessoas pra montar tela.</p>
<p>Quem assistiu às minhas palestras em eventos de tecnologia nos anos 2010 deve lembrar de <strong>Restrição == Inovação</strong>. O nome correto era com dois sinais de igual mesmo. Apresentei essa palestra na DevConf 2019, os <a href="https://www.slideshare.net/slideshow/devconf-2019-so-carlos/143923367"target="_blank" rel="noopener">83 slides estão no meu SlideShare</a>, e eu já tinha escrito a mesma tese em 2013, em <a href="https://startupi.com.br/restricoes-sao-libertadoras-menos-e-mais/"target="_blank" rel="noopener">&ldquo;Restrições são libertadoras: menos é mais&rdquo;</a>.</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/V0D3PuTLOZ8"
    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>É a velha máxima: <strong>a necessidade é a mãe da invenção</strong>. Restrição obriga a escolher. Escolha obriga a priorizar. Quando o caminho convencional não cabe no dinheiro, no prazo ou na capacidade disponível, alguém precisa encontrar outro caminho. É daí que costuma sair inovação de verdade.</p>
<p>Dinheiro financia pesquisa cara, compra equipamento e dá tempo pra experimentar. Sobra de dinheiro sem pressão por resultado faz outra coisa: esconde ineficiência com força bruta. Não precisa automatizar, simplificar nem descobrir uma solução melhor enquanto ainda der pra contratar mais gente e aumentar a planilha. Abundância demais transforma problema técnico em despesa operacional e empurra a decisão difícil pra depois.</p>
<p>Eu <a href="/2026/02/08/rant-ia-acabou-com-programadores/">repeti esse aviso no começo de 2026</a>: a bolha de programação conseguiu substituir eficiência por volume. A restrição voltou quando o dinheiro barato acabou. Coincidentemente, os agentes chegaram na mesma hora e deram às empresas uma maneira prática de automatizar o trabalho mecânico que elas passaram uma década escalando com contratação.</p>
<p>Foi assim que a gente passou uma década recriando o mesmo painel administrativo e chamando de inovação. Toda semana aparecia a próxima salvação do front-end: React, Angular, Vue, Svelte, outro <em>bundler</em>, outro framework em cima do framework. São ferramentas úteis em seus contextos, mas nenhuma atacava a pergunta óbvia: por que um ser humano ainda está escrevendo à mão a milésima variação do mesmo formulário?</p>
<p>O estouro da bolha de contratação em 2022 encerrou a festa. No fim daquele ano, o ChatGPT levou modelos de linguagem ao público. GPT-2, só pra corrigir a cronologia, tinha saído em 2019. Quatro anos depois do ChatGPT, programação agêntica virou ferramenta normal de trabalho.</p>
<p>Qualquer tarefa que consiste em copiar padrão, adaptar exemplo, rodar, olhar o erro e tentar de novo já está no alcance desses agentes. Front-end é o alvo mais visível porque acumulou uma quantidade absurda desse trabalho. O resto da programação não está protegido por alguma aura mística. Tem muito back-end que é CRUD, cola entre APIs e configuração repetida. Também será automatizado.</p>
<p>Pra mim, ótimo. Programar nunca foi digitar código. Programar é fazer um computador executar algo útil.</p>
<h2>VS Code é o novo cartão perfurado<span class="hx:absolute hx:-mt-20" id="vs-code-é-o-novo-cartão-perfurado"></span>
    <a href="#vs-code-%c3%a9-o-novo-cart%c3%a3o-perfurado" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Eu escrevi exatamente sobre isso em abril, em <a href="/2026/04/11/vs-code-e-o-novo-cartao-perfurado/">&ldquo;VS Code é o novo Cartão Perfurado&rdquo;</a>.</p>
<p>Por décadas, a interface entre intenção e computador mudou. Primeiro você ligava fios e chaves. Depois perfurava cartões. Depois escrevia Assembly, C, linguagens de alto nível, frameworks e configuração. Cada geração olhou pra anterior e automatizou uma camada de trabalho manual.</p>
<p>Ninguém em sã consciência defenderia hoje que operar uma perfuradora de cartões era a essência da ciência da computação. Era a interface disponível. Digitar cada tag e cada regra de CSS no VS Code também era a interface disponível.</p>
<p>Agora eu descrevo a intenção, mostro referências, avalio o resultado e mando o agente corrigir. Ainda preciso entender como navegador, rede, cache, acessibilidade, semântica, responsividade e SEO funcionam. Sem fundamento eu nem saberia dizer se o agente entregou uma solução ou uma bomba-relógio bonita. O teclado perdeu importância; julgamento ganhou.</p>
<p>Hoje, todos os modelos Tier A que eu testo, de Claude a Kimi, conseguem executar com confiabilidade o trabalho mecânico de front-end que apareceu nesses seis projetos. Eu consigo afirmar, sem hesitar, que qualquer tarefa de front-end que só transforma uma decisão já tomada em código é automatizável. Eles não inventaram seis designs brilhantes sozinhos, porém nenhum precisou que eu escrevesse manualmente o flexbox, o breakpoint ou o componente que já expliquei.</p>
<p>Isso cobre uma parcela enorme do trabalho, mas deixa justamente a parte de maior valor. Alguém ainda precisa conhecer os prós e contras do framework, entender o browser, escolher a arquitetura, perceber quando a biblioteca está sendo usada contra o seu desenho e descobrir por que uma solução aparentemente correta quebra numa combinação específica de aparelho, idioma, estado e dado real. O agente escreve o código; um profissional experiente dá direção, limita o estrago e assume o resultado.</p>
<p>Front-end continua existindo. O excesso de trabalho mecânico e de gente contratada só pra executar receita pronta é que perde valor. Um ótimo profissional de front-end consegue dirigir agentes e entregar muito mais do que entregava sozinho. Um profissional mediano que passou anos confundindo digitação de componente com engenharia vai disputar menos vagas. O mercado fica menor e mais seletivo, com os melhores profissionais produzindo mais. A meta continua a mesma de sempre: chegar perto do topo, onde julgamento e experiência ainda fazem diferença.</p>
<p>E isso não para no browser. <a href="/2026/10/11/existem-linguagens-melhores-pra-programar-com-llms/">Escolha o framework</a>: Android com Flutter ou Kotlin, iOS com Swift e SwiftUI, desktop com Rust/Tauri, Flutter, C# ou Electron. Em geral, agentes modernos conhecem os componentes, as ferramentas de build, os testes e as peculiaridades dessas plataformas. Ainda precisam de direção, feedback e verificação, exatamente como no front-end web. A parte mecânica já é automatizável.</p>
<p>Eu testei isso nos meus próprios projetos pequenos e abertos. O <a href="https://github.com/akitaonrails/FrankYomik"target="_blank" rel="noopener">FrankYomik</a> tem um cliente Flutter pra Android e Linux, além do servidor e da extensão. O <a href="https://github.com/akitaonrails/FrankSherlock"target="_blank" rel="noopener">Frank Sherlock</a> é um app desktop em Rust/Tauri que gera AppImage pra Linux, DMG pra macOS e instalador <code>.exe</code> pra Windows. Também fiz o <a href="https://github.com/akitaonrails/frank_karaoke"target="_blank" rel="noopener">Frank Karaoke</a> em Flutter pra Android e o <a href="https://github.com/akitaonrails/frank_mangaplus"target="_blank" rel="noopener">FRANK MANGA+</a> em Rust/Tauri pra desktop. Há vários outros no <a href="https://github.com/akitaonrails?tab=repositories"target="_blank" rel="noopener">meu GitHub</a>.</p>
<p>Nenhum deles saiu perfeito no primeiro prompt. Flutter e Tauri me deram trabalho, especialmente nos primeiros projetos. Confiável quer dizer que o agente consegue avançar, receber uma correção, criar testes, corrigir o que quebrou e chegar num aplicativo funcional sem eu assumir a digitação braçal. Depois de testar web, Android e desktop em stacks diferentes, estou muito satisfeito com o resultado. O padrão se repete: trabalho mecânico virou trabalho de agente.</p>
<p>Às duas da manhã eu tinha agentes fazendo os últimos ajustes neste blog sem perder o ritmo. É disso que eu preciso.</p>
<h2>Quando a web ainda não sabia o que era<span class="hx:absolute hx:-mt-20" id="quando-a-web-ainda-não-sabia-o-que-era"></span>
    <a href="#quando-a-web-ainda-n%c3%a3o-sabia-o-que-era" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>No auge da bolha da Internet, designers de carreira estavam criando sites. A web ainda não tinha decidido como uma página deveria funcionar, então havia espaço real pra experimentar. Era feio muitas vezes, pesado quase sempre, mas tinha criação acontecendo.</p>
<p>O iPhone abriu outra janela em 2007. De repente tudo precisava caber numa tela pequena, responder ao toque e funcionar sem mouse. Vieram anos de experimentação até o mercado formar uma gramática razoavelmente estável de navegação, gesto, formulário e conteúdo.</p>
<p>Hoje ninguém precisa redescobrir um menu, uma lista, um checkout ou uma página de artigo. Os padrões estão documentados, milhões de exemplos estão públicos e os modelos viram mais interfaces do que qualquer pessoa conseguiria estudar durante a vida.</p>
<p>O trabalho mais valioso continua existindo: entender o produto, a pessoa que vai usá-lo, as restrições e o que merece aparecer primeiro. O trabalho de converter uma decisão pronta em centenas de linhas previsíveis virou candidato perfeito à automação.</p>
<h2>Empregos desaparecem. Outros aparecem<span class="hx:absolute hx:-mt-20" id="empregos-desaparecem-outros-aparecem"></span>
    <a href="#empregos-desaparecem-outros-aparecem" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O mundo nunca ficou parado pra preservar uma função.</p>
<p>Operadores de cartão perfurado digitavam dados e programas em papel. Telefonistas conectavam chamadas fisicamente. Datilógrafos mantinham escritórios funcionando. Linotipistas montavam linhas de metal quente pra imprimir jornais. A tecnologia reduziu ou eliminou essas ocupações em escala. A <a href="https://www.bls.gov/emp/tables/fastest-declining-occupations.htm"target="_blank" rel="noopener">lista atual de profissões em maior declínio do Bureau of Labor Statistics</a> ainda inclui digitadores, datilógrafos e operadores de telefonia. Ninguém propõe voltar a elas pra proteger emprego.</p>
<p>Ao mesmo tempo apareceram trabalhos que uma pessoa de 1926 nem conseguiria nomear: desenvolvedor mobile, SRE, designer de experiência, criador de vídeo pra Internet, especialista em nuvem, engenheiro de machine learning. A própria web matou trabalho antigo e abriu uma indústria inteira.</p>
<p>A história real é menos limpa do que a caricatura. A <a href="https://www.ilo.org/publications/generative-ai-and-jobs-refined-global-index-occupational-exposure"target="_blank" rel="noopener">OIT mostra que a tecnologia costuma automatizar tarefas sem necessariamente eliminar ocupações inteiras</a>. Algumas funções encolhem, outras mudam e trabalhos novos aparecem. Esse consolo estatístico não resolve a vida de quem ficou obsoleto hoje. A pessoa precisa reagir antes que a média da economia reaja por ela.</p>
<p>Compare 2026 com 1926, três anos antes do crash de 1929. Parece outro planeta, embora um século seja muito pouco na escala da história. Não existe contrato garantindo que a sua função atual sobreviva aos próximos dez anos, muito menos aos próximos quarenta.</p>
<h2>E como um júnior ganha experiência agora?<span class="hx:absolute hx:-mt-20" id="e-como-um-júnior-ganha-experiência-agora"></span>
    <a href="#e-como-um-j%c3%banior-ganha-experi%c3%aancia-agora" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Vai ficar mais difícil. A escada antiga começava com alguém pagando um iniciante pra montar tela simples, corrigir CSS e copiar componente. O agente faz boa parte disso mais rápido e barato. Empresa nenhuma tem obrigação de preservar trabalho ineficiente como programa de treinamento.</p>
<p>O iniciante vai ter que construir a própria escada com projetos reais e problemas pelos quais ele seja responsável. Isso inclui ler o código que o agente gerou, quebrar e consertar, estudar documentação e código-fonte, escrever testes e contribuir com projetos abertos. O agente dá a qualquer estudante acesso a uma quantidade de prática que antes exigia uma equipe. Quem usar isso só pra gerar código que não entende vai continuar júnior por muito tempo.</p>
<p>O mercado vai premiar quem fizer esse esforço extra. Sempre premiou. Quando alguém pergunta como o júnior vai ganhar experiência sem as tarefas fáceis, muitas vezes a pergunta escondida é outra: &ldquo;como eu consigo um salário melhor sem passar pelo trabalho difícil de ficar bom?&rdquo; A vaga fácil, com salário crescente e pouca cobrança, foi uma distorção da bolha. Adaptar-se inclui aceitar que a entrada ficou mais competitiva.</p>
<h2>Minha carreira nunca ficou no mesmo lugar<span class="hx:absolute hx:-mt-20" id="minha-carreira-nunca-ficou-no-mesmo-lugar"></span>
    <a href="#minha-carreira-nunca-ficou-no-mesmo-lugar" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><a href="/2023/02/14/akitando-138-meu-comeco-de-carreira-durante-a-bolha-1997-a-2002-bug-do-milenio-crash/">Comecei com multimídia e sites estáticos</a>. Depois trabalhei em agência digital, consultoria enterprise, integração de SAP, startups, vídeo, educação e, agora, IA. No <a href="/2018/12/26/akitando-34-voce-nao-sabe-nada-de-enterprise-conhecendo-a-sap/">Akitando sobre SAP</a>, eu conto parte dessa fase em que precisei aprender um mundo completamente diferente do que fazia antes.</p>
<p>Atravessei o crash das pontocom, a crise de 2008 e o fim da bolha de juro zero. Sobrevivi porque nunca tratei a minha função do momento como identidade permanente. Continuei estudando, acumulando experiência e mudando de direção.</p>
<p>Uma carreira produtiva não deveria consistir em repetir a mesma tarefa mecânica por décadas. Se o seu valor depende de o mercado continuar pagando alguém pra empurrar tag e pixel à mão, o problema não começou com a IA. O problema foi apostar que o presente seria eterno.</p>
<p>Muita gente entende &ldquo;estar preparado&rdquo; como estudar o mínimo necessário pra vaga aberta hoje. Essa é a receita mais rápida pra ficar obsoleto. Faça o trabalho de curto prazo, mas estude além dele. Aprenda as coisas que parecem inúteis agora. Fundamentos, sistemas operacionais, redes, bancos de dados, negócio, economia, história. Você não sabe qual combinação vai salvar sua carreira daqui a cinco anos.</p>
<p>Eu passei anos falando disso no Akitando. Reveja as playlists <a href="https://www.youtube.com/playlist?list=PLdsnXVqbHDUehzKjiRruy--gncHz9Injy"target="_blank" rel="noopener"><strong>Eu Avisei</strong></a> e <a href="https://www.youtube.com/playlist?list=PLdsnXVqbHDUd6wEL932R7dW9Zazt2oNWF"target="_blank" rel="noopener"><strong>Que curso devo fazer??</strong></a>. Se tiver que começar por um vídeo, comece pelo <strong>Guia DEFINITIVO de Aprendendo a Aprender</strong>. Curso nenhum salva quem ainda terceiriza o próprio aprendizado.</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/oUPaJxk6TZ0"
    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>O mundo não tem falta de oportunidade. A maioria das pessoas sente que nunca recebe uma porque está sempre despreparada quando ela aparece. Quando a oportunidade pousa na sua frente, já é tarde pra começar a preparação.</p>
<p>Portanto, eu não vou lamentar que o pacote desses seis sites pequenos, que antes tomaria semanas ou meses, tenha cabido em cerca de um dia. Estou feliz pra cacete. Ganhei tempo pra pensar no que construir, experimentar mais e jogar fora versões ruins sem desperdiçar semanas de vida.</p>
<p>Hoje eu vi um carro girar no ar e pousar. Em cerca de um dia, coloquei seis sites com visual novo em produção sem escrever a maior parte do HTML e CSS. O futuro que eu queria não chegou perfeito, barato nem distribuído igualmente. Mas chegou o bastante pra eu trabalhar com ele.</p>
<p>Aceite o que acabou. Estude o que vem depois. E esteja pronto antes da próxima oportunidade aparecer.</p>
]]></content:encoded><category>inteligencia-artificial</category><category>frontend</category><category>automacao</category><category>carreira</category></item><item><title>Frontend Sem Framework | Bastidores do The M.Akita Chronicles</title><link>https://akitaonrails.github.io/2026/02/19/frontend-sem-framework-bastidores-do-the-m-akita-chronicles/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/02/19/frontend-sem-framework-bastidores-do-the-m-akita-chronicles/</guid><pubDate>Thu, 19 Feb 2026 01:08:23 +0000</pubDate><description>&lt;p&gt;Este post vai fazer parte de uma série; acompanhe pela tag &lt;a href="https://akitaonrails.github.io/tags/the-m-akita-chronicles/"&gt;/themakitachronicles&lt;/a&gt;. Esta é a parte 4.&lt;/p&gt;
&lt;p&gt;E não deixe de assinar minha nova newsletter &lt;a href="https://themakitachronicles.com/"target="_blank" rel="noopener"&gt;The M.Akita Chronicles&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&amp;ndash;&lt;/p&gt;
&lt;p&gt;Existe uma obsessão no mercado de que frontend = React. Ou Vue. Ou Svelte. Ou o framework da semana. E se eu te disser que é possível ter um frontend moderno, responsivo, com dark mode, email-compatible, multi-plataforma — sem uma única linha de JavaScript framework?&lt;/p&gt;</description><content:encoded><![CDATA[<p>Este post vai fazer parte de uma série; acompanhe pela tag <a href="/tags/the-m-akita-chronicles/">/themakitachronicles</a>. Esta é a parte 4.</p>
<p>E não deixe de assinar minha nova newsletter <a href="https://themakitachronicles.com/"target="_blank" rel="noopener">The M.Akita Chronicles</a>!</p>
<p>&ndash;</p>
<p>Existe uma obsessão no mercado de que frontend = React. Ou Vue. Ou Svelte. Ou o framework da semana. E se eu te disser que é possível ter um frontend moderno, responsivo, com dark mode, email-compatible, multi-plataforma — sem uma única linha de JavaScript framework?</p>
<p>Não estou falando de sites estáticos dos anos 2000. Estou falando de Tailwind CSS v4 no Rails, Hugo com tema Hextra pro blog, e templates de email que funcionam do Gmail ao Outlook 2016. Tudo no mesmo projeto, com um desenvolvedor.</p>
<h2>Tailwind CSS v4: O Fim do CSS Artesanal<span class="hx:absolute hx:-mt-20" id="tailwind-css-v4-o-fim-do-css-artesanal"></span>
    <a href="#tailwind-css-v4-o-fim-do-css-artesanal" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Eu tenho uma confissão: CSS sempre foi minha kryptonita. Não porque é difícil — mas porque é <strong>imprevisível</strong>. Você muda um <code>margin-top</code> e quebra o layout de uma página que nem tocou. Cascata, especificidade, herança — é um campo minado.</p>
<p>Tailwind resolve isso da forma mais pragmática possível: classes utilitárias direto no HTML.</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-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;flex items-center gap-4 p-6 bg-white dark:bg-gray-800
</span></span></span><span class="line"><span class="cl"><span class="s">            rounded-xl shadow-sm border border-gray-200 dark:border-gray-700&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">img</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;w-12 h-12 rounded-full&#34;</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;/images/avatar.jpg&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">h3</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;font-semibold text-gray-900 dark:text-gray-100&#34;</span><span class="p">&gt;</span>Título<span class="p">&lt;/</span><span class="nt">h3</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-sm text-gray-500 dark:text-gray-400&#34;</span><span class="p">&gt;</span>Descrição<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&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>&ldquo;Mas isso é inline style com passos extras!&rdquo; — Não, não é. É um sistema de design com constraints. Você não escolhe &ldquo;margin: 17px&rdquo; — você escolhe <code>m-4</code> (16px) ou <code>m-5</code> (20px). Esse constraint force consistência visual sem precisar de um design system documentado em 40 páginas.</p>
<p>E o Tailwind v4, que veio com o Rails via <code>tailwindcss-rails</code>, tem uma mudança fundamental: <strong>CSS-first configuration</strong>. Acabou o <code>tailwind.config.js</code>. Tudo é CSS:</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-css" data-lang="css"><span class="line"><span class="cl"><span class="p">@</span><span class="k">import</span> <span class="s2">&#34;tailwindcss&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">theme</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">--color-brand</span><span class="o">:</span> <span class="p">#</span><span class="nn">8b5cf6</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">--font-sans</span><span class="o">:</span> <span class="s2">&#34;Inter&#34;</span><span class="o">,</span> <span class="nt">system-ui</span><span class="o">,</span> <span class="nt">sans-serif</span><span class="o">;</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>Menos um arquivo de config. Menos um passo de build. Mais simples.</p>
<h2>Dark Mode: Respeite Seu Usuário<span class="hx:absolute hx:-mt-20" id="dark-mode-respeite-seu-usuário"></span>
    <a href="#dark-mode-respeite-seu-usu%c3%a1rio" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-20_01-05-42.jpg" alt="change theme"  loading="lazy" /></p>
<p>Uma coisa que me irrita profundamente é site que ignora preferência de tema do sistema operacional. O usuário configurou dark mode no OS — respeita.</p>
<p>Com Tailwind, dark mode é trivial:</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-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100&#34;</span><span class="p">&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>O <code>dark:</code> prefixo aplica o estilo quando o sistema operacional está em dark mode (via <code>prefers-color-scheme: dark</code>). Não precisa de JavaScript. Não precisa de toggle. Não precisa de cookie. Funciona.</p>
<p>Mas &ldquo;funciona&rdquo; é o mínimo. O desafio real é manter <strong>consistência</strong> entre dezenas de componentes. Toda cor que você usar precisa de uma variante <code>dark:</code>. Todo background, todo texto, toda borda.</p>
<p>O truque: crie convenções e siga. Background principal? <code>bg-white dark:bg-gray-900</code>. Texto primário? <code>text-gray-900 dark:text-gray-100</code>. Texto secundário? <code>text-gray-500 dark:text-gray-400</code>. Bordas? <code>border-gray-200 dark:border-gray-700</code>. Com 4-5 pares definidos, 90% dos componentes ficam cobertos.</p>
<h2>Hugo + Hextra: Blog Sem Banco de Dados<span class="hx:absolute hx:-mt-20" id="hugo--hextra-blog-sem-banco-de-dados"></span>
    <a href="#hugo--hextra-blog-sem-banco-de-dados" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O blog do projeto roda Hugo com o tema Hextra. Hugo é um gerador de sites estáticos escrito em Go — absurdamente rápido. Rebuild completo em &lt;500ms mesmo com dezenas de páginas.</p>
<p>O que torna o Hextra especial é que ele é um tema de <strong>documentação</strong> adaptado pra blog. Sidebar de navegação, search integrado, table of contents — tudo pronto. E customizável via layouts parciais:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>layouts/
├── _partials/
│   ├── custom/
│   │   └── head-end.html          # Injeta analytics, fonts, CSS customizado
│   └── section-box.html           # Partial compartilhado (DRY, veja abaixo)
├── blog/
│   └── home.html                  # Homepage customizada
├── newsletters/
│   └── single.html                # Template de newsletter individual
├── podcast-transcripts/
│   └── list.html                  # Listagem de episódios
└── shortcodes/                    # 17 shortcodes — componentes sem JS
    ├── podcast-turn.html          # Balão de fala no transcript
    ├── akita-comment.html         # Comentário do Akita
    ├── marvin-comment.html        # Comentário do Marvin
    ├── history-timeline.html      # Timeline de eventos históricos
    ├── market-ticker.html         # Tabela de tickers financeiros
    ├── market-map.html            # Mapa FinViz S&amp;P 500
    ├── subscribe-cta.html         # Call-to-action de inscrição
    ├── score.html                 # Badge de relevância (high/medium/low)
    ├── holidays.html              # Feriados e datas comemorativas
    ├── ref-stories.html           # Links de referência
    ├── book-downloads.html        # Links de download do livro
    ├── hacker-news.html           # ┐
    ├── anime-ranking.html         # │ Section boxes — delegam para
    ├── youtube-digest.html        # │ section-box.html (ver abaixo)
    ├── world-events.html          # │
    ├── market-news.html           # │
    └── qa.html                    # ┘</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>Cada tipo de conteúdo tem seu template. Newsletters têm sidebar de navegação e seções visuais. Transcrições de podcast têm player embutido e formatação de diálogo. O mesmo conteúdo Markdown é renderizado de formas diferentes dependendo do <code>type</code> no frontmatter.</p>
<h2>Shortcodes: Componentes Sem JavaScript<span class="hx:absolute hx:-mt-20" id="shortcodes-componentes-sem-javascript"></span>
    <a href="#shortcodes-componentes-sem-javascript" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Hugo shortcodes são o equivalente a componentes React, mas sem build step:</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-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- layouts/shortcodes/podcast-turn.html --&gt;</span>
</span></span><span class="line"><span class="cl">{{ $speaker := .Get &#34;speaker&#34; }}
</span></span><span class="line"><span class="cl">{{ $isAkita := eq $speaker &#34;akita&#34; }}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;podcast-turn&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;display: flex; {{ if $isAkita }}flex-direction: row{{ else }}flex-direction: row-reverse{{ end }};&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;/images/{{ $speaker }}.jpg&#34;</span>
</span></span><span class="line"><span class="cl">       <span class="na">style</span><span class="o">=</span><span class="s">&#34;width: 40px; height: 40px; border-radius: 50%;&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;bubble {{ if $isAkita }}akita-bubble{{ else }}marvin-bubble{{ end }}&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    {{ .Inner | markdownify }}
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&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>No Markdown, o autor escreve:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>{{&lt; podcast-turn speaker=&#34;akita&#34; &gt;}}
Então, vamos falar sobre o que aconteceu essa semana no mundo tech...
{{&lt; /podcast-turn &gt;}}</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><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-20_01-06-23.jpg" alt="baloon"  loading="lazy" /></p>
<p>E o Hugo renderiza um balão de chat estilizado, com avatar, alinhamento correto, e markdown processado. Zero JavaScript. Zero framework. HTML + CSS + template logic.</p>
<h3>DRY com Partial Compartilhado<span class="hx:absolute hx:-mt-20" id="dry-com-partial-compartilhado"></span>
    <a href="#dry-com-partial-compartilhado" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Com 17 shortcodes, duplicação vira problema rápido. Sete deles — os &ldquo;section boxes&rdquo; que envolvem seções da newsletter (Hacker News, Anime, YouTube, etc.) — têm a mesma estrutura visual: ícone, título, borda colorida, conteúdo Markdown. Só mudam a cor e o label.</p>
<p>A solução é um partial compartilhado que recebe parâmetros:</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-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- layouts/partials/section-box.html --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;section-box {{ .class }}&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;section-box-header&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">span</span><span class="p">&gt;</span>{{ .icon }}<span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span> {{ .label }}
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;section-box-content&#34;</span><span class="p">&gt;</span>{{ .content | markdownify }}<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&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>E cada shortcode vira uma linha:</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-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- layouts/shortcodes/hacker-news.html --&gt;</span>
</span></span><span class="line"><span class="cl">{{ partial &#34;section-box&#34; (dict &#34;class&#34; &#34;hacker-news&#34; &#34;icon&#34; &#34;<span class="ni">&amp;#128274;</span>&#34; &#34;label&#34; &#34;The Hacker News&#34; &#34;content&#34; .Inner) }}</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><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-20_01-07-02.jpg" alt="hacker news"  loading="lazy" /></p>
<p>É o equivalente Hugo de herança de componentes: um partial polimórfico parametrizado por <code>dict</code>. O CSS em <code>custom.css</code> define cores por classe (<code>.hacker-news</code>, <code>.anime-ranking</code>, etc.) com variantes light/dark. Resultado: 7 shortcodes que antes tinham HTML duplicado agora são 7 linhas + 1 partial + CSS.</p>
<p>Mas tem um pegadinho crucial: <strong>shortcodes são exclusivos do Hugo</strong>. O mesmo conteúdo que vai pro blog também vai por email. E email não entende shortcodes.</p>
<h2>O Desafio: Mesmo Conteúdo, Dois Renders<span class="hx:absolute hx:-mt-20" id="o-desafio-mesmo-conteúdo-dois-renders"></span>
    <a href="#o-desafio-mesmo-conte%c3%bado-dois-renders" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Aqui está o problema central de frontend nesse tipo de projeto: o mesmo arquivo Markdown precisa funcionar em dois contextos completamente diferentes.</p>
<p><strong>No blog (Hugo):</strong></p>
<ul>
<li>Shortcodes funcionam (<code>\{\{&lt; subscribe-cta &gt;\}\}</code>)</li>
<li>CSS com classes (Tailwind, Hextra)</li>
<li>Dark mode via <code>prefers-color-scheme</code></li>
<li>Imagens responsive</li>
<li>Layout flexível</li>
</ul>
<p><strong>No email (Rails → Amazon SES):</strong></p>
<ul>
<li>Shortcodes são texto morto — precisam ser removidos</li>
<li>CSS deve ser <strong>inline</strong> (muitos clientes de email ignoram <code>&lt;style&gt;</code>)</li>
<li>Dark mode? Esqueça pra 80% dos clientes</li>
<li>Imagens com URLs absolutas e tamanho fixo</li>
<li>Layout com <code>&lt;table&gt;</code> (sim, em 2026)</li>
</ul>
<p>A solução: dois pipelines de renderização independentes. O conteúdo Markdown é a source of truth. O Hugo transforma pra web. O Rails transforma pra email. Cada um com suas regras.</p>
<p>No lado do Rails, o serviço <code>MarkdownToHtml</code> precisa:</p>
<ol>
<li>Processar Markdown padrão</li>
<li>Converter <code>\{\{&lt; shortcode &gt;\}\}</code> em HTML equivalente ou removê-los — via um hash de <code>SHORTCODE_PATTERNS</code> que mapeia cada shortcode a uma regex + template inline com CSS</li>
<li>Aplicar tema (light/dark baseado na preferência do assinante)</li>
<li>Injetar CSS inline pra compatibilidade de email clients</li>
<li>Reescrever URLs de imagem pra absolutas</li>
</ol>
<p>É mais trabalho que parece. Cada shortcode precisa de um equivalente email-safe. Avatares de comentário precisam de URLs absolutas. Seções com background colorido precisam de <code>&lt;td style=&quot;background-color:...&quot;&gt;</code> ao invés de classes CSS.</p>
<h2>Email HTML: O Pior Frontend do Mundo<span class="hx:absolute hx:-mt-20" id="email-html-o-pior-frontend-do-mundo"></span>
    <a href="#email-html-o-pior-frontend-do-mundo" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se você acha que cross-browser compatibility é difícil, tente cross-email-client compatibility. O Outlook 2016 renderiza HTML usando o <strong>engine do Microsoft Word</strong>. Sim, Word. Não é piada.</p>
<p>As regras de sobrevivência:</p>
<ol>
<li><strong>Layout com <code>&lt;table&gt;</code></strong>. <code>display: flex</code>? <code>grid</code>? Não existem no Outlook.</li>
<li><strong>CSS inline em todo elemento</strong>. <code>&lt;style&gt;</code> tags são ignoradas por vários clients.</li>
<li><strong>Sem <code>margin</code> em imagens</strong>. Use <code>padding</code> na <code>&lt;td&gt;</code> pai.</li>
<li><strong>Fonts web não funcionam</strong>. Use font stack: <code>font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;</code></li>
<li><strong>Temas são preferência do assinante</strong>, não detecção automática — muitos email clients não suportam <code>prefers-color-scheme</code>.</li>
</ol>
<p>O template de email do projeto é um ERB que recebe variáveis de tema:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>&lt;td style=&#34;padding: 8px 28px; color: &lt;%= @theme[:text] %&gt;;
           background: &lt;%= @theme[:card_bg] %&gt;;&#34;&gt;
  &lt;%= raw @content %&gt;
&lt;/td&gt;</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 tema define todas as cores:</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">THEMES</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="ss">light</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="ss">text</span><span class="p">:</span> <span class="s2">&#34;#1a1a1a&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="ss">card_bg</span><span class="p">:</span> <span class="s2">&#34;#ffffff&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="ss">link_color</span><span class="p">:</span> <span class="s2">&#34;#7c3aed&#34;</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="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="ss">dark</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="ss">text</span><span class="p">:</span> <span class="s2">&#34;#e6edf3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="ss">card_bg</span><span class="p">:</span> <span class="s2">&#34;#161b22&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="ss">link_color</span><span class="p">:</span> <span class="s2">&#34;#a78bfa&#34;</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="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>Cada assinante escolhe seu tema na hora de se inscrever. O email é renderizado com as cores corretas pra aquela pessoa. Sem media queries, sem detecção automática — <strong>porque email clients não suportam de forma confiável</strong>. É mais simples dar a escolha pro usuário.</p>
<h2>Paleta de Cores: HSL e a Ciência por Trás das Escolhas<span class="hx:absolute hx:-mt-20" id="paleta-de-cores-hsl-e-a-ciência-por-trás-das-escolhas"></span>
    <a href="#paleta-de-cores-hsl-e-a-ci%c3%aancia-por-tr%c3%a1s-das-escolhas" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>&ldquo;Mas como vocês escolheram essas cores?&rdquo; — Não foi no olhômetro. Existe uma ciência por trás, e ela se chama <strong>HSL</strong> (Hue, Saturation, Lightness).</p>
<p>A maioria dos desenvolvedores pensa em cor como hex (<code>#e8eef8</code>) ou RGB (<code>rgb(232, 238, 248)</code>). São formatos de máquina — úteis pra renderizar, inúteis pra <strong>projetar</strong>. Quando você olha <code>#3b50a2</code> e <code>#c93030</code> lado a lado, não tem como saber intuitivamente se vão funcionar juntas. São só números.</p>
<p>HSL resolve isso. Em vez de misturar canais de luz (Red, Green, Blue), você descreve cor em termos humanos:</p>
<ul>
<li><strong>Hue</strong> (matiz): a posição no círculo cromático, de 0° a 360°. Vermelho = 0°, verde = 120°, azul = 240°.</li>
<li><strong>Saturation</strong> (saturação): quão &ldquo;viva&rdquo; é a cor. 0% = cinza, 100% = cor pura.</li>
<li><strong>Lightness</strong> (luminosidade): quão clara ou escura. 0% = preto, 100% = branco.</li>
</ul>
<p>Com HSL, você controla cada dimensão independentemente. E é aí que entra a construção da paleta.</p>
<h3>Passo 1: Distribuir Matizes no Círculo Cromático<span class="hx:absolute hx:-mt-20" id="passo-1-distribuir-matizes-no-círculo-cromático"></span>
    <a href="#passo-1-distribuir-matizes-no-c%c3%adrculo-crom%c3%a1tico" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-20_01-09-47.jpg" alt="color wheel"  loading="lazy" /></p>
<p>A newsletter tem 8 tipos de seção, cada uma com cor própria. Pra que todas &ldquo;pertençam&rdquo; visualmente ao mesmo projeto, os matizes são distribuídos uniformemente no círculo de 360°:</p>
<table>
  <thead>
      <tr>
          <th>Seção</th>
          <th>Matiz</th>
          <th>Graus</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>YouTube</td>
          <td>Vermelho</td>
          <td>0°</td>
      </tr>
      <tr>
          <td>História</td>
          <td>Sépia</td>
          <td>35°</td>
      </tr>
      <tr>
          <td>Mundo</td>
          <td>Âmbar</td>
          <td>45°</td>
      </tr>
      <tr>
          <td>Livro/Downloads</td>
          <td>Verde</td>
          <td>140°</td>
      </tr>
      <tr>
          <td>Feriados</td>
          <td>Teal</td>
          <td>180°</td>
      </tr>
      <tr>
          <td>Anime/Q&amp;A</td>
          <td>Azul</td>
          <td>210°</td>
      </tr>
      <tr>
          <td>Market News</td>
          <td>Royal</td>
          <td>225°</td>
      </tr>
      <tr>
          <td>Hacker News</td>
          <td>Roxo</td>
          <td>270°</td>
      </tr>
  </tbody>
</table>
<p>Oito matizes espaçados ao redor do círculo. Não é aleatório — é a mesma lógica de uma <strong>paleta policromática</strong> em teoria das cores. Quando os matizes são equidistantes, o olho humano percebe harmonia mesmo com cores completamente diferentes. É o mesmo princípio que artistas usam com um círculo cromático de 12 cores desde o século XVIII.</p>
<h3>Passo 2: Fixar Saturação e Luminosidade por Camada<span class="hx:absolute hx:-mt-20" id="passo-2-fixar-saturação-e-luminosidade-por-camada"></span>
    <a href="#passo-2-fixar-satura%c3%a7%c3%a3o-e-luminosidade-por-camada" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/HSV_color_solid_cylinder_saturation_gray.jpg" alt="saturation luminosity"  loading="lazy" /></p>
<p>Aqui está o truque que faz tudo funcionar junto. Cada &ldquo;camada&rdquo; visual (background, texto, borda) tem saturação e luminosidade <strong>fixas</strong> — só o matiz muda:</p>
<p><strong>Tema claro:</strong></p>
<ul>
<li><strong>Backgrounds</strong>: ~92-95% luminosidade, saturação moderada. Com luminosidade tão alta, mesmo saturações diferentes resultam em cores pastel com peso visual parecido — nenhuma seção &ldquo;grita&rdquo; mais que outra.</li>
<li><strong>Texto</strong>: ~15-20% luminosidade. Escuro o suficiente pra garantir <strong>ratio de contraste &gt;7:1</strong> contra o background claro (acima do mínimo WCAG AAA de 7:1).</li>
<li><strong>Bordas</strong>: ~45-55% luminosidade. O acento cromático — visível mas não dominante.</li>
</ul>
<p><strong>Tema escuro:</strong></p>
<ul>
<li><strong>Backgrounds</strong>: ~10-12% luminosidade. Profundidade uniforme — todas as seções têm a mesma &ldquo;escuridão&rdquo;.</li>
<li><strong>Texto</strong>: ~80-85% luminosidade. Claro o suficiente pra <strong>ratio de contraste &gt;6:1</strong> no fundo escuro.</li>
<li><strong>Bordas</strong>: mesma faixa de luminosidade que no tema claro — funcionam como acento nos dois contextos.</li>
</ul>
<p>Na prática, isso significa que gerar a variante dark de qualquer cor é mecânico: mantém o matiz, inverte a luminosidade. Background que era 94% vira 11%. Texto que era 18% vira 83%. Não é design — é aritmética.</p>
<h3>Passo 3: Verificar Contraste (WCAG)<span class="hx:absolute hx:-mt-20" id="passo-3-verificar-contraste-wcag"></span>
    <a href="#passo-3-verificar-contraste-wcag" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><a href="https://accessibilityassistant.com/blog/accessibility-insights/wcag-2-colour-contrast-accessibility-guidelines/"target="_blank" rel="noopener"><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/Why-Colour-Contrast-Accessibility-Matters-More-Than-Ever.jpg" alt="Why Colour Contrast Accessibility Matters More Than Ever"  loading="lazy" /></a></p>
<p>Cores bonitas que ninguém consegue ler não servem pra nada. O padrão WCAG (Web Content Accessibility Guidelines) define ratios mínimos:</p>
<ul>
<li><strong>AA</strong>: 4.5:1 pra texto normal, 3:1 pra texto grande</li>
<li><strong>AAA</strong>: 7:1 pra texto normal, 4.5:1 pra texto grande</li>
</ul>
<p>Os backgrounds claros com ~95% luminosidade contra texto com ~20% luminosidade dão ratio &gt;7:1 — WCAG AAA. Os backgrounds escuros (~11%) contra texto claro (~82%) dão &gt;6:1 — confortavelmente WCAG AA, próximo de AAA.</p>
<p>Isso não é por altruísmo. Email com baixo contraste é mais difícil de ler, gera menos engajamento, e em casos extremos é sinalizado por filtros de acessibilidade de email clients.</p>
<h3>O Resultado<span class="hx:absolute hx:-mt-20" id="o-resultado"></span>
    <a href="#o-resultado" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-20_01-07-50.jpg" alt="example colors"  loading="lazy" /></p>
<p>Quando você abre a newsletter e vê a seção do Hacker News (roxa), seguida pelo YouTube (vermelha), seguida pelo Anime (azul) — elas parecem &ldquo;do mesmo projeto&rdquo; apesar de cores completamente diferentes. Isso é o HSL trabalhando: matizes diferentes, mesma saturação, mesma luminosidade. O olho percebe coerência sem saber explicar por quê.</p>
<p>E a melhor parte: adicionar uma seção nova é trivial. Escolhe um matiz que não está ocupado no círculo, aplica as mesmas regras de saturação/luminosidade por camada, e a nova seção automaticamente &ldquo;pertence&rdquo; ao design existente.</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"># O comentário no código resume as regras:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># - Hues evenly spaced: blue 210, green 140, sepia 35, amber 45, royal 225, purple 270, red 0, teal 180</span>
</span></span><span class="line"><span class="cl"><span class="c1"># - Light backgrounds: ~95% lightness, ~25% saturation</span>
</span></span><span class="line"><span class="cl"><span class="c1"># - Light text: ~20% lightness (&gt;7:1 contrast ratio on light bg)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># - Dark backgrounds: ~10-12% lightness</span>
</span></span><span class="line"><span class="cl"><span class="c1"># - Dark text: ~80-85% lightness (&gt;6:1 contrast ratio on dark bg)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># - Borders: ~45-55% lightness</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>Seis regras. Oito matizes. Dois temas. 100% determinístico. Nenhuma decisão subjetiva de &ldquo;acho que esse azul fica bonito&rdquo; — é sistema de design derivado de propriedades geométricas do espaço de cor HSL.</p>
<h2>HTML Bem Formado: O Filtro de Spam que Ninguém Te Conta<span class="hx:absolute hx:-mt-20" id="html-bem-formado-o-filtro-de-spam-que-ninguém-te-conta"></span>
    <a href="#html-bem-formado-o-filtro-de-spam-que-ningu%c3%a9m-te-conta" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Tudo que descrevi acima — tables, CSS inline, font stacks — resolve o problema de <strong>renderização</strong>. Mas tem um segundo motivo pra seguir essas regras que a maioria dos desenvolvedores ignora: <strong>provedores de email analisam a qualidade do seu HTML pra decidir se é spam</strong>.</p>
<p>Gmail, Outlook, Yahoo — todos passam o HTML do seu email por heurísticas antes de mostrar pro usuário. HTML malformado é um dos sinais clássicos de spam. Faz sentido: spammers não testam templates. Eles cospem HTML quebrado gerado por ferramentas de mass-mailing baratas. Se seu email parece output de spammer, vai ser tratado como spam.</p>
<p>Os sinais que disparam filtros:</p>
<ol>
<li><strong>Tags não fechadas ou mal aninhadas</strong>. Um <code>&lt;div&gt;</code> que abre e nunca fecha, ou um <code>&lt;/td&gt;</code> antes do <code>&lt;/tr&gt;</code>, é flag imediata. Provedores esperam HTML que parse sem erros — não HTML &ldquo;que o browser conserta&rdquo;.</li>
<li><strong>Ratio imagem/texto desbalanceado</strong>. Email que é basicamente uma imagem gigante com pouco texto? Spam clássico. Provedores querem ver texto real — não texto dentro de imagens. A regra de ouro é manter pelo menos 60% texto, 40% imagem.</li>
<li><strong>Atributo <code>alt</code> ausente em imagens</strong>. Além de acessibilidade, <code>alt</code> vazio ou ausente é sinal de descuido — e descuido correlaciona com spam. Toda <code>&lt;img&gt;</code> precisa de <code>alt</code> descritivo.</li>
<li><strong>CSS que esconde conteúdo</strong>. <code>display: none</code>, <code>visibility: hidden</code>, <code>font-size: 0</code>, texto da mesma cor que o background — são técnicas clássicas de keyword stuffing. Mesmo que você use legitimamente pra responsive email, o filtro não sabe a diferença.</li>
<li><strong>HTML excessivamente pesado</strong>. Email com 500KB de HTML levanta suspeita. Mantenha leve — o conteúdo real raramente precisa de mais de 100KB de HTML.</li>
<li><strong>Links com texto que não corresponde ao href</strong>. <code>&lt;a href=&quot;https://site-real.com&quot;&gt;https://site-falso.com&lt;/a&gt;</code> é phishing textbook. Mesmo discrepâncias sutis (domínio no texto diferente do domínio no link) são penalizadas.</li>
</ol>
<p>O ponto que conecta tudo: <strong>as mesmas práticas que fazem email renderizar corretamente no Outlook são as que mantêm seus emails fora do spam</strong>. Tables com estrutura limpa? HTML bem formado. CSS inline em cada elemento ao invés de hacks com <code>&lt;style&gt;</code>? Sem CSS escondido. Imagens com dimensões explícitas e <code>alt</code> text? Conteúdo legítimo.</p>
<p>Não é coincidência. Provedores de email calibraram seus filtros observando o que spammers fazem — e spammers fazem exatamente o oposto de HTML email bem construído. Quando você segue as melhores práticas de compatibilidade, <strong>de graça</strong> você também está sinalizando legitimidade pros filtros de spam.</p>
<p>A lição prática: trate o HTML do seu email como se fosse um documento XML. Valide, teste, e garanta que todo tag abre e fecha na ordem certa. Use ferramentas como <a href="https://www.emailonacid.com/"target="_blank" rel="noopener">Email on Acid</a> ou <a href="https://www.litmus.com/"target="_blank" rel="noopener">Litmus</a> pra testar em múltiplos clients — eles também reportam problemas de spam scoring. E resista à tentação de copiar templates prontos da internet sem validar — muitos são um desastre de HTML malformado que passa despercebido no Gmail mas faz o Outlook ter um ataque cardíaco e faz o filtro de spam do Yahoo te marcar como suspeito.</p>
<h2>RSS Feeds: O Frontend Esquecido<span class="hx:absolute hx:-mt-20" id="rss-feeds-o-frontend-esquecido"></span>
    <a href="#rss-feeds-o-frontend-esquecido" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-20_01-12-48.jpg" alt="RSS"  loading="lazy" /></p>
<p>Um detalhe que desenvolvedores web ignoram: RSS é um frontend. Podcasts no Spotify e Apple Podcasts são consumidos via RSS. O feed precisa de:</p>
<ul>
<li>XML válido (parsers de podcast são rígidos)</li>
<li>Metadata completa (título, descrição, duração, tamanho do arquivo)</li>
<li>URLs de imagem que <strong>realmente funcionam</strong> (Spotify valida)</li>
<li>Cover art com dimensões mínimas (1400×1400 pra Spotify)</li>
<li>GUIDs únicos por episódio</li>
</ul>
<p>Hugo gera RSS automaticamente, mas o template padrão não tem os campos que plataformas de podcast exigem. Um template customizado com tags iTunes é necessário:</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="nt">&lt;itunes:duration&gt;</span>{{ .Params.duration_seconds }}<span class="nt">&lt;/itunes:duration&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;itunes:image</span> <span class="na">href=</span><span class="s">&#34;{{ .Params.cover_image | absURL }}&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;enclosure</span> <span class="na">url=</span><span class="s">&#34;{{ .Params.audio_url }}&#34;</span>
</span></span><span class="line"><span class="cl">           <span class="na">length=</span><span class="s">&#34;{{ .Params.file_size }}&#34;</span>
</span></span><span class="line"><span class="cl">           <span class="na">type=</span><span class="s">&#34;audio/mpeg&#34;</span><span class="nt">/&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>
<h2>A Filosofia: Conteúdo Primeiro, Framework Nunca<span class="hx:absolute hx:-mt-20" id="a-filosofia-conteúdo-primeiro-framework-nunca"></span>
    <a href="#a-filosofia-conte%c3%bado-primeiro-framework-nunca" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O padrão que emerge de todo esse setup é: <strong>o conteúdo é soberano</strong>. Markdown com frontmatter YAML é o formato universal. Cada frontend (blog, email, RSS, podcast player) consome o mesmo conteúdo e renderiza do seu jeito.</p>
<p>Não existe &ldquo;componente React que busca dados de uma API&rdquo;. Existe um arquivo Markdown que é lido por quem precisar. Isso é radicalmente simples — e radicalmente poderoso. Trocar o blog engine de Hugo pra outro? Muda os templates, o conteúdo fica igual. Trocar o provedor de email de SES pra Sendgrid? Muda o mailer, os templates ficam iguais.</p>
<p>É o Unix philosophy aplicado a frontend: cada ferramenta faz uma coisa bem feita, conectadas por um formato universal (Markdown + YAML).</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>Você não precisa de React pra ter frontend moderno. Tailwind v4 te dá design system com constraints, dark mode nativo, e zero JavaScript. Hugo te dá um blog absurdamente rápido com templates flexíveis. ERB com CSS inline te dá emails que funcionam em qualquer client.</p>
<p>O investimento real é entender as restrições de cada plataforma e construir pipelines de renderização pra cada uma. Não é aprender o framework da moda que vai morrer em 18 meses.</p>
<p>No final das contas, a melhor abstração de frontend é nenhuma. HTML, CSS, e Markdown. Simples, debugável, e vai funcionar daqui a 10 anos.</p>
]]></content:encoded><category>the-m-akita-chronicles</category><category>frontend</category><category>desenvolvimento-web</category></item><item><title>[Akitando] #39 - A História do Front-End para Iniciantes em Programação | Série "Começando aos 40"</title><link>https://akitaonrails.github.io/2019/02/13/akitando-39-a-historia-do-front-end-para-iniciantes-em-programacao-serie-comecando-aos-40/</link><guid isPermaLink="true">https://akitaonrails.github.io/2019/02/13/akitando-39-a-historia-do-front-end-para-iniciantes-em-programacao-serie-comecando-aos-40/</guid><pubDate>Wed, 13 Feb 2019 17:00:00 -0200</pubDate><description>&lt;p&gt;Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal &lt;a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener"&gt;Akitando&lt;/a&gt;. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.&lt;/p&gt;
&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/VKmPGmFY7H4"
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;h3&gt;Descrição no YouTube&lt;span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"&gt;&lt;/span&gt;
&lt;a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Este episódio vai testar a paciência de vocês. Desta vez quero explorar um pouco os diversos assuntos que são associados com a tal carreira de &amp;ldquo;Front-End&amp;rdquo; no mundo de desenvolvimento Web.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal <a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener">Akitando</a>. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/VKmPGmFY7H4"
    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>

<h3>Descrição no YouTube<span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"></span>
    <a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Este episódio vai testar a paciência de vocês. Desta vez quero explorar um pouco os diversos assuntos que são associados com a tal carreira de &ldquo;Front-End&rdquo; no mundo de desenvolvimento Web.</p>
<p>Se já começaram a estudar o assunto devem ter visto que existe uma infinidade de sopas de letrinhas, coisas como SASS, CSS, React, Vue, e muito mais. Por que existem tantas letrinhas assim? Quais escolher?</p>
<p>Obs: eu fico falando &ldquo;Webpacker&rdquo; o tempo todo mas na verdade é &ldquo;Webpack&rdquo; sorry :-)</p>
<p>Links:</p>
<ul>
<li>
<p>History of Web Fonts: (<a href="https://thehistoryoftheweb.com/web-fonts/"target="_blank" rel="noopener">https://thehistoryoftheweb.com/web-fonts/</a>)</p>
</li>
<li>
<p>A brief history of CSS until 2016 (<a href="https://www.w3.org/Style/CSS20/history.html"target="_blank" rel="noopener">https://www.w3.org/Style/CSS20/history.html</a>)</p>
</li>
<li>
<p>Responsive Web Design: Media Queries (<a href="http://blog.chrismaxwell.com/responsive-web-design-media-queries"target="_blank" rel="noopener">http://blog.chrismaxwell.com/responsive-web-design-media-queries</a>)</p>
</li>
<li>
<p>Front-End Developer Handbook 2018 (<a href="https://frontendmasters.com/books/front-end-handbook/2018/"target="_blank" rel="noopener">https://frontendmasters.com/books/front-end-handbook/2018/</a>)</p>
</li>
<li>
<p>Eloquent JavaScript (<a href="https://eloquentjavascript.net/"target="_blank" rel="noopener">https://eloquentjavascript.net/</a>)</p>
</li>
<li>
<p>Mastering Regular Expressions (<a href="http://shop.oreilly.com/product/9780596528126.do"target="_blank" rel="noopener">http://shop.oreilly.com/product/9780596528126.do</a>)</p>
</li>
<li>
<p>Juliana Negreiros (<a href="https://github.com/juunegreiros/utilities"target="_blank" rel="noopener">https://github.com/juunegreiros/utilities</a>) - página cheia de boas referências.</p>
</li>
</ul>
<h2>Script<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Estamos no quarto episódio desta série, semana passada começamos com algumas coisas básicas do Developer Roadmap onde eu discuti porque saber Git e aprender Linux de verdade são as duas coisas mais importantes pra começar. Mas eu me lembrei no caso de Linux que eu disse que tinha duas formas, você aprender do jeito difícil que é instalar e configurar uma distro como Arch Linux num ambiente virtualizado como Virtualbox. Ou aprender do jeito ultra difícil, que eu esqueci de mencionar no final do episódio, e que é instalando direto como sistema operacional principal da sua máquina! Sim, vai ser bem difícil se for sua única máquina e você só tiver acesso ao site do Arch Wiki pelo celular. Mas vai por mim, hoje tá bem fácil porque a gente fazia essas coisas nos anos 90 só confiando que tinha tudo certo nos disquetes e sem internet pra baixar alguma coisa caso tivesse esquecido, e Wikis sequer haviam sido inventados, aliás, Google não tinha sido inventado. Então considere que hoje tá muito fácil e instale Arch Linux como seu sistema operacional principal. Se conseguir fazer isso sozinho, você está no caminho certo.</p>
<p>No episódio de HOJE vamos falar sobre um dos assuntos mais desnecessariamente complicados da carreira de programador Web, o guarda-chuva que chamamos de Front-end. Porém, em vez de seguir o Roadmap, neste episódio vou fazer uma longa tangente hoje. Aliás, vai ser um dos meus episódios mais longos, então não deixem de dizer nos comentários se preferiam que eu tivesse quebrado esse episódio em duas partes ou se nesses casos está ok ser longo mesmo.</p>
<p>Se você está entrando hoje no mercado de front-ends vai esbarrar em dezenas de letrinhas, coisas como Sass, Vue, React, Angular, Ember, Bootstrap, Npm, Yarn, Webpacker, Flow, TypeScript, Dart, BEM, SMACSS, PWAs, SPAs, RAIL, e por aí vai. Eu entendo porque alguém que está iniciando os estudos agora vai ficar extremamente confuso sobre por onde começar. E como sempre o melhor jeito de entender como começar é do começo.</p>
<p>(&hellip;)</p>
<p>A tangente que eu me referi é que eu vou fazer o contrário da maioria dos tutoriais ou guias que falam de front-end. Em vez de dizer logo de cara o que eu acho que você devia estudar, eu pessoalmente acho mais fácil explicar porque aquelas sopas de letrinhas existem e daí vocês mesmos podem começar a chegar em algumas conclusões. A história do front-end é a própria história da Web e algumas coisas que você tem hoje só fazem sentido no contexto dos anos 90 ou dos anos 2000, sem saber disso você meio que tem que engolir como as coisas são sem entender porque. No fim do episódio eu espero que vocês tenham muito mais perguntas do que neste momento.</p>
<p>A estrutura mais simples que você precisa ter na cabeça é a seguinte: quando você abre um navegador e digita um endereço tipo https : // meusite.com.br ele vai fazer dezenas de operações mas no mínimo ele precisa primeiro traduzir o domínio num endereço IP, e quem faz esse trabalho inicial é o tal servidor de DNS ou Domain Name Server que você já deve ter ouvido pelo menos o nome, pra onde a configuração de rede do seu computador está apontando. DNS é como se fosse uma lista telefônica, traduzindo nomes pra números.</p>
<p>Agora é tarefa da tal rede, roteadores, provedores, conectar seu navegador a um servidor. A internet funciona porque todas as pontas falam o mesmo formato de mensagens e trafegam de uma determinada maneira, que é o que chamamos de protocolo, e o padrão que nos interessa é o famoso TCP/IP. Por agora só entenda que com o endereço ele encontra o servidor certo no mundo. Bem em resumo estamos falando de um programa ou aplicação que é um cliente de TCP ligando a um servidor TCP, que fica escutando no que chamamos de uma porta. Todo servidor web que serve páginas HTML é um programa que quando você carrega ele se liga a uma porta, que é representado por um número que vai de 1 a mais de 65 mil.</p>
<p>A convenção é que um servidor Web ouve conexões na porta 80. Um servidor SSH ouve na porta 22. Um servidor Web também escuta na na porta 443 quando recebe requisição de conexão segura via HTTPS, e muitos servidores de desenvolvimento como Node.js, Rails ou Java ouvem em portas como 3000 ou 5000 ou 8080, respectivamente. Não há nada de mágico nesses números, são arbitrários e foram escolhidos anos atrás e por isso é só uma convenção, mas na prática, qualquer número de porta serve, contanto que exista alguma coisa “escutando” nessa porta.</p>
<p>Nesse minuto de explicação eu simplifiquei bastante. Na faculdade você faria exercícios simples de escrever um programinha que escuta numa porta e outro que conecta nessa porta. É assim que começa a nascer a semente de um servidor web e de um navegador. Agora o que é o servidor Web mais simples de todos? Ele simplesmente lê um arquivo da máquina e serve esse arquivo. O endereço completo que você digita no navegador se chama URL ou Universal Resource Locator que é composto pelo protocolo, pelo domínio e pelo caminho de um arquivo, no caso mais simples. Então antigamente você veria endereços como esse <a href="http://sitedafaculdade.edu/~jose/arquivo.html"target="_blank" rel="noopener">http://sitedafaculdade.edu/~jose/arquivo.html</a></p>
<p>E esse til jose, se você estudou Linux como eu disse no episódio anterior, vai saber que é um atalho pro diretório /home/jose. E é assim que você faz o servidor web mais simples de todos e, como hoje em dia sabemos, o mais bugado em termos de segurança porque se ele traduz direto da URL pra um caminho de arquivos no seu sistema operacional, sem nenhuma checagem, você poderia ficar tentando achar maneiras de vasculhar todos os arquivos do usuário, mesmo os que ele achou que não seriam servidos publicamente. E fazíamos isso mesmo antigamente pra achar brechas.</p>
<p>E quando o navegador, que é um exemplo de um programa que é cliente de TCP, recebe esse arquivo.html agora ele traduz as marcações pra elementos visuais. E como existe o elemento que chamamos de LINK que é o <a href=”http://….> onde “A” significa anchor ou âncora e href é hypertext reference, nasce o conceito de hipertexto onde um texto possui links pra outros textos. No começo ninguém estava interessado em visual, simplesmente em disponibilizar textos linkando com outros textos. E assim nasce a tal World Wide Web ou WWW. É um conceito que nasceria naturalmente dados esses elementos que eu mencionei até agora, era inevitável.</p>
<p>No próximo passo alguém pensou, e se em vez de digitar o nome de um arquivo eu digitar o nome de um programa executável? Um binário que é um programa, que no caso do Windows são aqueles arquivos que terminam o nome com .exe, tipo um Word.exe ou Excel.exe em vez de um hipertexto cujo nome termina com a extensão .html?</p>
<p>Num navegador bem antigo não ia acontecer nada. Provavelmente poderia dar erro porque o servidor não saberia interpretar algo que não é texto. Mas aí alguém fez um dos primeiros servidores web reconhecer quando alguém pede um executável e em vez de só ler o arquivo e cuspir o conteúdo pro navegador, ele primeiro tenta rodar esse executável no servidor e cospe pro navegador o resultado dessa execução, que esperamos que seja um texto em HTML.</p>
<p>Ou seja, pro navegador não faz diferença se pedimos um executável ou um arquivo texto ao servidor, contanto que o que volte pra ele como resposta seja um conteúdo texto em HTML. E se considerarmos que pedir arquivos texto, que nunca mudam é algo estático, assim nasce a primeira geração de sites dinâmicos, ou seja, que dinamicamente podem mudar o resultado dependendo do que o executável faz. Isso começa com programas binários feitos em C mesmo, e o padrão foi nomeado como CGI ou Common Gateway Interface. Mas fazer C ficar cuspindo HTML é como usar um canhão pra matar uma mosca, mas felizmente havia outra linguagem mais simples em ambientes UNIX e Linux e melhor adaptada pra manipular cadeias de texto, ou strings, e foi assim que Perl se tornou a principal linguagem para fazer CGIs e foi assim que o mundo começou a conhecer mais sobre essa sub-linguagem que o Perl trazia chamada Regular Expressions ou REGEX pra manipular strings. Se você ainda não sabe o que é REGEX coloque na sua lista de coisas obrigatórias pra estudar.</p>
<p>E depois do surgimento de alguns servidores web como o da NCSA, ou da Netscape, dentre outros, um começou a despontar pela sua facilidade de uso e por ser open source, ou seja de código aberto, o famoso Apache, cujo nome eu sempre ouvi a história de que foi inspirado em “A Patch”, que traduz em português como “remendo” já que ele não deixa de ser um puxadinho de remendos de código que foram sendo adicionados, um patch de cada vez. Pra quem não sabe, você pode comparar duas versões do mesmo arquivo de código, um mais antigo e outro mais novo usando uma ferramenta de linha de comando que vem na maioria das distribuições Linux chamada “diff” pra tirar a “diferença” entre eles e colocar num arquivo de remendo ou “patch”, e usar outra ferramenta, a linha de comando “patch” pra aplicar essa diferença.</p>
<p>E falando em remendos de arquivos, uma das coisas mais irritantes da Web no começo dos anos 90 era que se eu quisesse fazer um site com 10 páginas e algum layout ou estilo visual, eu precisava copiar e colar o HTML inteiro de uma página pra outra, dez vezes e daí editar o conteúdo diferente entre cada uma. Se depois eu quisesse mudar o layout pra outro formato, eu precisava abrir as 10 páginas e mudar uma a uma. Originalmente a idéia não era que o HTML ditasse como o conteúdo seria visualmente estilizado, a idéia era que cada navegador estilizasse as páginas do jeito que quisesse. O motivo disso foi porque HTML foi inspirado em outras linguagens de marcação de conteúdo como SGML, voltados estruturar documentos governamentais que pudessem ser lidos por um computador. A função do HTML era marcar o que era um cabeçalho, um parágrafo, uma lista, e os perfis de impressão decidiam fonte, espaçamentos e tudo mais pra comportar esse conteúdo numa página de papel, e os navegadores é decidiriam como apresentar esse conteúdo num monitor, inclusive o usuário do navegador poderia customizar escolhendo que não queria o padrão de fontes Times New Roman e sim Arial e todas as páginas mostrariam Arial. Por isso se você viu exemplos de páginas do começo da web, ela eram todas bem sem sal, com fundo cinza, fontes pretas e links azuis, tudo meio igual.</p>
<p>Você já podia embutir um jeito rudimentar de forçar o controle de estilo direto no HTML com o atributo chamado style, mas foi com CSS que separamos o estilo do conteúdo e ganhamos mais controle sobre como o navegador pinta o visual na tela, coisas como escolher fontes, tamanhos, espaçamentos, alinhamentos, e muito mais. Isso foi no começo dos anos 90, até lá por 95 ou 96 quando CSS apareceu pela primeira vez. Nessa época você já tinha Netscape superando o antigo Mosaic, mas o Internet Explorer sairia na frente porque implementava melhor o primeiro CSS, ao contrário da Netscape. Foi quando surgiram páginas de teste como o famoso ACID pra checar a compatibilidade de CSS entre navegadores. Tem uma página com a história completa da novela em cima do CSS que estou deixando nos links na descrição abaixo.</p>
<p>Essa briga deu início à Guerra dos Navegadores, as ferozes batalhas entre a Microsoft e a Netscape pela hegemonia da Web com cada versão de um navegador tentando ser melhor, principalmente em termos visuais e de interatividade do que o anterior. Recomendo muito que você procure pelo documentário da Discovery, The True Story of the Internet onde o primeiro episódio narra em mais detalhes como foi essa guerra. Eu falei sobre isso num dos primeiros episódios desse canal quando dei dicas de séries e filmes pra programadores.</p>
<p>No fim dos anos 90, a Netscape estava perdendo a guerra e resolveu mudar seu modelo de negócio que envolvia vender o navegador, e abriu seu código fonte, criando a fundação Mozilla. O Opera apareceu também mostrando como redimensionar conteúdo pros dispositivos móveis da época como os Palms, Pocket PC e os primeiros dumb-fones de marcas famosas na época como Nokia ou Siemens, a primeira geração de pseudo-“smartphones” vamos assim dizer, isso antes ainda do surgimento da Blackberry e foi quando vimos o primeiro uso de media types, que evoluiriam pra media queries e viriam a se tornar oficiais só no CSS3 em 2012, mas os primeiros rascunhos datam desde pelo menos 1997. Se você não sabe o que são media types, lembre-se que layout de conteúdo era mais especializado para impressão em papel, pra editorar revistas, jornais ou livros. E tínhamos agora que adaptar para monitores de computador, mas além disso ainda havia outras formas de mostrar conteúdo. Imagine dispositivos tácteis como braille para cegos ou que geram som para surdos. Cada um deles é um “tipo de mídia” ou media types. E o Opera começou a popularizar o uso do atributo @media que evoluiria para as media queries que usamos hoje onde o CSS pergunta ao navegador que tipo de mídia estamos lidando, se é uma tela de celular, tela de tablet ou tela de computador.</p>
<p>Com o boom do Internet Explorer foi quando a Microsoft começou a embutir o máximo possível da Web no Windows, e com o Windows 98 vimos pela primeira vez a integração dessas tecnologias direto no sistema operacional, como a utilização de MIME-types, uma tecnologia que surgiu primeiro em e-mails para possibilitar embutir arquivos binários em e-mails que eram somente texto puro. MIME que significa Multipurpose Internet Email Extensions e que você conhece como pares que determinam o tipo de arquivo como text/html ou image/png ou video/mpeg. É uma forma mais avançada do que no Windows são as extensões de arquivo como .html ou .png ou .mpg.</p>
<p>O File Explorer foi mesclado ao Internet Explorer que possibilitava explorar os arquivos da máquina ou explorar os arquivos na Web. Hoje em dia todos os navegadores de arquivos usam MIME types. E isso foi um dos estopins para caracterizar prática monopolista da Microsoft em forçar as pessoas a usar Internet Explorer. A comissão européia na época forçou a Microsoft a deixar os fabricantes de computador escolher se deixariam pré-instalado o Netscape ou Opera. Mas o argumento da Microsoft era que era impossível separar o Internet Explorer do Windows porque ele era parte fundamental de como você navegava no sistema. Foi uma controvérsia enorme na época do Windows 98.</p>
<p>Voltando ao assunto dos servidores web, quando servidores como Apache se tornaram capazes de coisas como executar um script que manipulava o HTML antes de mandar para os navegadores eu poderia escrever o HTML do layout como se fosse um template, um modelo, e fazer só o conteúdo diferente daquelas 10 páginas separadas, e se precisasse mudar o layout, bastava mudar nesse template e todas as 10 automaticamente seriam modificadas.</p>
<p>Com esse conceito começamos a evoluir o mundo de escrever páginas de texto com hiperlink pra algo mais dinâmico e visualmente mais agradável do que textos em fontes Times New Roman ou Courier New com fundo cinza. A diferenciação passou a ser a customização gráfica e de design dos sites e começava a nascer a profissão de web designers que começou com designers gráficos de revista tentando entender como traduzir a linguagem visual de revistas e livros no formato digital da Web. A gente via muito design muito bizarro nessa época, eu diria que foi a época da esplendor exuberante barroco do design Web.</p>
<p>E não é só isso, dado que linguagens no servidor, seja em C ou Perl poderiam ter acesso a recursos como bancos de dados, de repente uma nova possibilidade se abre: aplicações Web em vez de só sites Web. E se eu pudesse fazer um formulário onde o usuário pudesse digitar seu cartão de crédito e enviar para esse script Perl no servidor e gravar no banco de dados? É como se inicia a semente para coisas como e-commerces. A Microsoft novamente não deixou passar a oportunidade e criou seu próprio servidor web, o IIS ou Internet Information Services que adicionou tecnologias proprietárias como o Frontpage Server Extensions que, junto com o programa Frontpage, permitir criar páginas dinâmicas, com validações complicadas de formulário, de maneira simples. Porém páginas feitas com essas extensões só podiam rodar em IIS e serem interpretados por Internet Explorer, aumentando ainda mais a pressão em cima de Apache ou Netscape. Você encontra resquícios disso em produtos atuais da Microsoft como o servidor Sharepoint.</p>
<p>Em paralelo ao Perl e os CGIs, nasceu outra forma de criar páginas dinâmicas com extensões embutidas diretamente dentro do núcleo do Apache, e seu principal proponente foi o PHP. Agora, em vez do servidor web chamar um programa externo pra gerar os HTMLs, o próprio servidor web ganhou a capacidade de interpretar lógica de programação pra cuspir HTML. A Microsoft seguiu lado a lado adicionando em 1996 a extensão de ASP no IIS. E então nasceu a guerra server-side entre PHP e ASP pela hegemonia do mundo servidor. Na sequência ainda ganhamos aberrações como Coldfusion, representando o pico do mau uso do então inovador XML não só como forma de representar dados em formato de tags, mas usar tags como linguagem de programação, inspirado pelo famigerado JSP no mundo J2EE.</p>
<p>Isso coincidiu com a evolução do Java no lado do servidor, e outra categoria de servidores Web, os application servers ou servidores de aplicação. Entenda que um servidor de aplicação é um servidor web, mas nem todo servidor Web como Apache ou NGINX de hoje é um servidor de aplicação. Servidores Web tem como principal função servir páginas HTML e servidores de aplicação tem como principal função executar programas que cospem HTML. Essa época também coincidiu com a abertura inicial de capital ou IPOs da Netscape e Yahoo que marcam o início da famosa Bolha da Internet do meio pro fim dos anos 90. Com a entrada de programadores mais experientes do que meros designers fazendo HTML, tecnologias que permitiam o tráfego de informações seguras como o SSL da Netscape, nasceram as primeiras aplicações Web como content management systems ou CMS como o histórico FileNet, o monstruoso Vignette que todo portal de conteúdo usava, versões open source como PHP Nuke, Mambo que viriam a inspirar a próxima geração como Drupal, Joomla, ou Zope em Python, que daria origem ao Plone, e finalmente o nascimento do Wordpress no começo dos anos 2000 que ajudaria a iniciar a era dos blogs.</p>
<p>Uma vez que começou a ficar claro que era possível programar aplicações interativas, controlar o visual se tornou mais e mais importante. A evolução do CSS1 pro CSS2 que inaugurou também a época das Web Fonts, e moveu os layouts ou estruturas de páginas de usar tabelas e células para a era Tableless e design fluido também viu o nascimento de outras 2 tecnologias fundamentais para a Web de hoje: Dynamic HTML ou DHTML com o HTML versão 4 e uma tecnologia inaugurada pela Mozilla na forma de Asynchronous Javascript ou AJAX. E seu principal garoto propaganda, o GMAIL que começou a ser desenvolvido em 2001 e foi lançado só no dia primeiro de abril de 2004, também inaugurando a era dos memes do dia da mentira.</p>
<p>O GMAIL foi a personificação de tudo que Bill Gates temia nos anos 90: que os navegadores Web se tornassem substitutos dos sistemas operacionais, no caso do Windows, na função de executar programas. Ele previu corretamente que sem padrões proprietários você não teria como segurar os usuários no Windows, porque eles poderiam rodar o mesmo programa num navegador como Netscape em qualquer sistema como MacOS ou distribuições Linux. O GMAIL foi provavelmente a primeira aplicação Web que substitui com sucesso um dos programas nativos mais importantes num sistema operacional: o cliente de e-mails como o Outlook da Microsoft. E isso ameaçava tudo, incluindo o monopólio sobre programas como o Office.</p>
<p>Neste ponto da história, estamos entrando em 2004. Em termos de tecnologias de front-end avançamos o HTML até a versão 4 que comporta coisas como Dynamic HTML e AJAX. Derivado do SGML tivemos o HTML mas também surgiria o XML para estruturar qualquer tipo de dados e como uma estrutura mais geral onde HTML, ou mais especificamente, XHTML seria um caso especial. HTML é especializado em estruturar conteúdo de texto. XML é especializado em estruturar dados, como cadastros. CSS versão 2.1 é o cascading style sheet, que passou a suportar web fonts, internacionalização, posicionamento relativo e absoluto que permite que os elementos na tela de ajustem dinamicamente de acordo com o tamanho total da tela, ao contrário do que fazíamos no meio dos anos 90 onde considerávamos um comprimento fixo de tela pra encaixar os elementos, especialmente dentro de tabelas e usando muito recorte de imagens porque CSS não era poderoso o suficiente para alguns designs gráficos. Além disso, é importante entender que CSS é uma linguagem declarativa, onde declaramos regras visuais que são aplicadas no HTML e essas regras são cascateadas onde uma regra herda as características da regra pai, numa hierarquia de regras. Aliás, tenham sempre em mente a estrutura de uma árvore ou hierarquia. Quase tudo na Web é baseado nessa estrutura. HTML são árvores de elementos. CSS são árvores de atributos de estilo. Sites são árvores de páginas uma linkando na outra. E agora que evoluímos de simples sites de conteúdo hipertexto, pra gerenciadores de conteúdo CMS, pra e-commerces e com o início da era de aplicativos Web como Gmail e redes sociais como Orkut, MySpace e Friendster, abriu-se uma nova fase de desenvolvimento.</p>
<p>Em paralelo, na era Java do começo dos anos 2000 popularizou-se o conceito de Frameworks, que mais do que linguagens fornecem uma estrutura pré-definida para ajudar a desenvolver aplicativos, fornecendo coisas como segurança, validação de dados, autenticação e autorização granular, templates visuais, e que determinavam meio que um “guia” de lugares onde você deveria colocar cada tipo diferente de código. Antes disso cada aplicativo como Joomla ou Drupal fazia tudo do zero. No mundo PHP começaram a surgir frameworks inspirados em J2EE ou Struts, como o Zend Framework. E finalmente abrimos a era do Ruby on Rails pro fim de 2004 e o agora lendário demo do blog em 15 minutos que David Hansson gravou no evento FISL de 2005 no Brasil, inaurando a era dos frameworks que herdam os conceitos de Agilidade inaugurado pelo Manifesto Ágil de 2001.</p>
<p>Por causa de Ruby on Rails começou a surgir a primeira geração de sub-frameworks Javascript e CSS. Até então escrever HTML ou CSS ou mesmo Javascript era algo trabalhoso, que não funcionava exatamente da mesma forma em todos os navegadores. Lembrando que estamos na época do famigerado Internet Explorer 6 do também famigerado Windows XP e do Netscape 7. Com isso em mente, alguém pensou: porque não unificar essas coisas? Por que não fazer um framework via Javascript pra homogeneizar e evitar que o programador tenha que fazer código específico para Internet Explorer ou Netscape? Daí surge a primeira geração de micro-frameworks especializados em visual do lado do navegador como Mootools, Dojo, o próprio JQuery, e os que o Ruby on Rails tornou popular na época, no caso o Prototype e o Scriptaculous. Scriptaculous que popularizou novas formas de elementos de interação como drag and drop de elementos no HTML, onde podíamos arrastar as coisas como podemos arrastar e-mails no inbox do GMAIL.</p>
<p>Pouco tempo depois, em 2007 surge uma nova revolução na área de design visual e usabilidade. Vemos o nascimento dos smartphones a partir do primeiro iPhone lançado em julho de 2007. Com ele temos o navegador Safari, que é uma evolução do navegador chamado Konqueror do KDE no Linux, que usava o núcleo KHTML que viria a se tornar o que conhecemos hoje como WebKit. Só que telas de celular tinham resolução muito menor do que um desktop. Em 2007 o mais comum eram monitores com resolução de 1024 pixels por 768, mas o iPhone tinha meros 320 por 480 pixels.</p>
<p>Uma coisa que se tornou popular nos anos 90 e que eu não falei ainda, foi uma forma de simplesmente não usar nada de HTML nem CSS nem Javascript. Pra tentar tomar a dianteira, empresas como a Microsoft não fizeram só componentes proprietários pro servidor como o Frontpage Server Extensions ou ASP. A Sun criou o Java e queria dar um jeito de colocar Java em tudo. O Java servia pra muitas coisas incluindo fazer aplicativos pra Desktop, como os malditos aplicativos de Imposto de Renda que você usa até hoje. Mas eles queriam dar um jeito de colocar Java nos navegadores, mas sendo uma linguagem geral ela virar um problema de segurança se de repente você rodasse Java no navegador e ele pudesse acessar seus arquivos. Por isso criou-se o conceito de sandboxing, um container que permite executar código Java com baixos privilégios ou seja, sem privilégios pra acessar arquivos ou a própria rede, ele devia rodar dentro desse container e assim nascem os aplets.</p>
<p>Quem dessa época não lembra do famigerado applet de demonstração que mostrava água se mexendo num tipo de animação que era impossível de fazer só com CSS ou mesmo com GIF animado. E seguindo nessa tendência a Microsoft criou o padrão ActiveX que era o mesmo que applets mas em vez de usar Java você programava em C++ ou mesmo Visual Basic 6. Felizmente Applets Java e ActiveX já não existem mais, mas a Macromedia também fez seu sandbox na forma do famigerado plugin Flash que trazia tecnologias realmente legais pra época como ilustrações vetoriais com curvas de Bezier que já era usado em programas como CorelDraw ou Adobe Illustrator para conseguir embutir ilustrações de melhor qualidade e tamanho muito menor do que imagens bitmap como JPEG ou GIF. E mais do que isso, adicionar o conceito de linha do tempo para animar essas ilustrações. Adicionando uma linguagem bem tosca e simples chamada ActionScript e você podia fazer coisas interativas como joguinhos. Todo mundo jogou alguma coisa em Flash no começo dos anos 2000.</p>
<p>Em 2019 o processador de um iPhone, o famoso A12 Bionic nos novos iPhone XS é quase tão potente quanto um processador Intel Core em um Macbook Pro. Um processador em um iPhone original de 2007 era um processador ARM11 de meros 412Mhz e tinha só meros 128 MEGAbytes de RAM. E a bateria tinha não mais que 1400 miliamperes, hoje celulares tem até 4000 miliamperes. Entre pouco processamento e pouca bateria, sem teclado, sem mouse, só com touch, rodar Flash era um exercício de futilidade. Os primeiros Androids queriam se destacar deixando rodar Flash mas rapidamente foi provado que isso só deixava celulares Android extremamente lentos e a bateria acabava num piscar de olhos. Flash já não tinha boa reputação por causa do tanto de bugs e principalmente buracos graves de segurança em sua programação porca. A gente dava um jeito de aguentar com CPUs mais potentes e muita RAM em desktops, mas em smartphones não tinha pra onde correr. A Apple se recusou a rodar Flash e não só isso demonizou o Flash publicamente na famosa carta aberta de Steve Jobs que selou o destino do fim do Flash.</p>
<p>Só que a partir de 2005 a única forma decente de ver videos do YouTube ou outros sites era via o plugin de Flash ou via o plugin de Silverlight que era tipo uma evolução do ActiveX da Microsoft, mas também não durou muito. Sem esses plugins não havia video. A Apple ajudou a evoluir o HTML para suportar playback de vídeo acelerado por hardware, e popularizou o envelope MP4 de vídeo com compressão H.264 e com isso nascia novas tags de vídeo pra HTML. Somado ao fim do Flash também aumentou a pressão por adicionar funcionalidades de animação também acelerado pela nova classe de GPUs e assim surgem coisas como o atual canvas controlado por CSS e WebGL.</p>
<p>A revolução dos smartphones, a queda da Sun que foi adquirida pela Oracle pondo também um ponto final em applets, as ingerências da Microsoft pós julgamento anti-truste nos anos 2000 desacelerando os desenvolvimentos do Internet Explorer e tecnologias como Silverlight. Tudo isso forçou a W3C e seu consórcio de empresas a acelerar uma coisa que já estava muitos anos atrasado: a evolução pro HTML5 e CSS3 que só aconteceu em 2014 onde alguns anos antes disso todos os navegadores, começando com o surgimento do Google Chrome em 2008 começaram a implementar uma funcionalidade de cada vez.</p>
<p>Ficamos de 2008 a 2015 num período muito chato de transição. Tínhamos que desenvolver Web em meio a HTML, CSS, Javascript estando no meio do caminho e mudando o tempo todo em cada nova versão de cada navegador. Era impossível fazer um site que funcionasse em todos os navegadores e em todos os dispositivos ao mesmo tempo com os padrões ainda não oficializados e cada navegador implementando partes da especificação. E por conta disso surgiram meta-linguagens pra nos blindar essa bagunça. Foi quando JQuery deixou de ser opcional e passou a ser uma necessidade pra lidar com Javascript e CSS. Com o fim do Flash, Javascript voltou a ser a única alternativa viável pra animação e interação, uma linguagem extremamente ruim que não via uma evolução significativa desde o começo dos anos 2000. Então começou a surgir uma gambiarra na forma de transpilers, linguagens que não são compiladas em instruções nativa de uma máquina real ou uma máquina virtual, mas que traduzem de uma linguagem para outra linguagem.</p>
<p>Novamente, o mundo Ruby on Rails veio tentar ajudar. O rubista Hamptom Caitlin escreveu o SASS ou Syntathically Awesome Stylesheets, usando Ruby. SASS é tipo um dialeto de CSS que permite estruturar melhor o CSS, dando funcionalidade de reusar código, e esconder detalhes específicos de navegadores. O Twitter resolveu criar sua própria versão e daí surgiu o LESS. SASS e LESS ficaram concorrendo por anos mas SASS é superior hoje em dia.</p>
<p>Também do mundo Ruby, em 2009 Jeremy Ashkenas escreveu um compilador em Ruby chamado Coffeescript. Uma nova linguagem cujo resultado da compilação era Javascript. Ele adicionou dezenas de conceitos de linguagens como Ruby e Python para criar uma nova linguagem muito mais agradável de trabalhar e que geraria a porcaria do Javascript por baixo. Isso ajudou muito a evitar termos que usar algo como JQuery ou ficar nós mesmos nos preocupando em adicionar código específico pra cada navegador. Hoje em dia todo mundo demoniza Coffeescript porque a sintaxe dele é um pouco agressivo e pouco ortodoxo, mas foi dele que saíram diversos conceitos como interpolação de strings, melhor controle sobre enumerators e iteradores, a agora famosa sintaxe de fat arrow pra funções anônimas e muito mais que foi incorporado ao Javascript versão ES6 que só foi sair em 2015, tornando o Coffescript obsoleto.</p>
<p>Mais do que só matar o Flash, o surgimento de smartphones com telas verticais e resoluções diferentes de um monitor de computador, também surgiu a necessidade de criar estilos que se adequassem automaticamente ao formato da tela e com o movimento de tableless, ou seja, parar de usar tabelas pra estruturar layout em HTML, e a consolidação de elementos flutuantes com espaçamento relativo do CSS2 transicionamos pro tal design responsivo que tanto se fala em hoje em dia. Layouts que automaticamente se adequam ao dispositivo e com o advento da interação via touch uma nova classe de pesquisa foi iniciada, o que hoje chamamos de UX ou usabilidade. Pense que até então o normal eram teclado e mouse, como criar telas interativas que só usam o dedo? Hoje em dia isso é um problema resolvido, mas principalmente entre 2009 e 2015 isso foi assunto de muita discussão.</p>
<p>E como o primeiro smartphone de verdade do mundo foi o iPhone da Apple, além das tecnologias ela trouxe outra coisa do seu DNA: seu estilo de design e linguagem de usabilidade. Se o fim dos anos 90 foi a época barroca do design Web, a Apple trouxe a linguagem de Bauhaus: o que hoje chamamos de design minimalista, que valoriza os espaços em branco entre elementos, linhas simples sem o escândalo de cores bregas e tudo desalinhado que se via em páginas de redes sociais como MySpace. E em cima dessa linguagem as empresas pararam de reinventar a roda em cada site e passaram a criar bibliotecas de estilo reusáveis e daí nascem os frameworks de CSS, escritos nas linguagens SASS e LESS que falei acima. O mais famoso deles sendo o Twitter Bootstrap. Por isso muitos sites parecem que foram feitos pelo mesmo designer, porque todo mundo usa o Bootstrap em vez de partir do zero e ficar reinventando a roda redesenhando o visual de botões e outros elementos. Daí criamos elementos novos com o hamburguinho pra menus, os Jumbotrons e Scrollspy que toda landing page usa.</p>
<p>Com tantos novos elementos de Javascript e CSS surgiu outra necessidade. Agora não era mais só escrever um arquivo de CSS, linkar no HTML e pronto. Passamos a reusar dezenas de bibliotecas como um JQuery da vida, código pra Google Analytics, frameworks de CSS gigantes, enfim muitos e muitos assets. Primeiro, precisávamos dar um jeito de baixar todos esses assets e bibliotecas nos nossos projetos sem ficar indo de site em site, clicando no link de download e descomprimindo zips manualmente nos diretórios. No mundo Perl já tinhamos coisas como o CPAN que permitia baixar bibliotecas de um servidor central. No mundo Ruby, depois de várias tentativas finalmente consolidamos no Bundler pra não só baixar as bibliotecas mas gerenciar corretamente as versões corretas que nossos projetos precisariam. E no mundo Javascript as coisas começaram meio devagar com o advento do NPM que só ficaria realmente usável quando concorrentes como YARN tentaram tomar seu lugar. Mas no mundo Javascript o NPM pelo menos se tornou o padrão de fato para gerenciar os assets no lado da máquina do desenvolvedor.</p>
<p>Mas só isso não é suficiente. Como eu expliquei no começo do episódio, um servidor web cospe HTML e todos esses assets pro navegador. E como agora colocarmos dezenas deles nos nossos projetos você devia imaginar que quanto mais arquivos e quanto maior eles forem, mais lento vai ser pro navegador baixar e renderizar tudo. E isso se tornou ainda mais óbvio nos primeiros smartphones. Hoje em dia temos alta velocidade em conexões 4G e já estamos próximos de ir pra 5G, mas em 2009 o normal ainda era 2G e poucos com 3G. Como otimizar esses assets pra ficarem menor? Alguém parou pra pensar: esses assets de Javascript e CSS são tudo texto. Nós programadores sabemos fazer texto ficar menor. Por exemplo, nós escrevemos código com tabulação, espaços, usamos nomes grandes e descritivos pra ficar mais fácil outro programador conseguir editar esse código. Mas um navegador não se importa com espaços em branco ou nomes descritivos, computadores sabem ler dados sem formatação. E espaços em branco ou nomes grandes ocupam espaço desnecessário. Mais do que isso, quanto mais arquivos separados tivermos, mas lento vai ser. Então porque não juntar vários arquivos num único arquivo e porque não filtrarmos e tirarmos fora espaços em branco e reduzir nomes de variáveis e funções ao menor tamanho possível, dentre outros truques? Esse é o conceito que chamamos de minificação, minificar textos humanamente legíveis em textos menores horrorosos mas que navegadores conseguem ler. E com tantas atividades agora pra chegar nos arquivos de HTML, CSS e Javascript finais otimizados, temos que transpilar de SASS pra CSS, de Coffeescript ou ES6 pra Javascript ES5 que na época era mais usado. É o que chamamos de um canos de processamento de assets ou Asset Pipeline como chamamos no Ruby on Rails onde o conceito nasceu. E no mundo Javascript surgiram coisas similares como Grunt, Gulp e hoje em dia Webpacker somado a ferramentas como Babel que fazem exatamente esse tipo de coisa. Por causa disso, todo novo framework que se preza tem algum tipo de integração com coisas como Webpacker pra ter esse pipeline de processamento. Isso ficou um pouco menos necessário desde que evoluímos pro padrão HTTP/2, mas isso é assunto pra outro dia.</p>
<p>Evoluindo em cima da herança do GMAIL, só AJAX não era mais suficiente e passamos a experimentar em tirar lógica dos servidores de aplicação e transferir o controle do estado da aplicação pra mais próximo da tela gráfica, e assim surgem os frameworks Javascript. Em vez de ficar controlando cada elemento da tela individualmente via Javascript e ficar controlando os estilos globais com CSS, por que organizar uma tela de uma aplicação direto só em seus menores elementos? Por exemplo, num Spotify em vez de falarmos em botões ou listas, podemos separar em coisas maiores como Playlist, Menu de Usuário, dashboard de playback, lista de resultados de pesquisa de música. São todos grandes “componentes” que interagem entre si. E cada um deles tem uma representação gráfica que depende de um estado. Por exemplo, no dashboard de playback, temos estados como tocando, pausado, shuffle ativado ou Não. E pra criar esses componentes e gerenciar os estados no lado do cliente, aproveitando tecnologias do HTML5 como web storage que permite guardar esse estado no navegador, surgem frameworks como Ember, Angular, React + Redux ou Vue.js. E assim também surge o conceito de uma única página que carrega toda essa aplicação que é composta de javascript e assets como CSS e imagens, sem precisar mudar de página pra atualizar entre cada estado. Pra fazer isso usamos Ajax por baixo e rodamos tudo numa única página e por isso temos o conceito que vocês já devem ter ouvido de SPAs ou Single Page Apps.</p>
<p>Isso aconteceu a partir do nascimento de aplicações mais complexas em interação e estado como o próprio Google Docs, Slack, Spotify e outros. E é por isso que hoje você tem esses frameworks. Na época que a Apple estava na dianteira desse tipo de aplicativos por conta do iCloud ela tinha um framework próprio chamado Sproutcore. Dali veio a semente que viria a se tornar Ember, que também saiu da comunidade Ruby on Rails e por isso Ember se parece um pouco em estrutura com o Rails. O Google tinha o antigo GWT que foi muito usado no mundo Java pra fazer aplicativos parecidos com o Gmail, mas depois criou o Angular e depois o Polymer que não deu muito certo em adoção. E com o crescimento do Facebook e necessidade de criar aplicativos como o Facebook Messenger, surgiu o React que torna mais veloz o desenho de partes da tela cujo estado foi modificado com seu Virtual DOM. Ou seja, em vez de ficarmos redesenhando explicitamente um elemento na tela, tipo mudar a cor de um botão pra vermelho, só mudamos o estado do botão e deixamos esse Virtual DOM redesenhar o que precisar na tela, de forma que podemos evitar redesenhar o que não se precisa e ao mesmo tempo otimizar a velocidade total do redesenho. E na tentativa de fazer concorrência e evoluir em cima da experiência do Angular e React, no mundo open source surgiu o Vue.js que começou a ganhar algum momento graças à sua inclusão como padrão no framework Laravel no mundo PHP.</p>
<p>E nos dias de hoje, com a evolução do Javascript ES6, a dominância do Chrome como navegador mais usado e a decadência do Internet Explorer, e também o surgimento de processadores multi-core mais rápidos e mais eficientes nos smartphones, com RAM mais barata que permite que desktops e dispositivos consigam ter 6GB ou 8GB ou mais de RAM, começou a surgir uma nova classe de aplicativos: os híbridos. Aplicativos de desktop ou de mobile que tecnicamente tem uma estrutura nativa que liga com o hardware como camera, touch, audio e muito mais mas são controlados por uma fina camada de Javascript e outras tecnologias Web como HTML5 e CSS3. Então podemos usar a mesma estrutura que descrevi acima pra fazer aplicativos desktop ou mobile como é o caso de coisas que usamos todo dia como o próprio Spotify ou Slack ou editores de código como Atom ou Visual Studio Code. E assim surgem também frameworks híbridos como o Electron que é uma forma de encapsular tecnologias do Chrome e abrir como se fosse um aplicativo nativo ou então React Native que torna possível você desenvolver um aplicativo simples pra iOS ou Android como se estivesse desenvolvendo um site web em React.</p>
<p>Caraca!! Eu sei que eu pincelei bem por cima de dezenas de assuntos e mais de 25 anos de história. Já disse em outros episódios que eu sinto que nasci na hora certa porque eu pude ver cada evento que eu narrei aqui acontecendo em tempo real. Isso me ajudou muito porque eu pude ir aprendendo cada nova tecnologia à medida que elas foram aparecendo. Algumas delas morreram já, como ASP clássico, Flash, Silverlight, Active X, Applets Java, Coffeescript, GWT, e ao mesmo tempo muitas novidades apareceram na forma dos atuais padrões HTML 5, CSS 3 e Javascript ES6 e as ferramentas por cima delas como React, SASS e assim por diante. Mas eu espero que ao ouvir sobre essa linha do tempo você consiga ter uma noção melhor de onde cada coisa se encaixa. E eu recomendo que você assista este episódio mais vezes, não agora, mas de tempos em tempos pra ver se o que eu estou falando vai fazendo mais sentido à medida que você vai estudando e treinando.</p>
<p>Sempre que tiver dúvidas sobre uma tecnologia comece do começo: pesquise sua história. A história vai te dizer, porque essa tecnologia existe? Que problemas ela foi desenhada pra resolver? Ela ainda resolve esse problema da melhor forma ou já surgiram substitutos? Ainda vale a pena aprender ou já tem coisa melhor na frente? Por onde anda o autor original da tecnologia? Ou a empresa que patrocinou essa tecnologia? São perguntas como essas que vão te dar o norte sobre o que estudar e como estudar.</p>
<p>Se eu fiz meu trabalho direito, você começou este episódio com dúvidas sobre o que estudar. E deve ter terminado este episódio com mais dúvidas ainda! E esse é o objetivo. Se você tem poucas perguntas, não está se esforçando o suficiente. Faça a pergunta errada e vai receber a resposta errada. Reveja seus conceitos, veja a lista de coisas que tem pra estudar, coloque na linha do tempo, veja quais problemas você vai resolver com quais ferramentas. Encontre problemas pra resolver e vá aprendendo dessa forma: na tentativa e erro. Quais problemas? Eu não disse no episódio anterior que você precisa aprender a fuçar os projetos open source que estão no Github? É por ali mesmo, você vai achar o tal clone do Instagram que alguém fez. Ele vai estar feito em Angular, que eu falei aqui. Você já entendeu pra que ele serve, procure um tutorial online sobre Angular. Daí vai ver que tem arquivos com extensão .scss que é SASS e agora você sabe o que é SASS e que deve ter algum pipeline, como Webpacker, então procure um tutorial de SASS e outro tutorial de Webpacker. E quando for rodar, você já sabe pelo menos em conceito a diferença de um servidor web e um servidor de aplicações, e se levou a sério meu episódio anterior você está estudando distribuições Linux, agora procure um tutorial de como instalar, configurar e subir um NGINX por exemplo.</p>
<p>Cada um desses assuntos não é pra levar anos pra aprender. Se você for devagar, gaste 1 mês em cada item. Aprenda os 20% que eu vivo falando. E aprenda treinando. Teoria e prática. A parte avançada vai aparecendo à medida que você realmente coloca a mão no código e faz rodar na sua máquina. Qualquer coisa antes disso é perda de tempo. E apesar de eu não gostar de recomendar diretamente sites de tutoriais ou cursos, vou deixar linkado alguns que conhecidos meus recomendaram, então não deixe de ver esses links na descrição do vídeo abaixo.</p>
<p>E por hoje é isso aí, o objetivo de hoje foi colocar as tecnologias de front-end dentro da história. Semana que vem quero tentar fazer a mesma coisa com tecnologias back-end e na seguinte finalmente um pouco sobre devops. Eu sei que vocês ficaram com dúvidas, e eu mesmo não sou o melhor programador de front-end, então mandem suas dúvidas nos comentários abaixo pra todo mundo poder ajudar a responder. Se curtiram mandem um joinha, compartilhem com seus amigos, assinem o canal e cliquem no sininho pra não perder os próximos episódios. A gente se vê, até mais pessoal!</p>
<p>Este episódio vai testar a paciência de vocês. Desta vez quero explorar um pouco os diversos assuntos que são associados com a tal carreira de &ldquo;Front-End&rdquo; no mundo de desenvolvimento Web.</p>
<p>Se já começaram a estudar o assunto devem ter visto que existe uma infinidade de sopas de letrinhas, coisas como SASS, CSS, React, Vue, e muito mais. Por que existem tantas letrinhas assim? Quais escolher?</p>
]]></content:encoded><category>frontend</category><category>desenvolvimento-web</category><category>javascript</category><category>akitando</category></item><item><title>Iniciante Experimentando o Rails 5.1.x</title><link>https://akitaonrails.github.io/2017/10/24/iniciante-experimentando-o-rails-5-1-x/</link><guid isPermaLink="true">https://akitaonrails.github.io/2017/10/24/iniciante-experimentando-o-rails-5-1-x/</guid><pubDate>Tue, 24 Oct 2017 16:29:00 -0200</pubDate><description>&lt;p&gt;Uma coisa chata de lidar com projetos de clientes é que a maioria deles demora para fazer um upgrade completo de framework. E não dá para culpá-los, já que boa parte das dependências do ecossistema também leva um tempão para atualizar.&lt;/p&gt;
&lt;p&gt;O &lt;a href="http://weblog.rubyonrails.org/2017/9/29/this-week-in-rails-getting-closer-to-rails-5-2-beta/"target="_blank" rel="noopener"&gt;Rails 5.2 Beta&lt;/a&gt; está chegando, e eu apostaria que a maioria dos projetos Rails lá fora ainda está na versão 4.2, ainda cogitando o salto para o 5.0 (que até não é um upgrade tão complicado).&lt;/p&gt;</description><content:encoded><![CDATA[<p>Uma coisa chata de lidar com projetos de clientes é que a maioria deles demora para fazer um upgrade completo de framework. E não dá para culpá-los, já que boa parte das dependências do ecossistema também leva um tempão para atualizar.</p>
<p>O <a href="http://weblog.rubyonrails.org/2017/9/29/this-week-in-rails-getting-closer-to-rails-5-2-beta/"target="_blank" rel="noopener">Rails 5.2 Beta</a> está chegando, e eu apostaria que a maioria dos projetos Rails lá fora ainda está na versão 4.2, ainda cogitando o salto para o 5.0 (que até não é um upgrade tão complicado).</p>
<p>A propósito, caso você não saiba, o site oficial do Rails Guides tem uma seção dedicada a upgrades. É o <a href="http://guides.rubyonrails.org/upgrading_ruby_on_rails.html"target="_blank" rel="noopener">A Guide for Upgrading Ruby on Rails</a>. O processo de upgrade não é tão difícil <strong>se, e somente se</strong>:</p>
<ul>
<li>
<p>Você tiver uma cobertura de testes razoável. No mínimo, feature specs para as partes mais críticas do sistema.</p>
</li>
<li>
<p>Você subir apenas uma versão de cada vez, nunca pular direto para a mais recente. Por exemplo: se está no Rails 4.1, primeiro sobe para o 4.2, confirma que tudo funciona. Depois vai para o 5.0. E só então vai para o 5.1. Cada versão tem <strong>muitas</strong> deprecations, mudanças de comportamento, alterações na configuração padrão e recursos novos. Cada ponto merece atenção cuidadosa.</p>
</li>
</ul>
<p>Então, o que você está perdendo se ainda está no Rails 4.2?</p>
<p>De novo, caso não saiba, o site oficial do Rails Guides também compila Release Notes detalhadas e fáceis de ler para cada nova versão. As mais recentes são as <a href="http://guides.rubyonrails.org/5_1_release_notes.html"target="_blank" rel="noopener">Ruby on Rails 5.1 Release Notes</a>.</p>
<h2>Começando um novo projeto<span class="hx:absolute hx:-mt-20" id="começando-um-novo-projeto"></span>
    <a href="#come%c3%a7ando-um-novo-projeto" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O comando <code>rails new</code> ganhou várias flags novas. Acho que as que a maioria vai usar são:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>rails new --skip-action-mailer --skip-coffee --webpack=react my_fancy_new_project</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 desabilitar recursos que não precisa, como o ActionMailer. Pode desabilitar o CoffeeScript. Foi legal por um tempo, mas agora que temos ES6 é melhor usar direto.</p>
<p>O Rails 5.1 vem com suporte a webpack. Mas até o Rails 5.2 chegar, precisamos rodar o <code>webpack-dev-server</code> manualmente em modo de desenvolvimento para ter hot reload dos assets. Para isso, recomendo instalar o bom e velho <a href="https://github.com/ddollar/foreman"target="_blank" rel="noopener">Foreman</a>. Instale assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>gem install foreman</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 adicione um arquivo <code>Procfile.dev</code> no seu projeto com o seguinte conteúdo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>web: ./bin/rails s -b 0.0.0.0 -p 3000
webpack: ./bin/webpack-dev-server</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 você pode subir seu servidor de desenvolvimento assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>foreman start -f Procfile.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>Em produção, não precisa do <code>webpack-dev-server</code>, pois a task <code>bin/rails assets:precompile</code> consegue compilar todos os assets como arquivos estáticos com timestamps em <code>public/assets</code>.</p>
<h2>Mudanças no Front-end<span class="hx:absolute hx:-mt-20" id="mudanças-no-front-end"></span>
    <a href="#mudan%c3%a7as-no-front-end" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Agora você pode comemorar: Yarn, Webpack, e sem mais jQuery pré-instalado. Samuel Muller tem um <a href="http://samuelmullen.com/articles/embracing-change-rails51-adopts-yarn-webpack-and-the-js-ecosystem/"target="_blank" rel="noopener">artigo bem bacana</a> que resume a maioria dessas mudanças em mais detalhes.</p>
<p>O Ruby on Rails agora suporta oficialmente tudo o que o ecossistema Javascript tem a oferecer. Não falta mais nada.</p>
<p>Acabou a era de fazer vendor manual de assets JS no diretório <code>vendor/assets/javascripts</code>. Não precisa mais do <a href="https://rails-assets.org/"target="_blank" rel="noopener">Rails Assets</a>, que era uma fonte secundária do RubyGems específica para empacotar bibliotecas JS como gems.</p>
<p>Digamos que, por algum motivo, você quer o jQuery de volta. Agora é só fazer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>yarn add jquery</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, como de costume, adicionar no seu manifest <code>app/assets/javascripts/application.js</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>// app/assets/javascripts/application.js
...
//= require jquery
...
//= require_tree .</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 tal <a href="https://github.com/rails/rails/pull/26836"target="_blank" rel="noopener">&ldquo;suporte ao Yarn no Rails 5.1&rdquo;</a> se resume a alguns wrappers e boilerplate. Você pode rodar <code>rails yarn:install</code> para instalar as dependências, mas pode igualmente digitar <code>yarn</code> que ele instala tudo o que está declarado no <code>package.json</code>, como em qualquer projeto puramente Javascript.</p>
<p>Toda a mágica da integração vem com a inclusão da gem <a href="https://github.com/rails/webpacker"target="_blank" rel="noopener">Webpacker</a>. Ela adiciona um monte de configuração de boilerplate para o webpack. E, <strong>dado bônus</strong>: você pode incluí-la hoje mesmo nos seus projetos Rails 4.2+. Comece adicionando a gem ao <code>Gemfile</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># Gemfile
gem &#39;webpacker&#39;, &#39;~&gt; 3.0&#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>Depois <code>bundle install</code> normalmente e rode:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>bundle exec rake webpacker:install</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 não usou a flag <code>--webpack=react</code> no <code>rails new</code>, pode adicionar depois. Ou adicionar Angular também:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>bundle exec rake webpacker:install:angular
bundle exec rake webpacker:install:react </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 estiver no Rails 5.1, substitua <code>bundle exec rake</code> por <code>bin/rails</code>.</p>
<p>Adicionar componentes React de verdade exige um pouco mais do que os comandos acima, mas os detalhes fogem ao escopo deste post.</p>
<p>Andy Barnov tem um <a href="https://x-team.com/blog/get-in-full-stack-shape-with-rails-5-1-webpacker-and-reactjs/"target="_blank" rel="noopener">bom post</a> sobre como seguir a convenção de packs do Rails 5.1 para adicionar seus componentes React no pipeline. Como ele recomenda: não briga com as convenções e você fica tranquilo.</p>
<p>Curiosamente, mesmo tendo usado a flag <code>--webpack-react</code> no <code>rails new</code>, ainda precisei rodar o seguinte comando para inicializar o suporte a webpack e react:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>./bin/rails webpacker:install:react</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>Mas uma vez feito isso, você tem um novo lugar para adicionar componentes React: o diretório <code>app/javascripts/packs</code>. E nas views, em vez do habitual <code>javascript_include_tag</code>, você usa o novíssimo <code>javascript_pack_tag</code>. O resto você consegue descobrir a partir do componente de exemplo <code>hello_react.jsx</code> que é gerado automaticamente.</p>
<h2>Considerações Finais<span class="hx:absolute hx:-mt-20" id="considerações-finais"></span>
    <a href="#considera%c3%a7%c3%b5es-finais" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Agora você tem muito mais opções para Javascript, já que o Rails 5.1 abraça de vez os padrões atuais.</p>
<p>Houve uma época em que ninguém sabia direito como agilizar um <a href="http://guides.rubyonrails.org/asset_pipeline.html"target="_blank" rel="noopener">Asset Pipeline</a>. Era uma bagunça compilar todos os assets num único arquivo com timestamps para cache busting — mas a gem <strong>Sprockets</strong> resolveu isso.</p>
<p>Houve uma época em que o Javascript 5 era uma bagunça monumental. Bibliotecas como o jQuery &ldquo;consertaram&rdquo; de fato a situação do DOM nos browsers, e o CoffeeScript normalizou a linguagem de um jeito que a tornava prazerosa. O ES6 veio tomar as rédeas, mas é muito injusto criticar o jQuery e o CoffeeScript: nem o ES6 nem o HTML5 existiam para resolver o problema quase 10 anos atrás. O jQuery e o CoffeeScript estavam lá, e resolveram.</p>
<p>Quando Angular, Ember e React começaram a surgir, já tínhamos uma solução boa o suficiente para sites pesados em Javascript com o <a href="https://github.com/turbolinks/turbolinks"target="_blank" rel="noopener">Turbolinks</a>. Ainda é uma ótima solução que você deveria considerar antes de adicionar uma solução React/Redux completa (e às vezes desnecessária) de uma tacada só.</p>
<p>CSS é outro assunto. A comunidade Rails também criou o Sass (que o Twitter depois invejou e copiou como Less para o framework Bootstrap).</p>
<p>É fácil olhar para o cenário atual e simplesmente repetir que CoffeeScript é ruim ou que Sprockets é ruim. Mas nos últimos 10 anos essas ferramentas eram o estado da arte — numa época em que as pessoas não tinham Yarn, Webpack, React e tudo o que surgiu nos últimos 3 anos ou assim.</p>
<p>O Rails 5.1 é um release muito bom para casar o melhor do Rails com o melhor que se estabilizou no cenário caótico do Javascript. Recomendo fortemente que qualquer projeto novo, em campo aberto, comece com o Rails 5.1 em mente.</p>
<p>O que me motivou a escrever este post foi destacar que muita gente faz perguntas que já estão respondidas no <a href="http://guides.rubyonrails.org/"target="_blank" rel="noopener">Rails Guides</a> oficial. Não só sobre como instalar e usar os vários componentes do framework, mas também como fazer upgrade e quais são as novidades de cada release. Vale a pena dar uma olhada mais cuidadosa por lá.</p>
]]></content:encoded><category>rails</category><category>javascript</category><category>frontend</category></item><item><title>[Small Bites] Trocando de SASS para SASSC e colocando no Heroku</title><link>https://akitaonrails.github.io/2015/05/15/small-bites-trocando-de-sass-para-sassc-e-colocando-no-heroku/</link><guid isPermaLink="true">https://akitaonrails.github.io/2015/05/15/small-bites-trocando-de-sass-para-sassc-e-colocando-no-heroku/</guid><pubDate>Fri, 15 May 2015 13:07:00 -0300</pubDate><description>&lt;p&gt;Obs: libsass não necessariamente é 100% compatível com Sass-Ruby ainda. Em muitos casos você não deve ter problemas (teste!) Mas fique de olho nesta &lt;a href="http://sass-compatibility.github.io"target="_blank" rel="noopener"&gt;tabela comparativa de compatibilidade&lt;/a&gt;. No instante da publicação o libsass 3.2 está a 98.53% de compatibilidade!&lt;/p&gt;
&lt;p&gt;Nada foi mais disruptivo no mundo de desenvolvimento de stylesheets do que o advento do SASS em 2007. Sim, faz 8 anos, e sim, estou exagerando, CSS 3 foi bem mais disruptivo, mas vocês entenderam! A menos que eu esteja enganado foi quando se entendeu também que era possível transformar uma linguagem tosca (CSS 2) em algo bem mais trabalhável, foi o início do uso do conceito de &lt;a href="http://en.wikipedia.org/wiki/Source-to-source_compiler"target="_blank" rel="noopener"&gt;transpilers&lt;/a&gt; para Web.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Obs: libsass não necessariamente é 100% compatível com Sass-Ruby ainda. Em muitos casos você não deve ter problemas (teste!) Mas fique de olho nesta <a href="http://sass-compatibility.github.io"target="_blank" rel="noopener">tabela comparativa de compatibilidade</a>. No instante da publicação o libsass 3.2 está a 98.53% de compatibilidade!</p>
<p>Nada foi mais disruptivo no mundo de desenvolvimento de stylesheets do que o advento do SASS em 2007. Sim, faz 8 anos, e sim, estou exagerando, CSS 3 foi bem mais disruptivo, mas vocês entenderam! A menos que eu esteja enganado foi quando se entendeu também que era possível transformar uma linguagem tosca (CSS 2) em algo bem mais trabalhável, foi o início do uso do conceito de <a href="http://en.wikipedia.org/wiki/Source-to-source_compiler"target="_blank" rel="noopener">transpilers</a> para Web.</p>
<p>Por todos esses anos o Sass, escrito em Ruby, serviu seu propósito muito bem, mas sabemos que ele é um dos calcanhares de aquiles quando fazemos deployment de projetos Ruby. Em paralelo se iniciou uma reimplementação em C (<a href="http://libsass.org"target="_blank" rel="noopener">libsass</a>) para tornar o Sass não dependente de Ruby (agnóstico de linguagem) e com performance mais adequada.</p>
<p>O pessoal de Node e outras linguagens já usa o libsass em pacotes como o node-sass. É hora do filho pródigo retornar à sua casa. Para usar em projetos Ruby on Rails precisamos fazer o seguinte:</p>
<p>Atualizar o arquivo <tt>Gemfile</tt> e trocar <tt>sass-rails</tt> por <tt>sassc-rails</tt>:</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">gem</span> <span class="s1">&#39;sassc-rails&#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>Rodar o bom e velho <tt>bundle install</tt> e adicionar um arquivo chamado <tt>.buildpacks</tt> à raiz do seu projeto:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>https://github.com/djmattyg007/heroku-buildpack-sassc.git#1.1.0
https://github.com/heroku/heroku-buildpack-ruby.git#v137</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 a seguinte linha de comando:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>heroku config:add BUILDPACK_URL=https://github.com/ddollar/heroku-buildpack-multi.git</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 conceito é simples: todo container do Heroku é configurado através de uma <a href="https://devcenter.heroku.com/articles/buildpacks"target="_blank" rel="noopener">buildpack</a> (que controla os fluxos de compilação e lançamento). O Heroku escolhe o padrão correto para sua aplicação. O que fizemos acima foi substituir por um <a href="https://devcenter.heroku.com/articles/third-party-buildpacks"target="_blank" rel="noopener">Custom Buildpack</a>. Uma das alternativas seria fazer um fork do buildpack de Ruby e adicionar o libsass. Por outro lado já existe um buildpack só com o libsass e podemos criar um slug com duas buildpacks usando a buildpack &ldquo;heroku-buildpack-multi&rdquo; criada pelo <a href="https://github.com/ddollar"target="_blank" rel="noopener">@ddollar</a>.</p>
<p>Ou digamos que gostaríamos de rodar uma aplicação Ruby mas queremos ter dependências de Node ou Python para executar alguma coisa específica durante o deployment, basta adicionar outras buildpacks ao arquivo <tt>.buildpacks</tt>.</p>
<p>Enfim, com isso vamos conseguir reduzir consideravelmente (depende da quantidade de stylesheets que você tem no seu projeto) o tempo de cada deployment.</p>
]]></content:encoded><category>rails</category><category>frontend</category><category>performance</category></item><item><title>[Small-Bites] Em defesa do Asset Pipeline: tudo que você precisa</title><link>https://akitaonrails.github.io/2014/12/02/small-bites-em-defesa-do-asset-pipeline-tudo-que-voce-precisa/</link><guid isPermaLink="true">https://akitaonrails.github.io/2014/12/02/small-bites-em-defesa-do-asset-pipeline-tudo-que-voce-precisa/</guid><pubDate>Tue, 02 Dec 2014 17:26:00 -0200</pubDate><description>&lt;p&gt;No atual caos que são as opções no mundo Javascript para gerenciamento de dependências (CommonJS, AMD, ES6-modules), sistemas de build (Grunt, Gulp, Broccoli), pacotes (NPM, Bower) é fácil sermos contaminados com o velho &amp;ldquo;F.U.D.&amp;rdquo; (Fear, Uncertainty, Doubt).&lt;/p&gt;
&lt;p&gt;Asset Management é um problema praticamente todo resolvido no mundo Rails desde Fevereiro de 2009 (nós basicamente criamos o conceito). Mesmo assim, ainda temos que ouvir afirmações sem sentido como &amp;ldquo;precisamos consertar ou substituir o Asset Pipeline&amp;rdquo; e então nascem coisas como &amp;ldquo;requirejs-rails&amp;rdquo; ou receitas de como desabilitar o Sprockets e usar outras coisas. Em resumo: não faça isso.&lt;/p&gt;</description><content:encoded><![CDATA[<p>No atual caos que são as opções no mundo Javascript para gerenciamento de dependências (CommonJS, AMD, ES6-modules), sistemas de build (Grunt, Gulp, Broccoli), pacotes (NPM, Bower) é fácil sermos contaminados com o velho &ldquo;F.U.D.&rdquo; (Fear, Uncertainty, Doubt).</p>
<p>Asset Management é um problema praticamente todo resolvido no mundo Rails desde Fevereiro de 2009 (nós basicamente criamos o conceito). Mesmo assim, ainda temos que ouvir afirmações sem sentido como &ldquo;precisamos consertar ou substituir o Asset Pipeline&rdquo; e então nascem coisas como &ldquo;requirejs-rails&rdquo; ou receitas de como desabilitar o Sprockets e usar outras coisas. Em resumo: não faça isso.</p>
<p>Até eu fico sem paciência para participar dessas discussões porque, no final do dia, o que precisamos é da solução mais simples que funciona e podemos confiar. E sem mais delongas eu elejo a combinação ModuleJS e Rails-Assets como as melhores opções.</p>
<p>Antes de continuar vou assumir que você pelo menos leu (ou já conhece) os seguintes assuntos:</p>
<ul>
<li><a href="http://www.akitaonrails.com/2012/07/01/asset-pipeline-para-iniciantes"target="_blank" rel="noopener">Asset Pipeline para Iniciantes</a></li>
<li><a href="http://www.akitaonrails.com/2012/07/01/asset-pipeline-para-iniciantes-parte-2"target="_blank" rel="noopener">Asset Pipeline para Iniciantes - Parte 2</a></li>
<li><a href="http://www.akitaonrails.com/2013/12/13/rails-assets"target="_blank" rel="noopener">Rails Assets</a></li>
</ul>
<p>Para ilustrar a solução vamos direto a um pequeno exemplo. Se for algo pequeno, podemos ficar tentados a colocar tudo no <tt>app/assets/application.js</tt>, como 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-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">Foo</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">hello</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;Hello&#34;</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="nx">world</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;World&#34;</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></span><span class="line"><span class="cl"><span class="nx">Bar</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">helloWorld</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">Foo</span><span class="p">.</span><span class="nx">hello</span><span class="p">()</span> <span class="o">+</span> <span class="s2">&#34; &#34;</span> <span class="o">+</span> <span class="nx">Foo</span><span class="p">.</span><span class="nx">world</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></span><span class="line"><span class="cl"><span class="nx">$</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">$</span><span class="p">(</span><span class="s1">&#39;body&#39;</span><span class="p">).</span><span class="nx">append</span><span class="p">(</span><span class="s1">&#39;&lt;p&gt;&#39;</span> <span class="o">+</span> <span class="nx">Bar</span><span class="p">.</span><span class="nx">helloWorld</span><span class="p">()</span> <span class="o">+</span> <span class="s1">&#39;&lt;/p&gt;&#39;</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>Isso vai simplesmente adicionar um parágrafo escrito &ldquo;Hello World&rdquo; na página. A idéia é um exemplo bem trivial para que a técnica fique clara, mas obviamente imagine isso em algo mais complicado.</p>
<p>A primeira coisa que gostaríamos de fazer é separar esse código. Talvez quebrar em arquivos como <tt>app/assets/javascripts/foo.js</tt> e <tt>app/assets/javascripts/bar.js</tt>. Então vamos direto ao assunto e adicionar o <a href="http://larsjung.de/modulejs/"target="_blank" rel="noopener">ModuleJS</a> diretamenta à nossa <tt>Gemfile</tt>:</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"># Gemfile</span>
</span></span><span class="line"><span class="cl"><span class="n">source</span> <span class="s1">&#39;https://rails-assets.org&#39;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ...</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;rails-assets-modulejs&#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>Depois de instalar com o bom e velho <tt>bundle install</tt>. Modificamos o <tt>app/assets/javascripts/application.js</tt> para carregar a dependência:</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-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">//= require jquery
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require jquery_ujs
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require modulejs
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require_tree .
</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 foi o que eu já expliquei no meu post mais antigo sobre <a href="http://www.akitaonrails.com/2013/12/13/rails-assets"target="_blank" rel="noopener">Rails Assets</a>. Se a biblioteca que você quer tem um pacote Bower, ele pode ser automaticamente encapsulado numa gem de Rails Engine como uma gem feita manualmente. E se não tiver como pacote Bower aí não tem jeito: baixe os arquivos e jogue num diretório em <tt>vendor/assets</tt> para mantê-la estática dentro do projeto, mas o ideal é que você <strong>não</strong> deixe bibliotecas javascript vendorizados.</p>
<p>Para começar a refatoração do javascript acima, vamos começar quebrando os objetos &lsquo;Foo&rsquo; e &lsquo;Bar&rsquo; em dois arquivos:</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-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// app/assets/javascripts/foo.js
</span></span></span><span class="line"><span class="cl"><span class="nx">modulejs</span><span class="p">.</span><span class="nx">define</span><span class="p">(</span> <span class="s1">&#39;foo&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">hello</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="s2">&#34;Hello&#34;</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="nx">world</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="s2">&#34;World&#34;</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></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>
<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-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// app/assets/javascripts/bar.js
</span></span></span><span class="line"><span class="cl"><span class="nx">modulejs</span><span class="p">.</span><span class="nx">define</span><span class="p">(</span> <span class="s1">&#39;bar&#39;</span><span class="p">,</span> <span class="p">[</span> <span class="s1">&#39;foo&#39;</span> <span class="p">],</span> <span class="kd">function</span><span class="p">(</span><span class="nx">Foo</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">helloWorld</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nx">Foo</span><span class="p">.</span><span class="nx">hello</span><span class="p">()</span> <span class="o">+</span> <span class="s2">&#34; &#34;</span> <span class="o">+</span> <span class="nx">Foo</span><span class="p">.</span><span class="nx">world</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></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>Veja que não precisamos mais das constantes &lsquo;Foo&rsquo; e &lsquo;Bar&rsquo;, apenas devolver o corpo do hash com as funções. No módulo &lsquo;bar&rsquo;, declaramos que precisamos do módulo &lsquo;foo&rsquo; (em um array) e passamos como parâmetro do construtor com o nome antigo da constante &lsquo;Foo&rsquo;, daí internamente o código se mantém exatamente o mesmo. E já que estamos modularizando, vamos adicionar um módulo que encapsula a chamada principal:</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-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// app/assets/javascripts/main.js
</span></span></span><span class="line"><span class="cl"><span class="nx">modulejs</span><span class="p">.</span><span class="nx">define</span><span class="p">(</span> <span class="s1">&#39;main&#39;</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;bar&#39;</span><span class="p">,</span> <span class="s1">&#39;jquery&#39;</span><span class="p">],</span> <span class="kd">function</span><span class="p">(</span><span class="nx">Bar</span><span class="p">,</span> <span class="nx">$</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">start</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">$</span><span class="p">(</span><span class="s1">&#39;body&#39;</span><span class="p">).</span><span class="nx">append</span><span class="p">(</span><span class="s1">&#39;&lt;p&gt;&#39;</span> <span class="o">+</span> <span class="nx">Bar</span><span class="p">.</span><span class="nx">helloWorld</span><span class="p">()</span> <span class="o">+</span> <span class="s1">&#39;&lt;/p&gt;&#39;</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></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 pode ver acima, o módulo &lsquo;main&rsquo; vai depender do &lsquo;bar&rsquo; (definido em &lsquo;bar.js&rsquo;) e também de um &lsquo;jquery&rsquo; que ainda não existe, então vamos criar um genérico <tt>modules.js</tt> para encapsular o jQuery 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-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// app/assets/javascripts/modules.js
</span></span></span><span class="line"><span class="cl"><span class="nx">modulejs</span><span class="p">.</span><span class="nx">define</span><span class="p">(</span> <span class="s1">&#39;jquery&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">jQuery</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>Veja que podemos fazer a mesma coisa para qualquer outra biblioteca. Damos um nome em string, no caso &lsquo;jquery&rsquo;, que o ModuleJS vai conseguir encontrar depois e retornamos a classe principal, só isso.</p>
<p>Para finalizar no arquivo original <tt>application.js</tt> chamamos esse novo &lsquo;main&rsquo; 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-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">$</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">modulejs</span><span class="p">.</span><span class="nx">require</span><span class="p">(</span><span class="s1">&#39;main&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">app</span><span class="p">.</span><span class="nx">start</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>Pronto! Tudo isolado e com as dependências todas controladas. A grande vantagem de fazer desta forma é que no cabeçalho do <tt>application.js</tt> podemos continuar declarando as dependências com o <tt>require_tree .</tt> pois não importa a ordem dos arquivos, apenas a chamada principal do módulo &lsquo;main&rsquo;.</p>
<p>Pronto, não precisamos de RequireJS, podemos continuar usando o Asset Pipeline que vai gerar os arquivos minificados como o Rails sempre fez e com o mínimo de dependências que podem causar conflitos difíceis de rastrear depois. Vá adicionando seus pacotes preferidos de Bower diretamente na <tt>Gemfile</tt> e continue usando apenas um sistema de gerenciamento de dependência que sabemos que funciona: Bundler.</p>
]]></content:encoded><category>rails</category><category>javascript</category><category>frontend</category></item><item><title>Web Components é uma Revolução?</title><link>https://akitaonrails.github.io/2014/07/06/web-components-e-uma-revolucao/</link><guid isPermaLink="true">https://akitaonrails.github.io/2014/07/06/web-components-e-uma-revolucao/</guid><pubDate>Sun, 06 Jul 2014 01:09:00 -0300</pubDate><description>&lt;p&gt;Existe uma nova celebridade na cidade, e seu nome é &lt;a href="http://webcomponents.org/"target="_blank" rel="noopener"&gt;&amp;ldquo;Web Components&amp;rdquo;&lt;/a&gt;. Ultimamente muitos apresentam esta nova tecnologia como o &amp;lsquo;Santo Graal&amp;rsquo; que vai resolver todos os problemas da Web. Este artigo não é um apoio incondicional, não é uma crítica negativa irrefutável, mas meramente uma apresentação de perspectivas com o objetivo de dar clareza. Nenhuma discussão pode funcionar onde os gritos mais altos são simplesmente &lt;em&gt;&amp;ldquo;Isso vai revolucionar a Web!&amp;rdquo;&lt;/em&gt; (sem explicar porque) ou &lt;em&gt;&amp;ldquo;O Google e a Mozilla estão por trás então é foda!&amp;rdquo;&lt;/em&gt; (sem comentários).&lt;/p&gt;</description><content:encoded><![CDATA[<p>Existe uma nova celebridade na cidade, e seu nome é <a href="http://webcomponents.org/"target="_blank" rel="noopener">&ldquo;Web Components&rdquo;</a>. Ultimamente muitos apresentam esta nova tecnologia como o &lsquo;Santo Graal&rsquo; que vai resolver todos os problemas da Web. Este artigo não é um apoio incondicional, não é uma crítica negativa irrefutável, mas meramente uma apresentação de perspectivas com o objetivo de dar clareza. Nenhuma discussão pode funcionar onde os gritos mais altos são simplesmente <em>&ldquo;Isso vai revolucionar a Web!&rdquo;</em> (sem explicar porque) ou <em>&ldquo;O Google e a Mozilla estão por trás então é foda!&rdquo;</em> (sem comentários).</p>
<p>Do ponto de vista puramente técnico é muito simples entender o que é um &ldquo;Web Component&rdquo;. Ele é o conjunto de 4 tecnologias de navegadores web.</p>
<ul>
<li>Custom Elements</li>
<li>HTML Imports</li>
<li>Templates</li>
<li>Shadow DOM</li>
</ul>
<p>Colocando em ordem de importância, eu resumiria da seguinte forma:</p>
<ul>
<li>Custom Elements - permite criar tags diferente dos canônicos como &ldquo;body&rdquo;, &ldquo;input&rdquo; e evitar o classes-hell ou div-hell onde tudo é um punhado de divs dentro de divs criando os horrendos macarrões de tags. A idéia é encapsular tudo dentro de tags simples e mais fáceis de manusear.</li>
<li>Shadow DOM - para o fator encapsulamento, precisamos primeiro conseguir eliminar o sangramento de estilos e comportamentos por tudo que existe numa página. Shadow DOM permite criar trechos de nós de elementos de DOM que são independentes e isolados entre si, onde o estilo de um trecho não interfere com outro. Somente o Chrome e Firefox mais novos suportam isso. WebKit/Safari deixou de suportar a implementação incompleta que tinham (mas talvez voltem) e o IE, para variar, nunca nem tentou implementar isso. Para funcionar na maioria dos navegadores existem projetos como o Polymer/Platform e X-Tag para servir como &ldquo;polyfill&rdquo; para navegadores legados.</li>
<li>Templates - como o nome diz, para criar componentes dinâmicos, é importante ter trechos de DOM inertes, diferentes de somente uma string, para conseguir manipular e instanciar conforme necessário. Por exemplo, linhas de uma tabela dinâmica. Existem várias bibliotecas de templates, como Mustache, mas para Web Components funcionar, era importante ter um padrão.</li>
<li>HTML Imports - e uma vez tendo um trecho de DOM isolado, com estilos e comportamentos independentes de outros componentes, agora precisamos empacotar isso. E para isso servem esses imports que basicamente é um arquivo HTML que declara os arquivos CSS e Javascript que ele depende, e o HTML Import sabe como carregar tudo de uma vez. Aqui fica uma das minhas maiores preocupações - que vou tentar explicar mais para o fim.</li>
</ul>
<p>Existem já dezenas de artigos e documentação de como é um Web Component, como construir um do zero e onde encontrar componentes. Não é foco deste artigo explicar os detalhes técnicos de um Web Component.</p>
<p>Por si só, devo dizer que são tecnologias interessantes que todo bom nerd gosta de brincar. Porém não deveria ser novidade que só porque alguma coisa é tecnicamente interessante, por si só, não garante absolutamente nada. A principal pergunta é: <em>&ldquo;o que eu ganho com isso?&rdquo;</em> Antes de responder a essa pergunta, toda tecnologia é somente uma solução procurando um problema.</p>
<p>Infelizmente nem eu, nem ninguém, temos como prever o futuro, então neste artigo quero avaliar qualquer proposta baseado no que temos no presente, relembrar o passado e o que isso pode representar no curto prazo. O longo prazo deixo para os profetas e quem gosta de apostar em loteria. Então vou tentar quebrar alguns dos grandes problemas do desenvolvimento Web que temos hoje.</p>
<h2>O Problema do HTML/CSS macarrônicos<span class="hx:absolute hx:-mt-20" id="o-problema-do-htmlcss-macarrônicos"></span>
    <a href="#o-problema-do-htmlcss-macarr%c3%b4nicos" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A forma de desenhar uma página Web mudou bastante nos últimos 20 anos. As tecnologias básicas ainda são as mesmas: HTML 5 para estruturar um documento. CSS 3 para estilizar os elementos nesse documento. Javascript para adicionar comportamento. Acho que a esta altura todos já sabemos que devemos separar estilização/apresentação da estrutura e conteúdo. Também sabemos que não devemos nem usar tabelas para tudo e nem usar divs com float para tudo.</p>
<p>Passamos pela era da <a href="http://semanticweb.org/wiki/Main_Page"target="_blank" rel="noopener">Web Semântica</a> e <a href="http://www.smashingmagazine.com/2011/01/12/guidelines-for-responsive-web-design/"target="_blank" rel="noopener">Web Responsiva</a>. Quando os navegadores implementaram tables no <a href="http://www.barrypearson.co.uk/articles/layout_tables/history.htm"target="_blank" rel="noopener">Netscape Navigator 1.1 e IE 2.0 em 1995 e 1996</a> ainda não se falava em acessibilidade e semântica apareceriam a partir de 1997 com o HTML 3.2. Estávamos criando a Web e usando ao mesmo tempo, como hoje as implementações apareciam antes de se ter uma especificação mais geral. A W3C é lenta não é de hoje.</p>
<p>Acho que a evangelização de Responsive Web também merece um &ldquo;rant&rdquo; mas fica para outro artigo.</p>
<p>Mesmo assim ainda somos reféns de códigos como deste exemplo de tabs do framework <a href="http://getbootstrap.com/components/#input-groups"target="_blank" rel="noopener">Bootstrap</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-HTML" data-lang="HTML"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">ul</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;nav nav-tabs&#34;</span> <span class="na">role</span><span class="o">=</span><span class="s">&#34;tablist&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">li</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;active&#34;</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#&#34;</span><span class="p">&gt;</span>Home<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#&#34;</span><span class="p">&gt;</span>Profile<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#&#34;</span><span class="p">&gt;</span>Messages<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&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>E vejamos um trecho para tabs mas usando um web component do novíssimo <a href="http://www.polymer-project.org/components/paper-elements/demo.html#paper-tabs"target="_blank" rel="noopener">Polymer/Paper</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-HTML" data-lang="HTML"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">paper-tabs</span> <span class="na">selected</span><span class="o">=</span><span class="s">&#34;0&#34;</span> <span class="na">selectedindex</span><span class="o">=</span><span class="s">&#34;0&#34;</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;paper_tabs&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">paper-tab</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;tab_home&#34;</span> <span class="na">active</span><span class="p">&gt;</span>Home<span class="p">&lt;/</span><span class="nt">paper-tab</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">paper-tab</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;tab_profile&#34;</span><span class="p">&gt;</span>Profile<span class="p">&lt;/</span><span class="nt">paper-tab</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">paper-tab</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;tab_messages&#34;</span><span class="p">&gt;</span>Messages<span class="p">&lt;/</span><span class="nt">paper-tab</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">paper-tabs</span><span class="p">&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 é o resultado da primeira tecnologia de Web Components: <a href="http://www.html5rocks.com/en/tutorials/webcomponents/customelements/"target="_blank" rel="noopener">Custom Elements</a>. De fato isso acrescenta uma clareza enorme. No primeiro exemplo o que temos é:</p>
<blockquote>"Uma lista não ordenada com classes nav e nav-tags e dentro delas itens de lista."</blockquote>
<p>Se você souber o que são as classes &ldquo;nav&rdquo; e &ldquo;nav-tabs&rdquo; vamos saber que se trata de uma lista de tabs. No caso do exemplo do Paper podemos ler claramente:</p>
<blockquote>"Um conjunto de tabs, implementado com Paper."</blockquote>
<p>Isto é um exemplo pequeno mas imaginando como páginas Web são complexas, tirar a ambiguidade de para que servem cada conjunto de tags numa árvore infinita de tags é muito importante. Perdemos muito tempo em tentativa e erro de retirar pedaços e tentando inserir outros pedaços no meio da árvore de HTML e decifrando nomes de classes CSS.</p>
<p>Na prática, sem ter Custom Elements para tornar as tags mais amigáveis, outra forma é encapsular a inicialização usando um marcador simples numa tag HTML (como uma classe CSS ou ID) e ativar o resto via Javascript. É como todo plugin JQuery funciona. Um exemplo simples é como encapsulamos o Google Maps com o plugin <a href="http://gmap3.net/en/catalog/9-map/map-32"target="_blank" rel="noopener">Gmap3</a> do JQuery:</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-HTML" data-lang="HTML"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;my-map&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nx">$</span><span class="p">(</span><span class="s2">&#34;#my-map&#34;</span><span class="p">).</span><span class="nx">gmap3</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nx">map</span><span class="o">:</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">address</span><span class="o">:</span><span class="s2">&#34;Cupertino, CA&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">options</span><span class="o">:</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">zoom</span><span class="o">:</span><span class="mi">4</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">mapTypeId</span><span class="o">:</span> <span class="nx">google</span><span class="p">.</span><span class="nx">maps</span><span class="p">.</span><span class="nx">MapTypeId</span><span class="p">.</span><span class="nx">SATELLITE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">mapTypeControl</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">mapTypeControlOptions</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">style</span><span class="o">:</span> <span class="nx">google</span><span class="p">.</span><span class="nx">maps</span><span class="p">.</span><span class="nx">MapTypeControlStyle</span><span class="p">.</span><span class="nx">DROPDOWN_MENU</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nx">navigationControl</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">scrollwheel</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">streetViewControl</span><span class="o">:</span> <span class="kc">true</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="p">&lt;/</span><span class="nt">script</span><span class="p">&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>E outro exemplo com a alternativa com o <a href="http://polymerlabs.github.io/google-map/components/google-map/"target="_blank" rel="noopener">Web Component Google-Map</a> (lembrando que esta versão é mais simples do que o plugin Gmap3):</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-HTML" data-lang="HTML"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">google-map</span> <span class="na">latitude</span><span class="o">=</span><span class="s">&#34;37.77493&#34;</span> <span class="na">longitude</span><span class="o">=</span><span class="s">&#34;-122.41942&#34;</span> <span class="na">fitToMarkers</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">google-map-marker</span> <span class="na">latitude</span><span class="o">=</span><span class="s">&#34;37.779&#34;</span> <span class="na">longitude</span><span class="o">=</span><span class="s">&#34;-122.3892&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">draggable</span><span class="o">=</span><span class="s">&#34;true&#34;</span> <span class="na">title</span><span class="o">=</span><span class="s">&#34;Go Giants!&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">google-map-marker</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">google-map-marker</span> <span class="na">latitude</span><span class="o">=</span><span class="s">&#34;37.777&#34;</span> <span class="na">longitude</span><span class="o">=</span><span class="s">&#34;-122.38911&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">google-map-marker</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">google-map</span><span class="p">&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>Estamos movendo parte do que faríamos via Javascript de volta ao HTML. Mas somente isso não é suficiente e já sabemos disso. A tecnologia de &ldquo;Cascading Style Sheet versão 3&rdquo;, comumente conhecida como CSS 3 até hoje permanece arcaica. Felizmente inventamos uma forma de tornar os malditos macarrões de CSS gerenciáveis. Agradecimentos são devidos à <a href="http://www.hamptoncatlin.com"target="_blank" rel="noopener">Hampton Catlin</a>, um grande rubista que - em <strong>2007</strong> - inventou a <a href="http://sass-lang.com/"target="_blank" rel="noopener">linguagem de script SASS</a>, que é um &ldquo;super-set&rdquo; de CSS e gera CSS 3, adicionando variáveis, nesting, partials, import, mixins, extensão/herança, operadores.</p>
<p>SASS foi a primeira linguagem de pré-processamento que acelerou a idéia da possibilidade de novas formas de gerenciar assets Web como CSS. Depois dele vieram LESS e toda uma gama de frameworks e bibliotecas como <a href="http://singularity.gs/"target="_blank" rel="noopener">Singularity</a>, <a href="http://neat.bourbon.io/"target="_blank" rel="noopener">Bourbon Neat</a>, <a href="http://susy.oddbird.net/"target="_blank" rel="noopener">Susy</a>, <a href="http://foundation.zurb.com/"target="_blank" rel="noopener">Zurb Foundation</a>, <a href="http://compass-style.org/"target="_blank" rel="noopener">Compass</a>. Foi uma pena terem escrito o Twitter Bootstrap original em <a href="http://wordsbyf.at/2012/03/08/why-less/"target="_blank" rel="noopener">LESS em vez de SASS</a>, mas acho que o projeto <a href="http://libsass.org/"target="_blank" rel="noopener">LibSass</a> (SASS escrito em C) ainda não existia. Mas pelo próprio exemplo acima fica claro porque você deveria escolher outro framework em vez de Bootstrap.</p>
<p>Porém SASS não foi a primeira vez que alguém criou um pré-processador para a Web. Provavelmente a primeira foi a <a href="http://en.wikipedia.org/wiki/Wiki_markup"target="_blank" rel="noopener">Wiki Markup</a> implementada junto com o primeiro Wiki, o <a href="http://en.wikipedia.org/wiki/WikiWikiWeb"target="_blank" rel="noopener">WikiWikiWeb</a> de Ward Cunningham em 1995. Depois disso ainda tivemos o MediaWiki da Wikipedia até chegarmos atualmente a pré-processadores de HTML como Markdown, que praticamente usamos sem pensar em comentários de Github.</p>
<p>No mundo Rails já tentamos melhorar o HTML também, com pré-processadores como o HAML (do próprio Catlin) ou outros mais recentes como <a href="http://slim-lang.com/"target="_blank" rel="noopener">Slim</a>. Mas nesse caso ainda não estamos melhorando o HTML tão drasticamente como SASS faz com CSS, por isso ainda não há uma boa alternativa e Custom Elements é o mais próximo de uma boa alternativa neste momento.</p>
<p>Estou trazendo estes pontos porque Web Components não declara nenhum pré-processador de CSS como padrão, por convenção. Assumimos CSS 3 puro. Não é um problema, mas se estamos falando de reuso, atualmente a melhor forma é usando mixins de SASS encapsuladas em bibliotecas como Compass ou Bourbon.</p>
<h2>O Problema do Sangramento de Estilos e Comportamentos<span class="hx:absolute hx:-mt-20" id="o-problema-do-sangramento-de-estilos-e-comportamentos"></span>
    <a href="#o-problema-do-sangramento-de-estilos-e-comportamentos" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Este é um dos aspectos mais irritantes de desenvolvimento front-end depois da compatibilidade de navegadores: como organizar as milhares de linhas de CSS? (seja ele puro, escrito em SASS ou LESS).</p>
<p>Antes de continuar, se nunca ouviu falar de <a href="http://markdaggett.com/blog/2011/12/04/css-anti-patterns/"target="_blank" rel="noopener">CSS Anti-patterns</a>, leia a respeito antes. E também que todos já pelo menos ouviram falar nas diversas metodologias para organizar CSS (<a href="http://www.smashingmagazine.com/2011/12/12/an-introduction-to-object-oriented-css-oocss/"target="_blank" rel="noopener">OOCss</a> , <a href="http://www.smashingmagazine.com/2012/04/20/decoupling-html-from-css/"target="_blank" rel="noopener">SMACSS</a>, <a href="http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/"target="_blank" rel="noopener">BEM</a>). Eu honestamente não usaria ACSS e me foram dados bons argumentos para preferir BEM.</p>
<p>Essa introdução foi somente porque também não sou ainda um bom desenvolvedor front-end mas até aqui é o mínimo para começar. Vamos assumir que já passamos desses pontos também. Meu raciocínio começa pelo seguinte caso de uso: <em>&ldquo;quero criar CSS para um elemento visual, composto de diversos tags, e que não deve se deformar aleatoriamente por estilo de fora e nem deformar outros elementos ao redor.&rdquo;</em> Seria o caso de um Widget ou o que poderia ser um &ldquo;Web Component&rdquo;, ao pé-da-letra.</p>
<p>Passamos anos fazendo <a href="http://codeutopia.net/blog/2012/05/26/best-practices-for-building-embeddable-widgets/"target="_blank" rel="noopener">Widgets</a>. As duas grandes formas são usando um iframe com <a href="http://blog.teamtreehouse.com/cross-domain-messaging-with-postmessage"target="_blank" rel="noopener">postMessage()</a> ou zerar os estilos de um trecho do DOM com <a href="https://github.com/premasagar/cleanslate"target="_blank" rel="noopener">Cleanslate</a> e garantir que você está isolando seu CSS e Javascript usando namespaces e torcendo pra acidentalmente não sobrar nada global no meio do caminho ou outro desenvolvedor sobrescrever o que você fez.</p>
<p>Isso mudou em 2012 quando surgiu a implementação de <a href="http://blog.teamtreehouse.com/working-with-shadow-dom"target="_blank" rel="noopener">Shadow DOM</a>. Os novos elementos do HTML 5 como &ldquo;audio&rdquo; ou &ldquo;video&rdquo; ou &ldquo;range&rdquo; foram criados dessa forma em muitos navegadores. Em resumo mencionamos que uma página Web é uma árvore de tags, essa árvore que chamamos de Document Object Model ou &ldquo;DOM&rdquo;. Imagine se pudéssemos isolar um trecho de nós, uma sub-árvore, que não fosse influenciada pelos estilos externos e cujos estilos internos não &ldquo;vazassem&rdquo; para fora. Para isso criamos um novo elemento, um &ldquo;ShadowRoot&rdquo; que delimita uma sub-árvore isolada e independente e que, para todos os propósitos, não aparece no DOM principal, mas tem sua própria raíz de DOM isolada. Esse é o conceito.</p>
<p>No <a href="http://caniuse.com/shadowdom"target="_blank" rel="noopener">momento deste artigo</a>, somente o Chrome a partir da versão 25 (hoje no 35), o Firefox a partir da versão 31 (hoje no 30) e Ópera versão 15 (hoje no 22) suportam Shadow DOM. A Apple <a href="http://trac.webkit.org/changeset/164131"target="_blank" rel="noopener">retirou o suporte do WebKit</a> em fevereiro. A Microsoft sequer <a href="http://connect.microsoft.com/IE/feedback/details/793965/ie11-feature-request-support-for-shadow-dom"target="_blank" rel="noopener">considerou começar a implementar</a> ainda.</p>
<p><a href="http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom/"target="_blank" rel="noopener">Shadow DOM</a> não é obrigatório para criar Custom Elements, mas é um recurso interessante e importante para atingir o objetivo de componentes Web independentes e isolados que não precisem recorrer a recursos como reiniciar o CSS de uma sub-árvore com artifícios como Cleanslate. De qualquer forma, seja via as convenções listadas acima e outras, ou seja via Shadow DOM, hoje temos como criar um nível razoável de isolamento e facilidade de manutenção. O problema é que isso não é inerente a HTML 5 e CSS 3, mas uma camada acima que não tem um consenso geral ainda.</p>
<h2>O Problema de Reusar Elementos<span class="hx:absolute hx:-mt-20" id="o-problema-de-reusar-elementos"></span>
    <a href="#o-problema-de-reusar-elementos" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Num mundo onde fazíamos praticamente tudo via server-side, esse era um problema praticamente resolvido. Seja com Rails e ERB/HAML/Slim, seja com templates de Django, seja com Blade em Laravel, seja com Razor em Asp.Net. Porém hoje temos aplicações mistas, onde deferimos muita lógica para a camada do navegador usando mais Javascript. Em vez de devolver pedaços prontos de HTML, agora devolvemos dados semi-estruturados em JSON e deixamos o Javascript criar os elementos no DOM.</p>
<p>Significa que no navegador temos um conjunto de dados e precisamos de uma forma de declarar os elementos de DOM onde vamos aplicar esses dados antes de instanciar o trecho no DOM. Do mundo Ruby surgiu uma das formas mais populares, <a href="http://mustache.github.io/"target="_blank" rel="noopener">Mustache</a>, criado pelo co-fundador do Github, <a href="https://github.com/defunkt"target="_blank" rel="noopener">Chris Wanstrath</a>. Depois outro rubista, <a href="https://github.com/wycats"target="_blank" rel="noopener">Yehuda Katz</a>, trouxe outra alternativa com o <a href="http://handlebarsjs.com/"target="_blank" rel="noopener">Handlebars</a>. A idéia geral é criar um elemento &ldquo;script&rdquo; e dentro dela colocar templates usando a notação do Mustache. Então um Javascript pode pegar o conteúdo string dessa tag script e criar o elemento no DOM depois de mesclar com os dados que quiser.</p>
<p>Está claro que estamos fazendo uma &ldquo;gambiarra&rdquo; elegante. A parte não tão elegante é ter que injetar HTML usando &ldquo;innerHTML&rdquo;, mas funciona muito bem. O outro problema é controlar manualmente coisas como imagens e javascripts que um elemento pode precisar mas antes dele ser usado, ou seja, talvez sem necessidade. Para isso precisamos controlar via Javascript.</p>
<p>Na definição de Web Components foi criado um novo elemento para resolver alguns desses problemas, o <a href="http://www.html5rocks.com/en/tutorials/webcomponents/template/"target="_blank" rel="noopener">&ldquo;template&rdquo;</a>. Sua definição é bem recente, de fevereiro de 2014. Serve basicamente o mesmo propósito de colocar um template dentro de uma tag &ldquo;script&rdquo; mas em vez de ser somente um string injetável ele realmente é uma sub-árvore de DOM &ldquo;inerte&rdquo;, que podemos manipular os nós via Javascript antes de ativá-lo ao inserir no DOM principal.</p>
<p>Por não ser mais um trecho de texto, perdemos a função do Mustache e do Handlebar de facilmente mesclar dados e inclusive ter alguma lógica como condicionais e loops. Em vez disso precisamos manipular os nós via javascript, fazendo coisas como:</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-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">t</span><span class="p">.</span><span class="nx">content</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;img&#39;</span><span class="p">).</span><span class="nx">src</span> <span class="o">=</span> <span class="s1">&#39;logo.png&#39;</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>Em vez de ter:</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-HTML" data-lang="HTML"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;{{minha-imagem}}&#34;</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;great image&#34;</span><span class="p">&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>Neste caso de templates eu diria que demos 2 passos pra trás e 1 para frente. Provavelmente dentro de um &ldquo;HTML template&rdquo; vamos precisar ter um Mustache para completar o processo? Admito que não acompanho a especificação mas pelo pouco que vi até agora não me parece que a especificação W3C de HTML template resolve o problema que bibliotecas como Mustache, Handlebars ou <a href="https://github.com/tildeio/htmlbars"target="_blank" rel="noopener">HTMLBars</a> resolvem hoje.</p>
<p>E não estou nem mencionando sobre Binding. Pelo que entendi isso não está na especificação, mas implementado em projetos como o <a href="http://mdv.googlecode.com/svn/trunk/docs/design_intro.html"target="_blank" rel="noopener">Google MDV</a> que agora está integrado ao <a href="http://www.polymer-project.org/docs/polymer/databinding.html"target="_blank" rel="noopener">Data Binding e Event Mapping do Polymer</a> como <a href="https://github.com/Polymer/TemplateBinding"target="_blank" rel="noopener">TemplateBinding</a>.</p>
<h2>O Problema de Reusar &ldquo;Componentes&rdquo;<span class="hx:absolute hx:-mt-20" id="o-problema-de-reusar-componentes"></span>
    <a href="#o-problema-de-reusar-componentes" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Definindo &ldquo;Web Components&rdquo; como, um conjunto de HTML, CSS, Javascript que funciona em independência e isolamento, finalmente sobra o problema de &ldquo;empacotar&rdquo; esse conjunto. E agora entra a <a href="http://w3c.github.io/webcomponents/spec/imports/"target="_blank" rel="noopener">nova especificação de HTML Imports</a> de Julho de 2014.</p>
<p>Na prática, um HTML Import é nada mais do que um arquivo HTML &ldquo;normal&rdquo; com links para outros arquivos como stylesheets, imagens, javascripts e outros assets. Novamente, não vou descrever todos os detalhes e para aprender mais leia o artigo <a href="http://www.html5rocks.com/en/tutorials/webcomponents/imports/"target="_blank" rel="noopener">Why imports?</a>.</p>
<p>Atualmente, se precisamos usar algum conjunto desses, como um componente de Bootstrap, por exemplo, precisamos manualmente linkar para os stylesheets na ordem correta e fazer algo como:</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-HTML" data-lang="HTML"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;bootstrap.css&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;fonts.css&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  ...
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  ...
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;jquery.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;bootstrap.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;bootstrap-tooltip.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;bootstrap-dropdown.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&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>Com HTML Imports podemos fazer simplesmente:</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-HTML" data-lang="HTML"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;import&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;bootstrap.html&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&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>O problema imediato de algo como Web Components é que qualquer pequena página pode ter rapidamente dezenas de Imports, cada import requerendo outros arquivos e potencialmente triplicando ou mais a quantidade de chamadas pela rede. Quando tivermos algo melhor como HTTP 2.0 ou outro processo de <a href="http://stackoverflow.com/questions/10480122/difference-between-http-pipeling-and-http-multiplexing-with-spdy"target="_blank" rel="noopener">pipelining e multiplexing como com o protocolo SPDY</a> talvez não tenhamos que nos preocupar com isso, mas com o HTTP 1.1 que temos hoje precisamos ainda pensar em como economizar ao máximo chamadas de rede para pedir assets.</p>
<p>O mundo Rails trouxe pela primeira vez uma implementação de um processo completo e automatizado para concatenar e minificar stylesheets, javascript e sprites com o <a href="https://github.com/sstephenson/sprockets"target="_blank" rel="noopener">Sprockets</a> que <a href="https://github.com/sstephenson"target="_blank" rel="noopener">Sam Stephenson</a> - também criador do Prototype - iniciou em fevereiro de 2009 (obviamente os componentes que podem ser usados pelo Sprockets, como compressor, já existiam). Finalmente o Sprockets foi adicionado ao Rails 3.1 lançado em Agosto de 2011 como <a href="http://www.akitaonrails.com/2012/07/01/asset-pipeline-para-iniciantes"target="_blank" rel="noopener">&ldquo;Asset Pipeline&rdquo;</a>.</p>
<p>Hoje em dia o Sprockets continua sendo uma das opções mais completas e outros frameworks correram para criar suas próprias versões, como o <a href="https://github.com/d-i/half-pipe"target="_blank" rel="noopener">&ldquo;connect-assets&rdquo;</a> para Node.js, diversas receitas para Grunt e <a href="http://blog.carbonfive.com/2014/05/05/roll-your-own-asset-pipeline-with-gulp/"target="_blank" rel="noopener">Gulp</a>) e opções para <a href="http://grails.org/plugin/asset-pipeline"target="_blank" rel="noopener">Grails</a>, <a href="http://getcassette.net/"target="_blank" rel="noopener">Cassette/ASP.NET MVC</a>, <a href="https://github.com/cyberdelia/django-pipeline"target="_blank" rel="noopener">Pipeline Django</a>.</p>
<p>No mundo de Web Components foi imediatamente criado o projeto <a href="http://www.polymer-project.org/articles/concatenating-web-components.html"target="_blank" rel="noopener">Vulcanize</a> ou no caso do mundo Rails podemos adicionar o processo ao próprio Sprockets com o projeto <a href="http://www.akitaonrails.com/2014/06/29/usando-polymer-com-rails"target="_blank" rel="noopener">Emcee</a>, que foi o tema do meu post anterior. A idéia é pegar os HTML imports e concatená-los num único arquivo, incluindo tornar os links para stylesheets e javascripts externos como trechos inline num único HTML. Nem o Vulcanize e nem o Emcee atualmente minificam seus conteúdos, no entanto.</p>
<p>Note que imports é apenas uma parte do problema. Nem vamos entrar no problema de gerenciamento de versões (problema que projetos como Bower, NPM, Bundler no Ruby, PHP Composer, NuGet, Gradle, Pip, etc foram criados para resolver). O que acontece com componentes que dependem de versões específicas de outro componente? E bibliotecas internas como JQuery que um componente pode precisar? Ainda não sei o que acontece nesses casos.</p>
<h2>Web Components é uma &ldquo;Revolução&rdquo;?<span class="hx:absolute hx:-mt-20" id="web-components-é-uma-revolução"></span>
    <a href="#web-components-%c3%a9-uma-revolu%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Tudo isso dito, vamos revisar:</p>
<ul>
<li>Podemos criar conjuntos de tags isolados com Shadow DOM, com seus próprios estilos e comportamentos que não vazam e não automaticamente herdam estilos e comportamentos do DOM principal, a técnica é usar Shadow DOM (ou uma das formas antigas com Cleanslate, criação de elementos com Javascript, IFrame)</li>
<li>Podemos definir trechos inertes de HTML que podem ser instanciados via Javascript para dentro do DOM. Com projetos como TemplateBinding recuperamos algo como HTMLBars e podemos mesclar dados com o template. A técnica é usar HTML template e Polymer (ou uma das formas antigas com Mustache, Handlebars, HTMLBars).</li>
<li>Uma vez definido nosso &ldquo;widget&rdquo; ou &ldquo;componente&rdquo;, podemos encapsulá-lo dentro de uma embalagem mais amigável usando Custom Elements (ou uma das formas antigas como plugins de JQuery fazem com Javascript para encapsular).</li>
<li>E uma vez montado o Widget/Componente podemos empacotar tudo num único HTML Import para podermos resusar facilmente dentro de outros HTML (em vez da forma antiga de declarar manualmente os arquivos de dependência)</li>
</ul>
<p>Vocês devem ter notado que do ponto de vista de um desenvolvedor que apenas consome tais componentes/widgets, o processo em si é uma melhoria mas não é tão diferente quanto utilizar plugins de JQuery. De fato é uma passo adiante, oficializando o que antes era &ldquo;gambiarra&rdquo; em alguma coisa um pouco melhor (Custom Elements em vez de Javascript, Shadow DOM em vez de IFrame, HTML Template em vez de string em tag Script).</p>
<p>E aqui vem o maior desafio ao conjunto de tecnologias de Web Components: ele é definitivamente melhor. Só que não é disruptivo. Ao usuário final, o que ele &ldquo;vê&rdquo; e &ldquo;interage&rdquo; numa página web no navegador será exatamente idêntico. Significa que o usuário final tem muito pouco a se beneficiar da tecnologia. Portanto ele não é o público-alvo.</p>
<p>O público-alvo é o desenvolvedor front-end que, em vez de buscar um plugin JQuery - por exemplo - vai buscar um Web Component e a forma de utilizá-los não será muito diferente. Como ele não tem a motivação de melhorar muita coisa a seu público-alvo, ele tem muito pouco incentivo para mudar sua forma de trabalho.</p>
<p>O raciocínio é simples: quero colocar um Carrossel na minha página. Existem centenas, <a href="http://www.jssor.com/"target="_blank" rel="noopener">Jssor Slider</a>, <a href="http://sorgalla.com/jcarousel/"target="_blank" rel="noopener">jCarousel</a>, <a href="http://kenwheeler.github.io/slick/"target="_blank" rel="noopener">Slick - the last carousel you&rsquo;ll ever need</a>. E para Web Components? Pelo menos no momento deste artigo só encontrei o <a href="https://github.com/addyosmani/polymer-ui-carousel"target="_blank" rel="noopener">Polymer-ui-carousel</a>, que não chega aos pés dos antecessores. Qual você acha que o desenvolvedor front-end vai escolher?</p>
<p>Existem dois repositórios de componentes públicos (aliás, não entendi porque existem dois, um projeto como esse não ganha nada nascendo já fragmentado): o <a href="http://component.kitchen/"target="_blank" rel="noopener">Component Kitchen</a> com cerca de 276 componentes e o <a href="http://customelements.io/"target="_blank" rel="noopener">Custom Elements</a> com 327 componentes. Como todo repositório inicial, a maioria deles não é de grande serventia. São mais demonstrações e provas de conceito. Sinceramente, alguém se dar ao trabalho para fazer um componente de Pokémon tem que ser só um tech-demo. É inútil. E você pode contar com coisas redundantes e/ou desnecessárias como botões de Facebook, gravatar, botão de Github, coisas que um <a href="http://www.addthis.com/"target="_blank" rel="noopener">AddThis</a> já tem resolvido. Claro, não quero ser injusto e dizer que Web Components não tem utilidade só porque a primeira geração de componentes não é interessante. Essas coisas levam tempo para amadurecer, como qualquer tecnologia.</p>
<p>Para que a estratégia funcione ou a tecnologia em si é <string>disruptiva</string> - o que acabamos de ver que não é, é apenas <strong>evolutiva</strong> -, ou é necessário um pacote completo para ser candidato a <a href="http://en.wikipedia.org/wiki/Killer_application"target="_blank" rel="noopener">&ldquo;killer-app&rdquo;</a>. Voltamos ao que disse antes sobre uma solução procurando um problema. Sem um verdadeiro killer-app-blockbuster é muito difícil conseguir tração e coesão de um ecossistema. No caso do Ruby isso foi o Rails. No caso do Go parece que é o Docker. No caso do Node foi o halo-effect do Google Chrome evoluindo o V8.</p>
<p>Para isso o Google criou o <a href="http://www.polymer-project.org/components/paper-elements/demo.html#core-toolbar"target="_blank" rel="noopener">Polymer/Paper</a> que define um conjunto coeso e visualmente integrado (via filosofia gráfica e interativa do <a href="http://www.google.com/design/"target="_blank" rel="noopener">Material Design</a> que, assumindo que &ldquo;pegue&rdquo;, também tem a vantagem de dar ao usuário final um &ldquo;eye-candy&rdquo; que justifique sua adoção). É uma proposição &ldquo;tudo ou nada&rdquo;, embora você possa pegar pedaços dela, a intenção é fazer aplicações/sites usando inteiramente o Polymer.</p>
<p>O projeto <a href="http://www.polymer-project.org/"target="_blank" rel="noopener">Polymer</a> é ambicioso e somente uma corporação como o Google poderia fazer algo assim tão rápido (dado que metade das especificações de tecnologia de Web Components como HTML Imports e HTML template são deste ano de 2014). Ao contrário do que deveria parecer, a arquitetura não é simples e com o pouco suporte de navegadores a única estratégia é criar Polyfills, implementações (&ldquo;quasi&rdquo;-completas) para tudo que navegadores &ldquo;legados&rdquo; mas que muita gente usa como IE abaixo de 10, Safari e Mobile Safari. Para isso serve o <a href="http://www.polymer-project.org/docs/start/platform.html"target="_blank" rel="noopener">Polymer/Platform</a>.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/413/polymer-architecture.png" alt="Arquitetura do Polymer"  loading="lazy" /></p>
<p>Só que já passamos do ponto de somente usar plugins JQuery, agora migramos para a era dos one-page-apps via Javascript que consome endpoints REST que retornam JSON. Estamos falando somente do ponto de vista de componentes, mas uma aplicação é maior do que isso. Para quem vem do mundo ASP.NET MVC, Rails, Django, falamos muito sobre o conceito &ldquo;Web MVC&rdquo; (Model-View-Controller) ou mais corretamente, <a href="http://en.wikipedia.org/wiki/Model_2"target="_blank" rel="noopener">MVC Model-2</a> da Sun, definido para J2EE.</p>
<p>Muitos nem sabem que usam outro modelo, o <a href="http://en.wikipedia.org/wiki/Model%e2%80%93view%e2%80%93presenter"target="_blank" rel="noopener">MVP</a> (Model-View-Presenter) presente em ASP.NET (antigo), Window.Forms, <a href="http://www.gwtproject.org/?csw=1"target="_blank" rel="noopener">GWT</a> (Google Web Toolkit, lembram? Também do Google!). E, finalmente, também da Microsoft chegamos ao <a href="http://en.wikipedia.org/wiki/Model_View_ViewModel"target="_blank" rel="noopener">MVVM</a> (Model View View Model), uma evolução de MVP com MVC2, mais conhecido pelos seus conceitos de Data Binding 2-way de elementos do DOM com models puxados via Javascript. Agora temos a categoria que vocês já devem ter conhecido com <a href="http://knockoutjs.com/"target="_blank" rel="noopener">Knockout.js</a>, <a href="https://angularjs.org/"target="_blank" rel="noopener">Angular.js</a>, <a href="http://emberjs.com/"target="_blank" rel="noopener">Ember</a>. Estamos falando de mais do que o jeito MVP de arrastar componentes a um canvas e adicionar eventos de callback, mas um framework completo que lida com Models, Views, Rotas.</p>
<p>O Yehuda Katz escreveu sobre <a href="https://gist.github.com/wycats/9144666b0c606d1838be"target="_blank" rel="noopener">integrar o Ember com Web Components</a> em Maio e ele explicita muitos detalhes que vale a pena ler a respeito. Segundo esta <a href="https://groups.google.com/forum/#!msg/polymer-dev/4RSYaKmbtEk/uYnY3900wpIJ"target="_blank" rel="noopener">thread no Google Groups</a>, parece que o projeto Angular 2.0 deve também se integrar com Web Components e Polymer. Quando estes projetos e outros frameworks se unirem ao esforço Polymer provavelmente vamos ver o real valor dos Web Components.</p>
<p>Polymer é o projeto principal. Web Components é o conjunto de mecanismos, o meio para atingir esse objetivo. E é um problema clássico de ovo e galinha: os navegadores diferentes de Chrome precisam se equiparar e seguir a vontade do Google. Os polyfills, via Polymer/Platform e <a href="http://www.x-tags.org/blog"target="_blank" rel="noopener">Mozilla X-Tag</a> vão suprir o que falta (Mutation Observers, Shadow DOM, etc) como o JQuery fez no passado. Mas para os navegadores tracionarem nessa direção os desenvolvedores precisam entrar no barco, e para isso somente um pacote completo como Polymer/Paper e, se possível, aliado a frameworks como Ember e Angular podem fazer isso acontecer, e não pequenos componentes-demonstração.</p>
<h2>A História se Repete<span class="hx:absolute hx:-mt-20" id="a-história-se-repete"></span>
    <a href="#a-hist%c3%b3ria-se-repete" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Já vimos essa história acontecer antes. Durante o <a href="http://en.wikipedia.org/wiki/Browser_wars"target="_blank" rel="noopener">Browser Wars</a> muitos apenas vão se lembrar que a Microsoft conspirou ativamente para destruir a Netscape, culminando no processo que quase quebrou a Microsoft em 3 no ano 2000 e a consequência final que foi a sobra do amaldiçoado Internet Explorer 6 que, apesar de ter sido um excelente navegador em sua era, ele não deveria nunca ter durado mais de uma década em uso.</p>
<p>Muitos talvez não lembrem que isso também gerou muitas novas tecnologias, para listar algumas:</p>
<ul>
<li>O Internet Explorer 3.0 foi o primeiro navegador a implementar o CSS 1.</li>
<li>O Internet Explorer 4.0 foi quem expôs o DOM e criou o DHTML, Dynamic HTML, com Dynamic Styling e manipulação de elementos do DOM via Javascript</li>
<li>O Internet Explorer 5.0 foi quem trouxe a semente do Ajax com o componente proprietário XMLHTTP e a técnica de buscar conteúdo de forma assíncrona com iframes usada no Outlook Web Access, dando origem ao XMLHttpRequest</li>
</ul>
<p>O período negro pós-2000 da Microsoft culminou no fracasso do Windows Vista e consequente sobrevida à la The Walking Dead tanto do Windows XP quanto do IE 6. O Google pegou o bastão de onde a Microsoft largou. O Core Business do Google é o AdWords. Controlar as plataformas Web faz parte da estratégia e o navegador continua sendo importante, porque ele é o novo sistema operacional desta geração, seja desktop ou mobile. Esqueçam Windows vs OS X vs GNU/Linux.</p>
<p>Outra tecnologia Microsoft que foi esquecida foi a primeira verdadeira tentativa de componentizar a Web. Foi com <a href="http://www.w3.org/TR/NOTE-HTMLComponents"target="_blank" rel="noopener">HTML Components</a> do fim de 1998. Nessa época ainda estávamos tentando dominar drag-and-drop de elementos com DHTML e começando a brincar com as possibilidades de fazer requisições sem precisar recarregar a página.</p>
<p>Se olhar esta página de <a href="http://bit.ly/1jV3VpJ"target="_blank" rel="noopener">APIs Legadas</a> da Microsoft teremos:</p>
<ul>
<li>Legacy DOM - substituída, claro, pelo DOM mais moderno</li>
<li>IE Architecture/Active X - substituído por Silverlight, Flash, ainda Applets e widgets</li>
<li>Browser Extensions - ainda temos extensions mas nenhum padrão</li>
<li>DHTML Behaviors e DHTML Data Binding - são os novos Web Components</li>
<li>Filters and Transitions - que até hoje temos browser-specific com modificadores como -moz ou -webkit, CSS 3</li>
<li>Vector Markup Language (VML) - que é o atual SVG e HTML 5 Canvas</li>
</ul>
<p>Não estou querendo dizer que é tudo mérito da Microsoft. A intenção é apenas explicar - para quem começou a usar a Internet na era pós-Browser Wars e pós-<a href="http://en.wikipedia.org/wiki/Dot-com_bubble"target="_blank" rel="noopener">Crash das Dot-coms</a> que Web Components não surgiu do nada, ela é o próximo passo lógico da evolução dos browsers e arquitetura Web.</p>
<p>Para o próprio Google essa não é a primeira vez. A primeira vez foi com <a href="http://en.wikipedia.org/wiki/Google_Web_Toolkit"target="_blank" rel="noopener">GWT</a> (Google Web Toolkit), que nasceu em 2006. Esse foi o auge dos frameworks MVC back-end que facilitavam desenvolvimento de aplicações Web usando Ajax. Foi a era pós-J2EE onde surgiram Rails, Django e outros Rails/Django-like como CakePHP, Grails.</p>
<p>Depois veio a era back-end assíncrono, desenterrando o problema <a href="http://www.kegel.com/c10k.html"target="_blank" rel="noopener">C10K de 1999 de Dan Kegel</a>, use case de Web Sockets (chat, APIs) que começa com Scala, vai para Node.js, chega em GoLang, Elixir. Novamente, não veio do nada, foi o próximo passo evolutivo depois do próprio Erlang, Twisted, Tornado, Java NIO/Netty, etc.</p>
<p>Em paralelo o Google investiu no Chrome e para isso precisou investir em Javascript e tornar o V8 algo realmente usável. Milhares de homens-hora e milhões de dólares depois, temos uma engine realmente performática. Com isso o mundo Javascript cresceu exponencialmente. Uma tecnologia que já se imaginava dominada na década de 90 ganhou nova roupagem e uma segunda chance. Com isso a arquitetura MVC2 evoluiu para um híbrido, com frameworks MVVM no client-side.</p>
<p>Portanto é natural evoluir do mindset GWT para Angular.js e eventualmente para Web Components/Polymer. E é onde estamos agora. Note que estamos sempre circulando os conceitos de MVC-clássico, MVC Model-2, MVP e agora MVVM, todos patterns originados do lendário MVC de <a href="http://heim.ifi.uio.no/~trygver/themes/mvc/mvc-index.html"target="_blank" rel="noopener">Trygve Reenskaug</a> do Smalltalk-80. Lembrando que já experimentamos programação orientada a componentes antes: foi a década de 90, que imaginou uma revolução ao usar componentes reusáveis para tornar o processo de programação meramente um jogo de arrastar blocos. Isso ficou conhecido como <a href="http://en.wikipedia.org/wiki/Rapid_application_development"target="_blank" rel="noopener">RAD</a> (Rapid Application Development), que deu origem a toolkits e IDEs. Foi o conceito que deu origem a PowerBuilder, Visual Basic, Delphi, Swing. E a evolução na direção de ferramentas CASE (Computer Aided Software Engineering). Achávamos que poderíamos criar diagramas (UML) e as ferramentas gerariam o código-fonte para nós. Já ouviu falar disso? Pois é, não deu muito certo. O Visual Studio de hoje <a href="http://msdn.microsoft.com/en-us/library/ff657795.aspx"target="_blank" rel="noopener">ainda tem suporte</a> a isso (yikes).</p>
<p>A diferença são as ondas da moda: na década de 90 ainda estávamos definindo o que era possível e, pelo bem ou pelo mal, o que a Microsoft inventou realmente foi revolucionário porque não havia precedente. A era pré-Bolha trouxe o MVC2 para o desenvolvimento Web com Web Objects e J2EE. O advento do Ajax e aplicações dinâmicas trouxe de GWT a Rails. E agora a era da Renascença do Javascript trouxe o MVVM e Web Components.</p>
<p>Não sei o que vem depois daqui, mas depois de organizar a informação histórica espero que mais alguém tenha insights do que vem pela frente. O que acham que Web Components realmente pode trazer que já não vimos com GWT e anteriores?</p>
]]></content:encoded><category>frontend</category><category>javascript</category><category>desenvolvimento-web</category></item><item><title>Rails Assets</title><link>https://akitaonrails.github.io/2013/12/13/rails-assets/</link><guid isPermaLink="true">https://akitaonrails.github.io/2013/12/13/rails-assets/</guid><pubDate>Fri, 13 Dec 2013 19:49:00 -0200</pubDate><description>&lt;p&gt;Ontem o &lt;a href="https://twitter.com/fgrehm"target="_blank" rel="noopener"&gt;@Fgrehm&lt;/a&gt; mandou no nosso Campfire sobre o projeto &lt;a href="https://rails-assets.org"target="_blank" rel="noopener"&gt;rails-assets.org&lt;/a&gt;. Ele acabou de ser lançado, portanto não pode ser considerado mais que um &amp;ldquo;beta&amp;rdquo;. Ela pode ser tanto a salvação da lavoura para front-ends em projetos Rails ou não, na prática ainda não sabemos.&lt;/p&gt;
&lt;p&gt;O &lt;a href="https://twitter.com/jcemer"target="_blank" rel="noopener"&gt;@jcemer&lt;/a&gt; publicou hoje um &lt;a href="http://jcemer.com/asset-pipeline-rails-assets-or-let-die.html"target="_blank" rel="noopener"&gt;ótimo post&lt;/a&gt; explicando isso do ponto de vista de um front-end full-time, colocando em perspectiva.&lt;/p&gt;
&lt;p&gt;Em resumo, o Rails trás um mecanismo fantástico chamado Asset Pipeline. Ele tem diversas funções, em particular otimizar os assets dos projetos Rails, compilando fontes de Sass ou Less para CSS. Depois concatenando todos os CSS, JS em um único arquivo. Depois minificando (com Uglify) para diminuir seu tamanho. Em particular com Sass ele ainda pode concatenar sprites em uma única imagem também. Enfim, o pacote completo para otimizar assets ao máximo.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Ontem o <a href="https://twitter.com/fgrehm"target="_blank" rel="noopener">@Fgrehm</a> mandou no nosso Campfire sobre o projeto <a href="https://rails-assets.org"target="_blank" rel="noopener">rails-assets.org</a>. Ele acabou de ser lançado, portanto não pode ser considerado mais que um &ldquo;beta&rdquo;. Ela pode ser tanto a salvação da lavoura para front-ends em projetos Rails ou não, na prática ainda não sabemos.</p>
<p>O <a href="https://twitter.com/jcemer"target="_blank" rel="noopener">@jcemer</a> publicou hoje um <a href="http://jcemer.com/asset-pipeline-rails-assets-or-let-die.html"target="_blank" rel="noopener">ótimo post</a> explicando isso do ponto de vista de um front-end full-time, colocando em perspectiva.</p>
<p>Em resumo, o Rails trás um mecanismo fantástico chamado Asset Pipeline. Ele tem diversas funções, em particular otimizar os assets dos projetos Rails, compilando fontes de Sass ou Less para CSS. Depois concatenando todos os CSS, JS em um único arquivo. Depois minificando (com Uglify) para diminuir seu tamanho. Em particular com Sass ele ainda pode concatenar sprites em uma única imagem também. Enfim, o pacote completo para otimizar assets ao máximo.</p>
<p>Se você ainda não conhece sobre esse recurso, não deixe de ler meu <a href="http://www.akitaonrails.com/2012/07/01/asset-pipeline-para-iniciantes"target="_blank" rel="noopener">tutorial</a>.</p>
<p>Só que à medida que um projeto precisa ter várias bibliotecas CSS, JS, etc o processo pode ficar chato. Principalmente porque em alguns casos precisa manualmente modificar os paths dentro de seus fontes.</p>
<p>Um dos problemas é que o Asset Pipeline vai juntar todos os arquivos em um único, colocando timestamp no nome. E aí os paths não vão funcionar a menos que usem o helper <a href="http://api.rubyonrails.org/classes/ActionView/Helpers/AssetUrlHelper.html#method-i-asset_path"target="_blank" rel="noopener">asset_path</a> ou <a href="http://api.rubyonrails.org/classes/ActionView/Helpers/AssetUrlHelper.html#method-i-asset_url"target="_blank" rel="noopener">asset_url</a>. Por isso acabamos usando rubygems feitas manualmente como o <a href="https://github.com/anjlab/bootstrap-rails"target="_blank" rel="noopener">bootstrap-rails</a>.</p>
<p>E isso leva a outro problema: atualizações. Primeiro, os autores originais lançam uma determinada biblioteca Javascript. Podemos manualmente fazer o download dos fontes e &ldquo;vendorizá-los&rdquo; no projeto. Ou, se você utilizar o Bower podemos esperar sair esse novo pacote e atualizar com o Bower. Ou, se estivermos usando uma Rubygem, podemos esperar sair a nova gem e atualizar com Bundler.</p>
<p>Então, vamos separar tudo e usar Bower pra gerenciar bibliotecas de front-end e usar Bundler para gerenciar gems de back-end e todo mundo fica feliz? Não é tão fácil assim já que muitas gems - ActiveAdmin como um bom exemplo - já dependem de bibliotecas de front-end encapsuladas em rubygems como temos neste trecho da gemspec:</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="n">s</span><span class="o">.</span><span class="n">add_dependency</span> <span class="s2">&#34;bourbon&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">s</span><span class="o">.</span><span class="n">add_dependency</span> <span class="s2">&#34;jquery-rails&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">s</span><span class="o">.</span><span class="n">add_dependency</span> <span class="s2">&#34;jquery-ui-rails&#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>Já que temos duas comunidades tendo exatamente o mesmo trabalho: no Bower alguém vai atualizar pacotes de jquery; na Rubygems alguém vai ter que atualizar também. Então, porque não otimizar isso e apenas um deles atualizar. Nesse caso vamos aproveitar - e colaborar - para deixar as do Bower sempre atualizadas.</p>
<p>Será que existe uma forma, então, de converter um pacote Bower para um pacote Rubygems? E este é o problema que o Rails Assets se propõe a tentar resolver. Seguindo exatamente o mesmo exemplo do site deles, colocaremos na Gemfile de nossos projetos Rails 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="n">source</span> <span class="s1">&#39;https://rubygems.org&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">source</span> <span class="s1">&#39;https://rails-assets.org&#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></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">group</span> <span class="ss">:assets</span> <span class="k">do</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></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;coffee-rails&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;rails-assets-bootstrap&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;rails-assets-angular&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;rails-assets-leaflet&#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>A primeira diferença é um novo source. E isso é importante pois não vai poluir o repositório de Rubygems com gems que são meros &lsquo;stubs&rsquo;.</p>
<p>A segunda diferença são as gems prefixadas seguindo o padrão &ldquo;rails-assets-[nome do pacote Bower]&rdquo; que, obviamente, foram feitas desta forma para não conflitarem com as que já existem no Rubygems.org. Se isso funcionar significa que a antiga gem &ldquo;bootstrap-rails&rdquo; poderá ser descontinuada e passamos a usar a &ldquo;rails-assets-bootstrap&rdquo;, e assim por diante.</p>
<p>Se uma determinada gem ainda não existir, o processo vai demorar um pouco porque o Rails Assets vai baixar o pacote do Bower, modificá-la, encapsulá-la como uma Rubygem e publicar no seu repositório. Mas quanto mais gente usar com mais bibliotecas, mais rubygems já estarão prontas para baixar imediatamente.</p>
<p>Para o Asset Pipeline continua a mesma coisa. No mesmo arquivo <tt>app/assets/javascripts/application.js</tt> teríamos igual:</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-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">//= require_self
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require bootstrap
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require angular
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require leaflet
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require_tree .
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require_tree shared
</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 no <tt>app/assets/stylesheets/application.css</tt> teríamos também igual:</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-css" data-lang="css"><span class="line"><span class="cl"><span class="c">/*
</span></span></span><span class="line"><span class="cl"><span class="c"> *= require_self
</span></span></span><span class="line"><span class="cl"><span class="c"> *= require bootstrap
</span></span></span><span class="line"><span class="cl"><span class="c"> *= require leaflet
</span></span></span><span class="line"><span class="cl"><span class="c"> *= require_tree .
</span></span></span><span class="line"><span class="cl"><span class="c"> *= require_tree shared
</span></span></span><span class="line"><span class="cl"><span class="c"> */</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 rubygems que encapsulam bibliotecas front-end não são apenas por causa do Asset Pipeline, além disso elas também podem conter generators para facilitar a criação de alguma estrutura de diretórios ou arquivos de configuração específicos. Mas mesmo nesse caso podemos separar em duas coisas: uma sendo só os fontes originais e uma segunda gem que depende da primeira com ferramentas ou outros extras como generators.</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>Para Rubistas a primeira vantagem mais óbvia é o seguinte: não precisamos misturar dois workflows de dependência, uma com Bower (ou manualmente) e outra com Bundler. Podemos continuar apenas com Bundler. Lembrando que se pacotes Bower não forem vendorizados no projeto Rails ainda teríamos que manter esse processo com Bower na integração contínua - adicionando o comando no .travis.yml, por exemplo; e também teríamos que ter no deployment. Idealmente, não deveríamos ter que vendorizar bibliotecas. Isso sem contar que se usar Bower num projeto Rails e usar Heroku, precisa de um outro <a href="http://xseignard.github.io/2013/02/18/use-bower-with-heroku/"target="_blank" rel="noopener">buildpack</a> para funcionar. Ou seja, o Bower adiciona muito mais complexidade do que somente ter que rodar mais um comando &ldquo;install&rdquo; em desenvolvimento. Ambientes de teste, produção, tudo fica um pouco mais complicado, e desnecessariamente.</p>
<p>Além disso, quem colaborava mantendo a rubygems de um &ldquo;bootstrap-rails&rdquo;, por exemplo, pode continuar a dedicar seu tempo atualizando diretamente o pacote equivalente do Bower e deixar essa fonte única sempre atualizada, o que pode ser um reforço ao Bower e aos não-rubistas que a utilizam.</p>
<p>E com isso também não precisamos ter gems de stubs poluindo o Rubygems.org. Ou seja, &ldquo;se&rdquo; funcionar será uma situação ganha-ganha para todo mundo. Porém nem tudo são flores. Como uma das idéias do Rails Assets é modificar o fonte para adicionar os asset-paths corretos, quer dizer que podem existir situações onde patterns automáticos não peguem, ou existam ambiguidades que precisem ser resolvidos manualmente. Então, das duas uma: ou colaboramos com o Rails Assets para adicionar os patterns que faltam, se for possível; ou criamos um passo no workflow do Rails Assets que permitam modificações manuais. Ainda não sei, precisamos ver quais são esses cenários e como corrigí-los. Mas isso não elimina os pontos positivos descritos anteriormente.</p>
<p>O melhor a fazer: testar e experimentar. O projeto é open source, você pode <a href="https://github.com/rails-assets/rails-assets/"target="_blank" rel="noopener">colaborar</a> mandando Pull Requests ou mesmo reportando <a href="https://github.com/rails-assets/rails-assets/issues"target="_blank" rel="noopener">Issues</a> e ajudando a evoluir. Se a idéia for furada - o que não me parece - vamos saber rapidamente - e voltamos ao mesmo problema de antes, mas pelo menos esta é uma proposta de solução muito interessante que vale a pena ser explorada mais a fundo.</p>
]]></content:encoded><category>rails</category><category>frontend</category><category>javascript</category></item><item><title>Internationalização (I18n) Mínima em Rails 3.2 - Parte 2</title><link>https://akitaonrails.github.io/2012/07/14/internationalizacao-i18n-minima-em-rails-3-2-parte-2/</link><guid isPermaLink="true">https://akitaonrails.github.io/2012/07/14/internationalizacao-i18n-minima-em-rails-3-2-parte-2/</guid><pubDate>Sat, 14 Jul 2012 22:44:00 -0300</pubDate><description>&lt;p&gt;Este artigo inicia na &lt;a href="http://akitaonrails.com/2012/07/14/internationalizacao-i18n-minima-em-rails-3-2-parte-1"target="_blank" rel="noopener"&gt;Parte 1&lt;/a&gt;. Leia primeiro antes de continuar.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://s3.amazonaws.com/akitaonrails/assets/2012/7/14/Screen%20Shot%202012-07-14%20at%209.46.44%20PM_original.png?1342313242" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;Se quiser ver como essa aplicação se comporta de verdade, eu subi uma versão numa conta free do Heroku, então &lt;a href="http://i18n-demo.herokuapp.com"target="_blank" rel="noopener"&gt;clique aqui&lt;/a&gt; e veja.&lt;/p&gt;
&lt;p&gt;Nesta Parte 2 vamos continuar com as seguinte seções:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#markdown"&gt;Seção Bônus: Markdown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#twitterbootstrap"&gt;Seção Bônus: Twitter Bootstrap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#activeadminglobalize"&gt;ActiveAdmin e Globalize 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#routes"&gt;Rotas Internacionalizadas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusao"&gt;Conclusão&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Seção Bônus: Markdown&lt;span class="hx:absolute hx:-mt-20" id="seção-bônus-markdown"&gt;&lt;/span&gt;
&lt;a href="#se%c3%a7%c3%a3o-b%c3%b4nus-markdown" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Outra coisa que quero demonstrar no caso de um aplicativo “estilo” gerenciador de conteúdo (CMS) é o cache do HTML que queremos gerar. Neste exemplo defini que o atributo &lt;tt&gt;Article#body&lt;/tt&gt; terá texto no formato &lt;a href="http://daringfireball.net/projects/markdown/"target="_blank" rel="noopener"&gt;Markdown&lt;/a&gt;. Poderia ser qualquer outro formato como Textile, Creole, MediaWiki ou o qualquer outro que gere HTML. É uma forma de simplificar o processo de edição do conteúdo sem precisar de um editor &lt;a href="http://pt.wikipedia.org/wiki/WYSIWYG"target="_blank" rel="noopener"&gt;WYSIWYG&lt;/a&gt; mais complicado como um &lt;a href="http://jhollingworth.github.com/bootstrap-wysihtml5/"target="_blank" rel="noopener"&gt;bootstrap wysihtml5&lt;/a&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Este artigo inicia na <a href="http://akitaonrails.com/2012/07/14/internationalizacao-i18n-minima-em-rails-3-2-parte-1"target="_blank" rel="noopener">Parte 1</a>. Leia primeiro antes de continuar.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2012/7/14/Screen%20Shot%202012-07-14%20at%209.46.44%20PM_original.png?1342313242" alt=""  loading="lazy" /></p>
<p>Se quiser ver como essa aplicação se comporta de verdade, eu subi uma versão numa conta free do Heroku, então <a href="http://i18n-demo.herokuapp.com"target="_blank" rel="noopener">clique aqui</a> e veja.</p>
<p>Nesta Parte 2 vamos continuar com as seguinte seções:</p>
<ul>
<li><a href="#markdown">Seção Bônus: Markdown</a></li>
<li><a href="#twitterbootstrap">Seção Bônus: Twitter Bootstrap</a></li>
<li><a href="#activeadminglobalize">ActiveAdmin e Globalize 3</a></li>
<li><a href="#routes">Rotas Internacionalizadas</a></li>
<li><a href="#conclusao">Conclusão</a></li>
</ul>
<h2>Seção Bônus: Markdown<span class="hx:absolute hx:-mt-20" id="seção-bônus-markdown"></span>
    <a href="#se%c3%a7%c3%a3o-b%c3%b4nus-markdown" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Outra coisa que quero demonstrar no caso de um aplicativo “estilo” gerenciador de conteúdo (CMS) é o cache do HTML que queremos gerar. Neste exemplo defini que o atributo <tt>Article#body</tt> terá texto no formato <a href="http://daringfireball.net/projects/markdown/"target="_blank" rel="noopener">Markdown</a>. Poderia ser qualquer outro formato como Textile, Creole, MediaWiki ou o qualquer outro que gere HTML. É uma forma de simplificar o processo de edição do conteúdo sem precisar de um editor <a href="http://pt.wikipedia.org/wiki/WYSIWYG"target="_blank" rel="noopener">WYSIWYG</a> mais complicado como um <a href="http://jhollingworth.github.com/bootstrap-wysihtml5/"target="_blank" rel="noopener">bootstrap wysihtml5</a>.</p>
<p>Uma boa engine para converter Markdown em HTML é o <a href="https://github.com/rtomayko/rdiscount/"target="_blank" rel="noopener">RDiscount</a> (olhe também a gem <a href="https://github.com/rtomayko/tilt"target="_blank" rel="noopener">Tilt</a>, ambos do Ryan Tomayko). Novamente, apenas adicione a seguinte gem no <tt>Gemfile</tt>:</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">gem</span> <span class="s1">&#39;rdiscount&#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>Usá-lo é muito simples:</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></span><span class="line"><span class="cl"><span class="no">RDiscount</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&#34;texto em **markdown**.&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">to_html</span> <span class="c1">#=&gt; **markdown**</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 erro mais comum é adicionar essa lógica no controller, algo como 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="k">class</span> <span class="nc">ArticleController</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">show</span>  
</span></span><span class="line"><span class="cl">    <span class="n">article</span> <span class="o">=</span> <span class="no">Article</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@html</span> <span class="o">=</span> <span class="no">RDiscount</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="sb">`article.body).to_html  
</span></span></span><span class="line"><span class="cl"><span class="sb"> end  
</span></span></span><span class="line"><span class="cl"><span class="sb">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 acarreta um processamento de conversão para <strong>cada requisição de cada usuários à mesma página</strong>. É exatamente o tipo de cenário que queremos otimizar o quanto antes. A melhor forma, nesse tipo de cenário de conversão, é gravar a versão convertida junto com a original. Por isso na migration do model <tt>Article</tt> já criamos com a coluna <tt>body</tt> e também <tt>body_html</tt>. Então só precisamos adicionar um simples callback no model:</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">Article</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>  
</span></span><span class="line"><span class="cl"> <span class="n">attr_accessible</span> <span class="ss">:body</span><span class="p">,</span> <span class="ss">:body_html</span><span class="p">,</span> <span class="ss">:slug</span><span class="p">,</span> <span class="ss">:title</span><span class="p">,</span> <span class="ss">:locale</span><span class="p">,</span> <span class="ss">:translations_attributes</span>  
</span></span><span class="line"><span class="cl"> <span class="err">…</span>  
</span></span><span class="line"><span class="cl"> <span class="n">before_save</span> <span class="ss">:generate_html</span>  
</span></span><span class="line"><span class="cl"> <span class="err">…</span>  
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">translations_attributes</span><span class="o">=</span><span class="p">(</span><span class="n">attributes</span><span class="p">)</span>  
</span></span><span class="line"><span class="cl"> <span class="n">new_translations</span> <span class="o">=</span> <span class="n">attributes</span><span class="o">.</span><span class="n">values</span><span class="o">.</span><span class="n">reduce</span><span class="p">({})</span> <span class="k">do</span> <span class="o">|</span><span class="n">new_values</span><span class="p">,</span> <span class="n">translation</span><span class="o">|</span>  
</span></span><span class="line"><span class="cl"> <span class="n">translation</span><span class="o">.</span><span class="n">merge!</span><span class="p">(</span><span class="s2">&#34;body_html&#34;</span> <span class="o">=&gt;</span> <span class="no">RDiscount</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">translation</span><span class="o">[</span><span class="s2">&#34;body&#34;</span><span class="o">]</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">to_html</span> <span class="p">)</span>  
</span></span><span class="line"><span class="cl"> <span class="n">new_values</span><span class="o">.</span><span class="n">merge!</span> <span class="n">translation</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="s2">&#34;locale&#34;</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="n">translation</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">set_translations</span> <span class="n">new_translations</span>  
</span></span><span class="line"><span class="cl"> <span class="k">end</span>  
</span></span><span class="line"><span class="cl"><span class="kp">private</span>  
</span></span><span class="line"><span class="cl"> <span class="err">…</span>  
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">generate_html</span>  
</span></span><span class="line"><span class="cl"> <span class="nb">self</span><span class="o">.</span><span class="n">body_html</span> <span class="o">=</span> <span class="no">RDiscount</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">self</span><span class="o">.</span><span class="n">body</span><span class="p">)</span><span class="o">.</span><span class="n">to_html</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 foi simples. Agora não precisa adicionar nada no controller e na view basta chamar diretamente o conteúdo cacheado:</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-html" data-lang="html"><span class="line"><span class="cl"><span class="err">&lt;</span>%= raw @article.body_html %\&gt;  </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>Observe também o método <tt>raw</tt>: como estamos adicionando HTML vinda de model na view, o Rails, por padrão, vai considerar esse conteúdo “perigoso” e irá escapá-lo. Não é o que queremos, queremos mesmo que o HTML seja mesclado, para isso precisamos expôr nossa intenção explicitamente dizendo que queremos o HTML “crú” (raw). Leia mais sobre essa funcionalidade no <a href="http://guides.rubyonrails.org/active_support_core_extensions.html#output-safety"target="_blank" rel="noopener">Guia Oficial</a>.</p>
<p>Em particular o método <tt>translations_attributes=</tt> merece atenção. Por causa da forma como o Globalize3 lida com a associação de traduções, ao tentarmos simplesmente adicionar massivamente múltiplas traduções, ele não executa corretamente os <tt>before_filter</tt> para cada linguagem, acaba apagando items que não são a localização configurada atualmente e grava somente um ítem em vez de gravar massivamente múltiplos. Isso particularmente quebra a funcionalidade de ActiveAdmin que vamos discutir mais abaixo.</p>
<p>Para consertar isso, sobrescrevemos o método de assinalação em massa forçando o uso do método <tt>set_translations</tt> do Globalize3. Além disso também já criamos a versão HTML do campo <tt>body</tt> e gravamos de uma vez, isso porque a versão de cache do HTML no fundo é uma “tradução” e por isso não vai na tabela principal <tt>articles</tt> mas sim na implícita <tt>article_translations</tt>.</p>
<h2>Seção Bônus: Twitter Bootstrap<span class="hx:absolute hx:-mt-20" id="seção-bônus-twitter-bootstrap"></span>
    <a href="#se%c3%a7%c3%a3o-b%c3%b4nus-twitter-bootstrap" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Essa é completamente fora do escopo deste artigo, mas como meu código no Github está utilizando, vou apenas listar como instalei. Para quem não conhece, o Twitter Bootstrap é um conjunto de stylesheets e javascripts para estilizar rapidamente seu site, justamente para casos como este, onde o design não é importante, é basicamente um protótipo e eu queria algo menos feio do que não colocar nada. Existem diversas gems derivadas que utilizam o bootstrap, mas para o básico podemos começar adicionando as seguintes gems no <tt>Gemfile</tt>:</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">group</span> <span class="ss">:assets</span> <span class="k">do</span>  
</span></span><span class="line"><span class="cl"> <span class="err">…</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># See &lt;https://github.com/sstephenson/execjs#readme&gt; for more supported runtimes  </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="o">=&gt;</span> <span class="ss">:ruby</span>  
</span></span><span class="line"><span class="cl"> <span class="n">gem</span> <span class="s1">&#39;less-rails&#39;</span>  
</span></span><span class="line"><span class="cl"> <span class="n">gem</span> <span class="s1">&#39;twitter-bootstrap-rails&#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>Agora podemos começar a instalar os arquivos estáticos para a aplicação:</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-bash" data-lang="bash"><span class="line"><span class="cl">rails g bootstrap:install application fluid  </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 podemos adicionar as views estilizadas para cada recurso da sua aplicação:</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-bash" data-lang="bash"><span class="line"><span class="cl">rails g bootstrap:themed Articles  </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>Neste ponto, ele vai criar vários arquivos desnecessários que podemos apagar e precisamos customizar bastante. Não vou copiar todo o fonte das views no arquivo, então faça o clone <a href="http://github.com/akitaonrails/Rails-3-I18n-Demonstration/"target="_blank" rel="noopener">deste projeto no Github</a> e aprenda lendo o código que está em <tt>app/uploads</tt> e <tt>app/views</tt>. Compare com os arquivos gerados pelo geradores acima e o que foi modificado.</p>
<p>Um dos pontos importantes a lembrar quando lidamos com plugins de javascript, arquivos CSS, é não deixar sobrando <tt>require_tree .</tt> sem que você saiba para que serve. Leia meu artigo sobre <a href="http://akitaonrails.com/2012/07/01/asset-pipeline-para-iniciantes"target="_blank" rel="noopener">Assets Pipeline para Iniciantes</a>.</p>
<p>No caso do layout principal note que adicionei bloco de <tt>.navbar</tt> para o menu principal no topo. Adicionei o <tt>yield</tt> onde os outros conteúdos vão se encaixar dentro de um <tt>.container</tt> e temos uma área de botões no rodapé num bloco <tt>.form-actions</tt>. Um cuidado sobre esta gem é que no arquivo que ele cria em <tt>app/uploads/stylesheets/bootstrap_and_overrides.css.less</tt> ele define margens no elemento <tt>body</tt> que são desnecessários, apenas remove essas definições logo no começo do arquivo.</p>
<p>Note também que no meu Github todas as views de Devise que mencionamos anteriormente já estão estilizadas com o bootstrap (por isso que na seção que mencionamos sobre Devise eu faço um <tt>git checkout 0ff207…</tt> para fazer download das views traduzidas mas que ainda não tinham sido estilizadas). Agora você pode voltar ao diretório do meu projeto, fazer <tt>git checkout master</tt> e realizar a mesma cópia dos arquvos para pegar as views estilizadas.</p>
<p>Um lembrete para menus é criar itens que saibam em que controller/action estamos e desabilitar o link da página atual, um exemplo é o que fiz de exemplo nesta aplicação. No arquivo <tt>app/helpers/application_helper.rb</tt> temos:</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></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">ApplicationHelper</span>  
</span></span><span class="line"><span class="cl"> <span class="err">…</span>  
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">navigation_links</span>  
</span></span><span class="line"><span class="cl"> <span class="n">links</span> <span class="o">=</span> <span class="o">[]</span>  
</span></span><span class="line"><span class="cl"> <span class="n">options</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="ss">:controller</span><span class="o">]</span> <span class="o">==</span> <span class="s2">&#34;welcome&#34;</span> <span class="p">?</span> <span class="p">{</span> <span class="ss">class</span><span class="p">:</span> <span class="s2">&#34;active&#34;</span> <span class="p">}</span> <span class="p">:</span> <span class="p">{}</span>  
</span></span><span class="line"><span class="cl"> <span class="n">links</span> <span class="o">&lt;&lt;</span> <span class="n">content_tag</span><span class="p">(</span><span class="ss">:li</span><span class="p">,</span> <span class="n">link_to</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="s2">&#34;hello&#34;</span><span class="p">),</span> <span class="n">welcome_path</span><span class="p">),</span> <span class="n">options</span><span class="p">)</span><span class="o">.</span><span class="n">html_safe</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">options</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="ss">:controller</span><span class="o">]</span> <span class="o">==</span> <span class="s2">&#34;articles&#34;</span> <span class="p">?</span> <span class="p">{</span> <span class="ss">class</span><span class="p">:</span> <span class="s2">&#34;active&#34;</span> <span class="p">}</span> <span class="p">:</span> <span class="p">{}</span> 
</span></span><span class="line"><span class="cl"><span class="n">links</span> <span class="o">&lt;&lt;</span> <span class="n">content_tag</span><span class="p">(</span><span class="ss">:li</span><span class="p">,</span> <span class="n">link_to</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="s2">&#34;articles.title&#34;</span><span class="p">),</span> <span class="n">articles_path</span><span class="p">),</span> <span class="n">options</span><span class="p">)</span><span class="o">.</span><span class="n">html_safe</span> 
</span></span><span class="line"><span class="cl"><span class="n">links</span> <span class="o">&lt;&lt;</span> <span class="n">content_tag</span><span class="p">(</span><span class="ss">:li</span><span class="p">,</span> <span class="n">link_to</span><span class="p">(</span><span class="n">t</span><span class="p">(</span><span class="s2">&#34;admin.title&#34;</span><span class="p">),</span> <span class="n">admin_dashboard_path</span><span class="p">))</span><span class="o">.</span><span class="n">html_safe</span> <span class="n">content_tag</span><span class="p">(</span><span class="ss">:ul</span><span class="p">,</span> <span class="n">links</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">html_safe</span><span class="p">,</span> <span class="ss">class</span><span class="p">:</span> <span class="s2">&#34;nav&#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></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 no layout em <tt>app/views/layouts/application.html.erb</tt> colocamos:</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-html" data-lang="html"><span class="line"><span class="cl"><span class="err">&lt;</span>%= t(&#34;site_name&#34;) %\&gt;](#)\<span class="err">&lt;</span>%= navigation_links %\&gt;</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>
<hr>
<h2>ActiveAdmin e Globalize 3<span class="hx:absolute hx:-mt-20" id="activeadmin-e-globalize-3"></span>
    <a href="#activeadmin-e-globalize-3" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O ActiveAdmin é um excelente projeto para termos rapidamente um módulo simples de administração que consegue expôr as operações de CRUD de um model, incluindo suas validações e até elementos como upload de imagens (caso esteja usando CarrierWave, por exemplo). Por usar por baixo formtastic, customizar seus formulários também não é complicado. Instalar é simples, coloque na <tt>Gemfile</tt>:</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="err">…</span>  
</span></span><span class="line"><span class="cl"><span class="n">group</span> <span class="ss">:assets</span> <span class="k">do</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="err">…</span>  
</span></span><span class="line"><span class="cl"><span class="k">end</span>  
</span></span><span class="line"><span class="cl"><span class="err">…</span>  
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;jquery-rails&#39;</span>  
</span></span><span class="line"><span class="cl"><span class="n">gem</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;ActiveAdmin-Globalize3-inputs&#39;</span>  
</span></span><span class="line"><span class="cl"><span class="err">…</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>Execute o comando:</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-bash" data-lang="bash"><span class="line"><span class="cl">rails g active_admin:install  </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>Uma dica para que o Assets Pipeline não falhe em produção. Precisamos declarar explicitamente os assets do ActiveAdmin. Adicione no <tt>config/application.rb</tt>:</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">config</span><span class="o">.</span><span class="n">assets</span><span class="o">.</span><span class="n">precompile</span> <span class="o">+=</span> <span class="sx">%w(active_admin.js active_admin.css)</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 adicione em <tt>app/admin</tt> quaisquer models que precise expor. Leia a <a href="http://activeadmin.info/documentation.html"target="_blank" rel="noopener">documentação</a> do ActiveAdmin no site deles mas como exemplo, para customizar a tabela de listagem do model <tt>Article</tt> (<tt>index</tt>) e também a página visualização de um único artigo (<tt>show</tt>), podemos escrever em <tt>app/admin/articles.rb</tt>:</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">ActiveAdmin</span><span class="o">.</span><span class="n">register</span> <span class="no">Article</span> <span class="k">do</span>  
</span></span><span class="line"><span class="cl"> <span class="n">index</span> <span class="k">do</span>  
</span></span><span class="line"><span class="cl"> <span class="n">column</span> <span class="ss">:id</span>  
</span></span><span class="line"><span class="cl"> <span class="n">column</span> <span class="ss">:slug</span>  
</span></span><span class="line"><span class="cl"> <span class="n">column</span> <span class="ss">:title</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">default_actions</span> <span class="k">end</span> 
</span></span><span class="line"><span class="cl">  <span class="n">show</span> <span class="k">do</span> <span class="o">|</span><span class="n">article</span><span class="o">|</span> <span class="n">attributes_table</span> <span class="k">do</span> 
</span></span><span class="line"><span class="cl">    <span class="n">row</span> <span class="ss">:slug</span> <span class="no">I18n</span><span class="o">.</span><span class="n">available_locales</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> 
</span></span><span class="line"><span class="cl">      <span class="o">|</span><span class="n">locale</span><span class="o">|</span> <span class="n">h3</span> <span class="no">I18n</span><span class="o">.</span><span class="n">t</span><span class="p">(</span><span class="n">locale</span><span class="p">,</span> <span class="ss">scope</span><span class="p">:</span> <span class="o">[</span><span class="s2">&#34;translation&#34;</span><span class="o">]</span><span class="p">)</span> <span class="n">div</span> <span class="k">do</span> 
</span></span><span class="line"><span class="cl">        <span class="n">h4</span> <span class="n">article</span><span class="o">.</span><span class="n">translations</span><span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="ss">locale</span><span class="p">:</span> <span class="n">locale</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">title</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">div</span> <span class="k">do</span> 
</span></span><span class="line"><span class="cl">        <span class="n">article</span><span class="o">.</span><span class="n">translations</span><span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="ss">locale</span><span class="p">:</span> <span class="n">locale</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">body_html</span><span class="o">.</span><span class="n">html_safe</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="n">active_admin_comments</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 bloco <tt>show</tt> é o mais interessante. Aqui estamos usando diretamente a associação <tt>translations</tt> que o Globalize 3 adicionou ao nosso model para buscar explicitamente o conteúdo de uma localização, em vez de puxar o que o model nos der automaticamente baseado na localização. Assim colocamos ambos os conteúdo uma embaixo do outro de uma só vez.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2012/7/14/Screen%20Shot%202012-07-14%20at%208.28.56%20PM_original.png?1342308888" alt=""  loading="lazy" /></p>
<p>Mas e para editar os conteúdos de ambas as localizações? Para isso colocamos além do ActiveAdmin o <a href="https://github.com/mimimi/ActiveAdmin-Globalize3-inputs"target="_blank" rel="noopener">ActiveAdmin-Globalize3-inputs</a> e as gems de JQuery (porque vamos precisar do elemento de “Tabs”).</p>
<p>Esse módulo vai utilizar as funcionalidades do ActiveRecord de <a href="http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html#method-i-accepts_nested_attributes_for"target="_blank" rel="noopener">accepts_nested_attributes_for</a> para receber no mesmo formulário HTML os atributos das associações. Para isso garanta que seu modelo <tt>app/models/article.rb</tt> tem 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="k">class</span> <span class="nc">Article</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>  
</span></span><span class="line"><span class="cl"> <span class="n">attr_accessible</span> <span class="ss">:body</span><span class="p">,</span> <span class="ss">:body_html</span><span class="p">,</span> <span class="ss">:slug</span><span class="p">,</span> <span class="ss">:title</span><span class="p">,</span> <span class="ss">:locale</span><span class="p">,</span> <span class="ss">:translations_attributes</span>  
</span></span><span class="line"><span class="cl"> <span class="err">…</span>  
</span></span><span class="line"><span class="cl"> <span class="n">translates</span> <span class="ss">:title</span><span class="p">,</span> <span class="ss">:body</span><span class="p">,</span> <span class="ss">:body_html</span>  
</span></span><span class="line"><span class="cl"> <span class="n">accepts_nested_attributes_for</span> <span class="ss">:translations</span>  
</span></span><span class="line"><span class="cl"> <span class="err">…</span>  
</span></span><span class="line"><span class="cl"> <span class="k">class</span> <span class="nc">Translation</span>  
</span></span><span class="line"><span class="cl"> <span class="n">attr_accessible</span> <span class="ss">:locale</span><span class="p">,</span> <span class="ss">:title</span><span class="p">,</span> <span class="ss">:body</span><span class="p">,</span> <span class="ss">:body_html</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">translations_attributes</span><span class="o">=</span><span class="p">(</span><span class="n">attributes</span><span class="p">)</span> 
</span></span><span class="line"><span class="cl">    <span class="n">new_translations</span> <span class="o">=</span> <span class="n">attributes</span><span class="o">.</span><span class="n">values</span><span class="o">.</span><span class="n">reduce</span><span class="p">({})</span> <span class="k">do</span> 
</span></span><span class="line"><span class="cl">      <span class="o">|</span><span class="n">new_values</span><span class="p">,</span> <span class="n">translation</span><span class="o">|</span> <span class="n">translation</span><span class="o">.</span><span class="n">merge!</span><span class="p">(</span><span class="s2">&#34;body_html&#34;</span> <span class="o">=&gt;</span> <span class="no">RDiscount</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">translation</span><span class="o">[</span><span class="s2">&#34;body&#34;</span><span class="o">]</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">to_html</span> <span class="p">)</span> 
</span></span><span class="line"><span class="cl">      <span class="n">new_values</span><span class="o">.</span><span class="n">merge!</span> <span class="n">translation</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="s2">&#34;locale&#34;</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="n">translation</span> <span class="k">end</span> 
</span></span><span class="line"><span class="cl">      <span class="n">set_translations</span> <span class="n">new_translations</span> <span class="k">end</span> <span class="err">…</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 faz o modelo aceitar <a href="http://guides.rubyonrails.org/security.html#mass-assignment"target="_blank" rel="noopener">mass assignment</a> do atributo <tt>translations_attributes</tt>, usamos o <tt>accepts_nested_attributes_for</tt> para que os helpers de formulário saibam como gerar os elementos para os atributos da associação. Uma coisa estranha pode ser o fato de estarmos sobrescrevendo a classe <tt>Article::Translation</tt>. Acredito que é um bug do Globalize 3, e sem essa modificação o <em>mass assignment</em> iria falhar.</p>
<p>Precisamos também adicionar o JQuery UI para o ActiveAdmin. Basta alterar o arquivo <tt>app/uploads/stylesheets/active_admin.css</tt>:</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-css" data-lang="css"><span class="line"><span class="cl"><span class="o">//</span> <span class="nt">Active</span> <span class="nt">Admin</span> <span class="nt">CSS</span> <span class="nt">Styles</span>  
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">import</span> <span class="s2">&#34;active_admin/mixins&#34;</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">import</span> <span class="s2">&#34;active_admin/base&#34;</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">import</span> <span class="s2">&#34;jquery.ui.tabs&#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>E também o arquivo <tt>app/uploads/javascripts/active_admin.js</tt>:</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-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">//= require active_admin/base  
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require jquery.ui.tabs  
</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>Finalmente, precisamos alterar novamente o arquivo de configuração <tt>app/admin/articles.rb</tt> para adicionar 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="no">ActiveAdmin</span><span class="o">.</span><span class="n">register</span> <span class="no">Article</span> <span class="k">do</span>  
</span></span><span class="line"><span class="cl"> <span class="err">…</span>  
</span></span><span class="line"><span class="cl"> <span class="n">form</span> <span class="k">do</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span>  
</span></span><span class="line"><span class="cl"> <span class="n">f</span><span class="o">.</span><span class="n">input</span> <span class="ss">:slug</span>  
</span></span><span class="line"><span class="cl"> <span class="n">f</span><span class="o">.</span><span class="n">globalize_inputs</span> <span class="ss">:translations</span> <span class="k">do</span> <span class="o">|</span><span class="n">lf</span><span class="o">|</span>  
</span></span><span class="line"><span class="cl"> <span class="n">lf</span><span class="o">.</span><span class="n">inputs</span> <span class="k">do</span>  
</span></span><span class="line"><span class="cl"> <span class="n">lf</span><span class="o">.</span><span class="n">input</span> <span class="ss">:title</span>  
</span></span><span class="line"><span class="cl"> <span class="n">lf</span><span class="o">.</span><span class="n">input</span> <span class="ss">:body</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">lf</span><span class="o">.</span><span class="n">input</span> <span class="ss">:locale</span><span class="p">,</span> <span class="ss">:as</span> <span class="o">=&gt;</span> <span class="ss">:hidden</span> <span class="k">end</span> <span class="k">end</span> <span class="n">f</span><span class="o">.</span><span class="n">buttons</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</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>Atributos não internacionalizados como <tt>slug</tt> ficam fora, mas o resto vai dentro do bloco <tt>globalize_inputs</tt> passando o nome da associação, nesse caso <tt>translations</tt>. E dentro de cada sub-formulário precisamos de um campo escondido com a localização desse sub-formulário em particular, para isso temos o campo <tt>locale</tt> como <tt>hidden</tt>.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2012/7/14/Screen%20Shot%202012-07-14%20at%208.28.41%20PM_original.png?1342308896" alt=""  loading="lazy" /></p>
<p>Vale lembrar que o ActiveAdmin suporta internacionalização também. Neste aplicativo escolhi não adicionar esse suporte. É o cenário onde um único administrador controla ambas as linguagens. Mas se você tiver a situação onde cada equipe em cada país cuida da sua própria linguagem, talvez queira adicionar este suporte.</p>
<p>Finalmente, como explicamos na seção sobre Markdown, para que o ActiveAdmin consiga dar POST de várias traduções ao mesmo tempo, no mesmo formulário, usando a capacidade de assinalamento em massa e <tt>accepts_nested_attributes_for</tt>, temos que sobrescrever o método <tt>translations_attributes=</tt> pois simplesmente inserir os novos registros de tradução na associação não vai funcionar, o Globalize3 vai filtrar essa lista e só vai gravar o registro da localização atual. Mas forçando o uso to método interno do Globalize3, <tt>set_translations</tt>, conseguimos gravar múltiplas traduções ao mesmo tempo.</p>
<h2>Rotas Internacionalizadas<span class="hx:absolute hx:-mt-20" id="rotas-internacionalizadas"></span>
    <a href="#rotas-internacionalizadas" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Deixei por último uma das coisas mais interessantes nesta aplicação. Para efeitos de SEO também queremos que as URLs sejam traduzidas. Ou seja, queremos que as seguintes URLs todas apontem para o mesmo lugar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>/users/sign_in  
/en/users/sign_in  
/pt-BR/usuarios/login  </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>Existem algumas gems que fazem isso, a primeira que esbarrei se chama “i18n_routing”, mas não consegui fazê-la funcionar, acredito que tenha bugs ainda. Se procurar mais vai acabar encontrando a <a href="https://github.com/raul/translate_routes"target="_blank" rel="noopener">translate_routes</a> mas ela está obsoleta e dois outros forks passaram a atualizá-la. Uma é a <a href="https://github.com/enriclluelles/route_translator"target="_blank" rel="noopener">route_translator</a>, que eu não testei porque parecia ter pouca atividade. A que escolhi usar se chama <a href="https://github.com/francesc/rails-translate-routes"target="_blank" rel="noopener">rails-translate-routes</a>. Para adicionar ao projeto, edite seu <tt>Gemfile</tt>:</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></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;rails-translate-routes&#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>Depois de executar o comando <tt>bundle</tt> precisamos editar o arquivo <tt>config/routes.rb</tt> que controla todas as rotas. Neste estágio, ele deve ter o seguinte conteúdo:</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></span><span class="line"><span class="cl"><span class="no">I18nDemo</span><span class="o">::</span><span class="no">Application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#1. rotas para active admin  </span>
</span></span><span class="line"><span class="cl"> <span class="no">ActiveAdmin</span><span class="o">.</span><span class="n">routes</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>  
</span></span><span class="line"><span class="cl"> <span class="n">devise_for</span> <span class="ss">:admin_users</span><span class="p">,</span> <span class="no">ActiveAdmin</span><span class="o">::</span><span class="no">Devise</span><span class="o">.</span><span class="n">config</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#1. rotas de autenticação do Devise  </span>
</span></span><span class="line"><span class="cl"> <span class="n">devise_for</span> <span class="ss">:users</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#1. rotas pra artigos  </span>
</span></span><span class="line"><span class="cl"> <span class="n">resources</span> <span class="ss">:articles</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">#1. pagina principal  </span>
</span></span><span class="line"><span class="cl"> <span class="n">get</span> <span class="s2">&#34;welcome/index&#34;</span><span class="p">,</span> <span class="ss">as</span><span class="p">:</span> <span class="s2">&#34;welcome&#34;</span>  
</span></span><span class="line"><span class="cl"> <span class="n">root</span> <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;welcome#index&#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>O que queremos agora é traduzir todas as rotas públicas, incluindo as do Devise. Porém, como explicado antes, no caso do ActiveAdmin estamos no cenário onde não precisamos de telas de administração traduzidas. Então devemos modificar o arquivo para ficar 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">I18nDemo</span><span class="o">::</span><span class="no">Application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>  
</span></span><span class="line"><span class="cl"> <span class="n">devise_for</span> <span class="ss">:users</span>  
</span></span><span class="line"><span class="cl"> <span class="n">resources</span> <span class="ss">:articles</span>  
</span></span><span class="line"><span class="cl"> <span class="n">get</span> <span class="s2">&#34;welcome/index&#34;</span><span class="p">,</span> <span class="ss">as</span><span class="p">:</span> <span class="s2">&#34;welcome&#34;</span>  
</span></span><span class="line"><span class="cl"> <span class="n">root</span> <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;welcome#index&#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="no">ActionDispatch</span><span class="o">::</span><span class="no">Routing</span><span class="o">::</span><span class="no">Translator</span><span class="o">.</span><span class="n">translate_from_file</span><span class="p">(</span>  
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;config/locales/routes.yml&#39;</span><span class="p">,</span> <span class="p">{</span>  
</span></span><span class="line"><span class="cl"> <span class="ss">prefix_on_default_locale</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span>  
</span></span><span class="line"><span class="cl"> <span class="ss">keep_untranslated_routes</span><span class="p">:</span> <span class="kp">true</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">I18nDemo</span><span class="o">::</span><span class="no">Application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>  
</span></span><span class="line"><span class="cl"> <span class="no">ActiveAdmin</span><span class="o">.</span><span class="n">routes</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>  
</span></span><span class="line"><span class="cl"> <span class="n">devise_for</span> <span class="ss">:admin_users</span><span class="p">,</span> <span class="no">ActiveAdmin</span><span class="o">::</span><span class="no">Devise</span><span class="o">.</span><span class="n">config</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>Uma dica é que o bloco de rotas depois do método <tt>#draw</tt> pode ser dividido. Isso é importante porque as rotas definidas antes do método <tt>#translate_from_file</tt> serão traduzidas, e as definidas no bloco abaixo (onde colocamos o ActiveAdmin), não serão.</p>
<p>Este código diz que vamos colocar as traduções no arquivo <tt>config/locales/routes.yml</tt>. Então criamos esse arquivo com o seguinte conteúdo:</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="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">en</span><span class="p">:</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">routes</span><span class="p">:</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="nt">pt-BR</span><span class="p">:</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">routes</span><span class="p">:</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">welcome</span><span class="p">:</span><span class="w"> </span><span class="l">bemvindo  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">new</span><span class="p">:</span><span class="w"> </span><span class="l">novo  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">edit</span><span class="p">:</span><span class="w"> </span><span class="l">editar  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">destroy</span><span class="p">:</span><span class="w"> </span><span class="l">destruir  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">password</span><span class="p">:</span><span class="w"> </span><span class="l">senha  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">sign_in</span><span class="p">:</span><span class="w"> </span><span class="l">login  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">users</span><span class="p">:</span><span class="w"> </span><span class="l">usuarios  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">cancel</span><span class="p">:</span><span class="w"> </span><span class="l">cancelar  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">article</span><span class="p">:</span><span class="w"> </span><span class="l">artigo  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">articles</span><span class="p">:</span><span class="w"> </span><span class="l">artigos  </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 bloco <tt>en.routes</tt> fica vazio porque como nossa aplicação está toda em inglês, por padrão, as rotas são em inglês. Agora no bloco <tt>pt-BR.routes</tt> basta colocarmos as palavras que queremos traduzir, seja ela nome de controller, de action, de resource, e a gem fará o resto. Se executarmos o comando <tt>rake routes</tt> depois de termos isso configurado, teremos:</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="err">…</span>  
</span></span><span class="line"><span class="cl"><span class="n">article_pt_br</span> <span class="no">GET</span> <span class="sr">/pt-BR/</span><span class="n">artigos</span><span class="o">/</span><span class="ss">:id</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="n">articles</span><span class="c1">#show {:locale=&gt;&#34;pt-BR&#34;}  </span>
</span></span><span class="line"><span class="cl"> <span class="n">article_en</span> <span class="no">GET</span> <span class="sr">/en/</span><span class="n">articles</span><span class="o">/</span><span class="ss">:id</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="n">articles</span><span class="c1">#show {:locale=&gt;&#34;en&#34;}  </span>
</span></span><span class="line"><span class="cl"><span class="no">GET</span> <span class="sr">/articles/</span><span class="ss">:id</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="n">articles</span><span class="c1">#show  </span>
</span></span><span class="line"><span class="cl"><span class="no">PUT</span> <span class="sr">/pt-BR/</span><span class="n">artigos</span><span class="o">/</span><span class="ss">:id</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="n">articles</span><span class="c1">#update {:locale=&gt;&#34;pt-BR&#34;}  </span>
</span></span><span class="line"><span class="cl"><span class="no">PUT</span> <span class="sr">/en/</span><span class="n">articles</span><span class="o">/</span><span class="ss">:id</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="n">articles</span><span class="c1">#update {:locale=&gt;&#34;en&#34;}  </span>
</span></span><span class="line"><span class="cl"><span class="no">PUT</span> <span class="sr">/articles/</span><span class="ss">:id</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="n">articles</span><span class="c1">#update  </span>
</span></span><span class="line"><span class="cl"><span class="no">DELETE</span> <span class="sr">/pt-BR/</span><span class="n">artigos</span><span class="o">/</span><span class="ss">:id</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="n">articles</span><span class="c1">#destroy {:locale=&gt;&#34;pt-BR&#34;}  </span>
</span></span><span class="line"><span class="cl"><span class="no">DELETE</span> <span class="sr">/en/</span><span class="n">articles</span><span class="o">/</span><span class="ss">:id</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="n">articles</span><span class="c1">#destroy {:locale=&gt;&#34;en&#34;}  </span>
</span></span><span class="line"><span class="cl"><span class="no">DELETE</span> <span class="sr">/articles/</span><span class="ss">:id</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="n">articles</span><span class="c1">#destroy  </span>
</span></span><span class="line"><span class="cl"><span class="n">welcome_pt_br</span> <span class="no">GET</span> <span class="sr">/pt-BR/</span><span class="n">bemvindo</span><span class="o">/</span><span class="n">index</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="n">welcome</span><span class="c1">#index {:locale=&gt;&#34;pt-BR&#34;}  </span>
</span></span><span class="line"><span class="cl"> <span class="n">welcome_en</span> <span class="no">GET</span> <span class="sr">/en/</span><span class="n">welcome</span><span class="o">/</span><span class="n">index</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="n">welcome</span><span class="c1">#index {:locale=&gt;&#34;en&#34;}  </span>
</span></span><span class="line"><span class="cl"><span class="no">GET</span> <span class="sr">/welcome/in</span><span class="n">dex</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="n">welcome</span><span class="c1">#index  </span>
</span></span><span class="line"><span class="cl"> <span class="n">root_pt_br</span> <span class="sr">/pt-BR welcome#index {:locale=&gt;&#34;pt-BR&#34;}  
</span></span></span><span class="line"><span class="cl"><span class="sr"> root_en /en</span> <span class="n">welcome</span><span class="c1">#index {:locale=&gt;&#34;en&#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>Já se perguntou sobre a necessidade de usar rotas nomeadas como <tt>new_article_path</tt> em vez de digitar direto <tt>/articles/New</tt>? Este é um motivo, a mesma rota nomeada vai levar em consideração o parâmetro implícito de localização e nos dar a URI traduzida correta sem que você precise alterar mais nada em nenhuma parte da aplicação! Win-win.</p>
<p>Precisamos que a aplicação reconheça o parâmetro <tt>locale</tt> que virá dentro do hash <tt>params</tt> que já conhemos. Vamos colocar um <tt>before_filter</tt> no <tt>/app/controllers/application_controller.rb</tt>:</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">ApplicationController</span> <span class="o">&lt;</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span>  
</span></span><span class="line"><span class="cl"> <span class="n">protect_from_forgery</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">before_filter</span> <span class="ss">:set_locale</span> <span class="n">before_filter</span> <span class="ss">:set_locale_from_url</span> 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kp">private</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">set_locale</span> 
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="n">lang</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">env</span><span class="o">[</span><span class="s1">&#39;HTTP_ACCEPT_LANGUAGE&#39;</span><span class="o">]</span> 
</span></span><span class="line"><span class="cl">      <span class="n">lang</span> <span class="o">=</span> <span class="n">lang</span><span class="o">[</span><span class="sr">/^[a-z]{2}/</span><span class="o">]</span> 
</span></span><span class="line"><span class="cl">      <span class="n">lang</span> <span class="o">=</span> <span class="ss">:&#34;pt-BR&#34;</span> 
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="n">lang</span> <span class="o">==</span> <span class="s2">&#34;pt&#34;</span> <span class="k">end</span> 
</span></span><span class="line"><span class="cl">    <span class="no">I18n</span><span class="o">.</span><span class="n">locale</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="ss">:locale</span><span class="o">]</span> <span class="o">||</span> <span class="n">lang</span> <span class="o">||</span> <span class="no">I18n</span><span class="o">.</span><span class="n">default_locale</span> <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 podemos ir em <tt>http://localhost:3000/en/articles</tt> ou <tt>http://localhost:3000/pt-BR/artigos</tt> e vamos chegar no mesmo local. Basta colocarmos na aplicação links para podemos trocar de linguagem em qualquer página que estivermos. Para isso usaremos o helper <tt>url_for</tt> que usará os parâmetros correntes para gerar o link correto da página atual. Precisamos adicionar em <tt>app/helpers/application_helper.rb</tt>:</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">ApplicationHelper</span>  
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">language_links</span>  
</span></span><span class="line"><span class="cl"> <span class="n">links</span> <span class="o">=</span> <span class="o">[]</span>  
</span></span><span class="line"><span class="cl"> <span class="no">I18n</span><span class="o">.</span><span class="n">available_locales</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">locale</span><span class="o">|</span>  
</span></span><span class="line"><span class="cl"> <span class="n">locale_key</span> <span class="o">=</span> <span class="s2">&#34;translation.</span><span class="si">#{</span><span class="n">locale</span><span class="si">}</span><span class="s2">&#34;</span>  
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">locale</span> <span class="o">==</span> <span class="no">I18n</span><span class="o">.</span><span class="n">locale</span>  
</span></span><span class="line"><span class="cl"> <span class="n">links</span> <span class="o">&lt;&lt;</span> <span class="n">link_to</span><span class="p">(</span><span class="no">I18n</span><span class="o">.</span><span class="n">t</span><span class="p">(</span><span class="n">locale_key</span><span class="p">),</span> <span class="s2">&#34;#&#34;</span><span class="p">,</span> <span class="ss">class</span><span class="p">:</span> <span class="s2">&#34;btn disabled&#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="n">links</span> <span class="o">&lt;&lt;</span> <span class="n">link_to</span><span class="p">(</span><span class="no">I18n</span><span class="o">.</span><span class="n">t</span><span class="p">(</span><span class="n">locale_key</span><span class="p">),</span> <span class="n">url_for</span><span class="p">(</span><span class="ss">locale</span><span class="p">:</span> <span class="n">locale</span><span class="o">.</span><span class="n">to_s</span><span class="p">),</span> <span class="ss">class</span><span class="p">:</span> <span class="s2">&#34;btn&#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="n">links</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">html_safe</span>  
</span></span><span class="line"><span class="cl"> <span class="k">end</span>  
</span></span><span class="line"><span class="cl"> <span class="err">…</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 podemos colocar, neste exemplo, na área de rodapé da aplicação. Então adicionamos ao <tt>app/views/layouts/application.html.erb</tt>:</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-html" data-lang="html"><span class="line"><span class="cl">…
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>%= language_links %\&gt;</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>
]]></content:encoded><category>rails</category><category>tutoriais</category><category>frontend</category></item><item><title>Asset Pipeline para Iniciantes - Parte 2</title><link>https://akitaonrails.github.io/2012/07/01/asset-pipeline-para-iniciantes-parte-2/</link><guid isPermaLink="true">https://akitaonrails.github.io/2012/07/01/asset-pipeline-para-iniciantes-parte-2/</guid><pubDate>Sun, 01 Jul 2012 23:35:00 -0300</pubDate><description>&lt;p&gt;Não perca o início deste artigo, na &lt;a href="http://akitaonrails.com/2012/07/01/asset-pipeline-para-iniciantes"target="_blank" rel="noopener"&gt;Parte 1&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Ambientes de Desenvolvimento e Produção&lt;span class="hx:absolute hx:-mt-20" id="ambientes-de-desenvolvimento-e-produção"&gt;&lt;/span&gt;
&lt;a href="#ambientes-de-desenvolvimento-e-produ%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Agora entenda uma regra básica:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;toda URL que você passar ao Rails é analisada pelo sistema de roteamento, que você configura em &lt;tt&gt;config/routes.rb&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;a exceção é tudo que estiver no diretório &lt;tt&gt;public&lt;/tt&gt;, graças ao middleware Rack::Static.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ou seja, se você pedir por &lt;tt&gt;http://localhost:3000/uploads/application.js&lt;/tt&gt; isso nem vai passar pelo Rails porque, como vimos na listagem do diretório &lt;tt&gt;public/uploads&lt;/tt&gt; acima, esse arquivo existe lá e, portanto, será servido diretamente.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Não perca o início deste artigo, na <a href="http://akitaonrails.com/2012/07/01/asset-pipeline-para-iniciantes"target="_blank" rel="noopener">Parte 1</a></p>
<h2>Ambientes de Desenvolvimento e Produção<span class="hx:absolute hx:-mt-20" id="ambientes-de-desenvolvimento-e-produção"></span>
    <a href="#ambientes-de-desenvolvimento-e-produ%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Agora entenda uma regra básica:</p>
<ul>
<li>toda URL que você passar ao Rails é analisada pelo sistema de roteamento, que você configura em <tt>config/routes.rb</tt></li>
<li>a exceção é tudo que estiver no diretório <tt>public</tt>, graças ao middleware Rack::Static.</li>
</ul>
<p>Ou seja, se você pedir por <tt>http://localhost:3000/uploads/application.js</tt> isso nem vai passar pelo Rails porque, como vimos na listagem do diretório <tt>public/uploads</tt> acima, esse arquivo existe lá e, portanto, será servido diretamente.</p>
<p>Por isso a recomendação, depois do último exercício com pré-compilação é apagar tudo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>rm -Rf public/uploads</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>&ndash;</p>
<p>Desta forma, quando você subir o servidor Rails no ambiente padrão de desenvolvimento, a mesma URL irá passar pelo Rails, pelo Asset Pipeline, e processará o arquivo <tt>app/uploads/javascripts/application.js</tt>. Assim, toda vez que fizer modificações e recarregar a página no navegador, você verá as mudanças imediatamente. Se estivesse précompilado, veria a versão antiga, sem as modificações novas.</p>
<p>A compilação em tempo real só acontece em desenvolvimento pois, obviamente, é um processo lento. Em produção, ninguém em sã consciência vai alterar nenhum arquivo direto em servidor de produção, portanto é importante ter tudo précompilado em <tt>public/uploads</tt> para economizar o aplicativo Rails do trabalho.</p>
<p>Outra coisa importante, os arquivos de assets que se chamam “application”, são automaticamente compilados. Mas digamos que você tem um stylesheet que não vai carregar no layout principal, mas só em algumas outra seções, digamos que se chame <tt>special.css</tt>, ou seja, precisamos encontrá-lo em <tt>http://localhost:3000/uploads/special.css</tt>, criamos nesta estrutura:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>app  
 assets  
 stylesheets  
 special.css</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>Porém, se reexecutarmos o rake task <tt>assets:precompile</tt> novamente, verá que esse arquivo não aparece em <tt>public/uploads</tt>. Recapitulando, arquivos “application” são automaticamente usados na précompilação. Todo asset declarado nesses manifestos, será usado dentro dos arquivos minificados “application”. Agora, para adicionar arquivos extras, precisamos declará-los manualmente em <tt>config/application.rb</tt>, adicionado uma linha 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">config</span><span class="o">.</span><span class="n">assets</span><span class="o">.</span><span class="n">precompile</span> <span class="o">+=</span> <span class="sx">%w(special.css)</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>Colocar tudo no “application” e carregar esse arquivo no layout principal para que toda página tenha tudo? Ou separar CSS e JSS que é necessário seção a seção do site? Não tenho uma regra rígida. Se a aplicação não for tão grande assim, provavelmente centralizar tudo em “application” é a saída mais simples de gerenciar. Porém se a aplicação for ou muito grande, ou se determinada seção é relativamente mais pesada em JS que as demais, por exemplo, talvez valha a pena gerenciar assets em separado, usando a configuração acima.</p>
<p>Agora sim, se executar a task de precompile novamente, teremos algo como:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>special-f7bf76f875ce5c9edd0075eaea3f6140.css  
special-f7bf76f875ce5c9edd0075eaea3f6140.css.gz  
special.css  
special.css.gz  </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>Compass e SASS<span class="hx:absolute hx:-mt-20" id="compass-e-sass"></span>
    <a href="#compass-e-sass" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Agora, por que adicionamos o Compass? Não vou fazer desta seção um tutorial de Compass, mas para que entendem o básico, podemos adicionar o seguinte CSS de exemplo em <tt>app/uploads/stylesheets/application.css.scss</tt>:</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-css" data-lang="css"><span class="line"><span class="cl"><span class="p">@</span><span class="k">import</span> <span class="s2">&#34;compass&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">box</span> <span class="p">{</span>  
</span></span><span class="line"><span class="cl"> <span class="err">font</span> <span class="err">{</span>  
</span></span><span class="line"><span class="cl"> <span class="n">family</span><span class="p">:</span> <span class="n">Lucida</span> <span class="n">Grande</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="n">size</span><span class="p">:</span> <span class="mi">12</span><span class="kt">px</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="nt">width</span><span class="o">:</span> <span class="nt">400px</span><span class="o">;</span>  
</span></span><span class="line"><span class="cl"> <span class="nt">padding</span><span class="o">:</span> <span class="nt">15px</span><span class="o">;</span>  
</span></span><span class="line"><span class="cl"> <span class="nt">border</span><span class="o">:</span> <span class="nt">1px</span> <span class="nt">solid</span> <span class="nt">black</span><span class="o">;</span>  
</span></span><span class="line"><span class="cl"> <span class="p">@</span><span class="k">include</span> <span class="nt">text-shadow</span><span class="o">(</span><span class="nt">1px</span> <span class="nt">0</span> <span class="nt">1px</span> <span class="nt">opacify</span><span class="o">(</span><span class="p">#</span><span class="nn">5c5c5c</span><span class="o">,</span> <span class="p">.</span><span class="nc">37</span><span class="o">))</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="p">@</span><span class="k">include</span> <span class="nt">box-orient</span><span class="o">(</span><span class="nt">horizontal</span><span class="o">)</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="p">@</span><span class="k">include</span> <span class="nt">border-radius</span><span class="o">(</span><span class="nt">20px</span><span class="o">,</span> <span class="nt">20px</span><span class="o">)</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"><span class="err">}</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>Na maior parte parece um CSS normal, mas a diferença maior está nos comandos <tt>@include</tt> do Sass, que serve para carregar “Mixins”. Pense em Mixin no Sass como “funções” que retornam CSS parametrizável e reusável. O Compass é uma biblioteca de Mixins que encapsulam alguns dos aspectos mais comuns de CSS. No exemplo, estamos chamando os mixins <tt>text-shadow</tt> e <tt>border-radius</tt>, cuja função deve ser bastante óbvia. Vamos ver o CSS que isso gera:</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-css" data-lang="css"><span class="line"><span class="cl"><span class="o">**</span> <span class="nt">line</span> <span class="nt">3</span><span class="o">,</span> <span class="o">../../</span><span class="nt">app</span><span class="o">/</span><span class="nt">uploads</span><span class="o">/</span><span class="nt">stylesheets</span><span class="o">/</span><span class="nt">application</span><span class="p">.</span><span class="nc">css</span><span class="p">.</span><span class="nc">scss</span> <span class="o">**/</span>  
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">box</span> <span class="p">{</span>  
</span></span><span class="line"><span class="cl"> <span class="k">width</span><span class="p">:</span> <span class="mi">400</span><span class="kt">px</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="k">padding</span><span class="p">:</span> <span class="mi">15</span><span class="kt">px</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="k">border</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">solid</span> <span class="kc">black</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="k">text-shadow</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="mi">0</span> <span class="mi">1</span><span class="kt">px</span> <span class="mh">#5c5c5c</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="kp">-webkit-</span><span class="n">box-orient</span><span class="p">:</span> <span class="kc">horizontal</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="kp">-moz-</span><span class="n">box-orient</span><span class="p">:</span> <span class="kc">horizontal</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="kp">-ms-</span><span class="n">box-orient</span><span class="p">:</span> <span class="kc">horizontal</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="n">box-orient</span><span class="p">:</span> <span class="kc">horizontal</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="kp">-webkit-</span><span class="k">border-radius</span><span class="p">:</span> <span class="mi">20</span><span class="kt">px</span> <span class="mi">20</span><span class="kt">px</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="kp">-moz-</span><span class="k">border-radius</span><span class="p">:</span> <span class="mi">20</span><span class="kt">px</span> <span class="o">/</span> <span class="mi">20</span><span class="kt">px</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="kp">-ms-</span><span class="k">border-radius</span><span class="p">:</span> <span class="mi">20</span><span class="kt">px</span> <span class="o">/</span> <span class="mi">20</span><span class="kt">px</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="kp">-o-</span><span class="k">border-radius</span><span class="p">:</span> <span class="mi">20</span><span class="kt">px</span> <span class="o">/</span> <span class="mi">20</span><span class="kt">px</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="k">border-radius</span><span class="p">:</span> <span class="mi">20</span><span class="kt">px</span> <span class="o">/</span> <span class="mi">20</span><span class="kt">px</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="c">/** line 4, ../../app/uploads/stylesheets/application.css.scss */</span>  
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">box</span> <span class="nt">font</span> <span class="p">{</span>  
</span></span><span class="line"><span class="cl"> <span class="n">family</span><span class="p">:</span> <span class="n">Lucida</span> <span class="n">Grande</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="n">size</span><span class="p">:</span> <span class="mi">12</span><span class="kt">px</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>Fica claro a função de “nesting”, para herdar estilos de um elemento pai, que usamos no elemento “font” para não repetir em blocos separados, como no CSS final. O “text-shadow” foi simples, mas o “box-orient” e border-radius&quot; automaticamente adicionaram todas as diretivas específicas de cada browser, coisas como “-webkit”, “-moz”, etc.</p>
<p>Veja a documentação de <a href="http://compass-style.org/reference/compass/"target="_blank" rel="noopener">referência do Compass</a> para encontrar todos os mixins e exemplos detalhados de como usá-los.</p>
<p>Isso é interessante, mas a principal função, para fechar todas as situações que descrevi na introdução do artigo, é a que se segue.</p>
<p>Primeiro, vamos adicionar duas novas imagens na pasta <tt>app/images/social-icons</tt>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>app  
 images  
 social-icons  
 facebook.png  
 linkedin.png  
 twitter.png  </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, vamos adicionar o seguinte HTML em <tt>app/views/home/index.html.erb</tt>:</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-html" data-lang="html"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">…
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">1. [Twitter](http://www.twitter.com)
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">2. [Facebook](http://www.facebook.com)
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">3. [LinkedIn](http://www.linkedin.com)</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>
<hr>
<p>Nada demais, apenas o objetivo de adicionar ícones de redes sociais com links a eles. Para estilizá-los, vamos completar nosso SCSS 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-css" data-lang="css"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">import</span> <span class="s2">&#34;compass&#34;</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">import</span> <span class="s2">&#34;social-icons/*.png&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">…</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">ol</span><span class="p">.</span><span class="nc">social</span> <span class="p">{</span>  
</span></span><span class="line"><span class="cl"> <span class="err">@include</span> <span class="err">horizontal-list</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="err">@each</span> <span class="err">$network</span> <span class="err">in</span> <span class="err">twitter,</span> <span class="err">facebook,</span> <span class="err">linkedin</span> <span class="err">{</span>  
</span></span><span class="line"><span class="cl"> <span class="err">li.#{$network</span><span class="p">}</span> <span class="nt">a</span> <span class="p">{</span>  
</span></span><span class="line"><span class="cl"> <span class="err">@include</span> <span class="err">social-icons_sprite(#{$network</span><span class="p">}</span><span class="o">)</span>  
</span></span><span class="line"><span class="cl"> <span class="err">}</span>  
</span></span><span class="line"><span class="cl"> <span class="err">}</span>  
</span></span><span class="line"><span class="cl"> <span class="nt">a</span> <span class="p">{</span>  
</span></span><span class="line"><span class="cl"> <span class="k">height</span><span class="p">:</span> <span class="mi">32</span><span class="kt">px</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="k">width</span><span class="p">:</span> <span class="mi">32</span><span class="kt">px</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="k">display</span><span class="p">:</span> <span class="kc">block</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="k">text-indent</span><span class="p">:</span> <span class="mi">9000</span><span class="kt">px</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="k">color</span><span class="p">:</span> <span class="mh">#FFF</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="err">}</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>&ndash;</p>
<p>Novamente, estude SASS para entender essa sintaxe e também note que novamente usamos um mixin do Compass chamado <tt>horizontal-list</tt>. Lembre que colocamos 3 novas imagens, listados acima. Agora neste SCSS, na segunda linha, fazemos um <tt>@import</tt> de todos esses ícones.</p>
<p>Agora localize esta linha: <tt>@include social-icons_sprite(#{$network})</tt>. O nome da pasta, com o sufixo “_sprite” se torna um mixin, que recebe como parâmetro o nome da imagem/sprite. O que significa isso no CSS gerado ao final? Vejamos:</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-css" data-lang="css"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* line 58, social-icons/**.png */</span>  
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">social-icons-sprite</span><span class="o">,</span> <span class="nt">ol</span><span class="p">.</span><span class="nc">social</span> <span class="nt">li</span><span class="p">.</span><span class="nc">twitter</span> <span class="nt">a</span><span class="o">,</span> <span class="nt">ol</span><span class="p">.</span><span class="nc">social</span> <span class="nt">li</span><span class="p">.</span><span class="nc">facebook</span> <span class="nt">a</span><span class="o">,</span> <span class="nt">ol</span><span class="p">.</span><span class="nc">social</span> <span class="nt">li</span><span class="p">.</span><span class="nc">linkedin</span> <span class="nt">a</span> <span class="p">{</span>  
</span></span><span class="line"><span class="cl"> <span class="k">background</span><span class="p">:</span> <span class="nb">url</span><span class="p">(</span><span class="sx">/uploads/social-icons-s25bc94da3e.png</span><span class="p">)</span> <span class="kc">no-repeat</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="err">…</span>  
</span></span><span class="line"><span class="cl"><span class="o">/</span> <span class="o">**</span><span class="nt">line</span> <span class="nt">20</span><span class="o">,</span> <span class="o">../../</span><span class="nt">app</span><span class="o">/</span><span class="nt">uploads</span><span class="o">/</span><span class="nt">stylesheets</span><span class="o">/</span><span class="nt">application</span><span class="p">.</span><span class="nc">css</span><span class="p">.</span><span class="nc">scss</span><span class="o">**</span> <span class="o">/</span>  
</span></span><span class="line"><span class="cl"><span class="nt">ol</span><span class="p">.</span><span class="nc">social</span> <span class="nt">li</span><span class="p">.</span><span class="nc">twitter</span> <span class="nt">a</span> <span class="p">{</span>  
</span></span><span class="line"><span class="cl"> <span class="k">background-position</span><span class="p">:</span> <span class="mi">0</span> <span class="mi">-32</span><span class="kt">px</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="o">/</span> <span class="o">**</span><span class="nt">line</span> <span class="nt">20</span><span class="o">,</span> <span class="o">../../</span><span class="nt">app</span><span class="o">/</span><span class="nt">uploads</span><span class="o">/</span><span class="nt">stylesheets</span><span class="o">/</span><span class="nt">application</span><span class="p">.</span><span class="nc">css</span><span class="p">.</span><span class="nc">scss</span><span class="o">**</span> <span class="o">/</span>  
</span></span><span class="line"><span class="cl"><span class="nt">ol</span><span class="p">.</span><span class="nc">social</span> <span class="nt">li</span><span class="p">.</span><span class="nc">facebook</span> <span class="nt">a</span> <span class="p">{</span>  
</span></span><span class="line"><span class="cl"> <span class="k">background-position</span><span class="p">:</span> <span class="mi">0</span> <span class="mi">-64</span><span class="kt">px</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="c">/** line 20, ../../app/uploads/stylesheets/application.css.scss */</span>  
</span></span><span class="line"><span class="cl"><span class="nt">ol</span><span class="p">.</span><span class="nc">social</span> <span class="nt">li</span><span class="p">.</span><span class="nc">linkedin</span> <span class="nt">a</span> <span class="p">{</span>  
</span></span><span class="line"><span class="cl"> <span class="k">background-position</span><span class="p">:</span> <span class="mi">0</span> <span class="mi">0</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="err">…</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>Aqui vemos o que aconteceu: as 3 imagens separadas foram concatenadas numa única, declarada no topo do CSS, chamada neste exemplo de <tt>/uploads/social-icons-s25bc94da3e.png</tt>. Agora, cada uma das classes de rede social que criamos tem um reposicionamento da mesma imagem, como: <tt>background-position: 0 -64px;</tt>.</p>
<p>Isto resolve o ponto 2 da introdução do artigo, que estava pendente: gerenciamento de sprites. Dependendo da quantidade de pequenas imagens que uma única página do seu site tem, somente este truque pode causar um impacto positivo que seus usuários irão notar rapidamente por causa do aumento na velocidade de renderização.</p>
<p>Para complementar, sempre que usarmos chamadas como <tt>background: url(/uploads/social-icons-s25bc94da3e.png)</tt> no CSS, nunca devemos digitar a URL absoluta manualmente. Devemos deixar o Asset Pipeline cuidar disso, como ele fez aqui no caso dos sprites.</p>
<p>Vejamos um exemplo mais concreto. Vamos adicionar o seguinte ao nosso <tt>application.css.scss</tt>:</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-css" data-lang="css"><span class="line"><span class="cl"><span class="nt">h1</span> <span class="p">{</span>  
</span></span><span class="line"><span class="cl"> <span class="k">padding-left</span><span class="p">:</span> <span class="mi">60</span><span class="kt">px</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="k">height</span><span class="p">:</span> <span class="mi">70</span><span class="kt">px</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"> <span class="k">background</span><span class="p">:</span> <span class="nb">url</span><span class="p">(</span><span class="s2">&#34;/uploads/rails.png&#34;</span><span class="p">)</span> <span class="kc">no-repeat</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 que confunde é que isso de fato funciona. Porém, caímos nos problema mencionados no início do artigo: sem o número timestamp, se atualizarmos a imagem, os usuários ficarão travados na versão antiga em cache local. Se quisermos migrar para CDN vamos ter problemas de mudar todas essas URLs manualmente, etc. Portanto, no caso de SASS o correto é usar a função <tt>image-url</tt> e fazer 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-css" data-lang="css"><span class="line"><span class="cl"><span class="nt">background</span><span class="o">:</span> <span class="nt">image-url</span><span class="o">(</span><span class="s2">&#34;rails.png&#34;</span><span class="o">)</span> <span class="nt">no-repeat</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>
<hr>
<p>Isto irá gerar a URL correta. Temos as seguinte variações:</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">image</span><span class="o">-</span><span class="n">url</span><span class="p">(</span><span class="s2">&#34;rails.png&#34;</span><span class="p">)</span> <span class="c1"># url(/uploads/rails.png)  </span>
</span></span><span class="line"><span class="cl"><span class="n">image</span><span class="o">-</span><span class="n">path</span><span class="p">(</span><span class="s2">&#34;rails.png&#34;</span><span class="p">)</span> <span class="c1"># &#34;/uploads/rails.png&#34;.  </span>
</span></span><span class="line"><span class="cl"><span class="n">asset</span><span class="o">-</span><span class="n">url</span><span class="p">(</span><span class="s2">&#34;rails.png&#34;</span><span class="p">,</span> <span class="n">image</span><span class="p">)</span> <span class="c1"># url(/uploads/rails.png)  </span>
</span></span><span class="line"><span class="cl"><span class="n">asset</span><span class="o">-</span><span class="n">path</span><span class="p">(</span><span class="s2">&#34;rails.png&#34;</span><span class="p">,</span> <span class="n">image</span><span class="p">)</span> <span class="c1"># &#34;/uploads/rails.png&#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>Agora, se for necessário URLs de assets dentro do Javascript, não há equivalente no <tt>application.js</tt> puro, por isso precisaríamos renomeá-lo para <tt>application.js.erb</tt> e então a mesma regra que usaríamos em views HTML ERB normais valem:</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">var</span> <span class="n">imagem</span> <span class="o">=</span> <span class="s2">&#34;&lt;%= image_path(&#34;</span><span class="n">rails</span><span class="o">.</span><span class="n">png</span><span class="s2">&#34;) \&gt;&#34;</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"><span class="n">var</span> <span class="n">tag_imagem</span> <span class="o">=</span> <span class="s2">&#34;&lt;= image_tag(&#34;</span><span class="n">rails</span><span class="o">.</span><span class="n">png</span><span class="s2">&#34;) \&gt;&#34;</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"><span class="n">var</span> <span class="n">audio</span> <span class="o">=</span> <span class="s2">&#34;&lt;= audio_path(&#34;</span><span class="n">rails</span><span class="o">.</span><span class="n">mp3</span><span class="s2">&#34;) \&gt;&#34;</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"><span class="n">var</span> <span class="n">tag_audio</span> <span class="o">=</span> <span class="s2">&#34;&lt;= image_tag(&#34;</span><span class="n">rails</span><span class="o">.</span><span class="n">mp3</span><span class="s2">&#34;) \&gt;&#34;</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"><span class="n">var</span> <span class="n">video</span> <span class="o">=</span> <span class="s2">&#34;&lt;= video_path(&#34;</span><span class="n">rails</span><span class="o">.</span><span class="n">m4v</span><span class="s2">&#34;) \&gt;&#34;</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"><span class="n">var</span> <span class="n">tag_video</span> <span class="o">=</span> <span class="s2">&#34;&lt;= image_tag(&#34;</span><span class="n">rails</span><span class="o">.</span><span class="n">m4v</span><span class="s2">&#34;) %\&gt;&#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>Vejam a documentação do <a href="http://api.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html"target="_blank" rel="noopener">ActionView::Helpers::AssetTagHelper</a> para entender melhor sobre estes helpers, mas o importante é: se estiver escrevendo a URL de um asset manualmente, como um string, você está fazendo errado.</p>
<h2>NGINX<span class="hx:absolute hx:-mt-20" id="nginx"></span>
    <a href="#nginx" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Não vou entrar em detalhes sobre como configurar um NGINX completo, mas fica um lembrete para não esquecer de adicionar à configuração do servidor o seguinte trecho:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>location ~ ^/uploads/ {  
 expires 1y;  
 add_header Cache-Control public;  
 add_header Last-Modified &#34;&#34;;  
 add_header ETag &#34;&#34;;  
 break;  
}</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 garantirá que o browser do usuário guarde todos os assets no seu cache local para não pedir novamente. Com o Asset Pipeline, como explicamos exaustivamente acima, assets atualizados são imunes ao cache local.</p>
<p>E para diminuir ainda mais a quantidade de bits transportado entre o servidor e o browser dos usuários, lembre de checar se o suporte a gzip está ativado:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>
# 1. Gzip Settings  

gzip on;  
gzip_disable &#34;msie6&#34;;

gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml&#43;rss text/javascript;  </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>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>Como podem ver, o Asset Pipeline é bastante simples para a grande maioria dos casos de uso. As regras são diferentes mas simples:</p>
<ul>
<li>Assets devem ir todas em <tt>app/uploads</tt></li>
<li>Em desenvolvimento <tt>public/uploads</tt> deve ficar vazio. Em produção, sempre execute <tt>rake assets:precompile</tt></li>
<li>Não digite URL de assets manualmente, use os helpers de URL</li>
</ul>
<p>Isso lhe dará, “de graça”:</p>
<ul>
<li>concatenação de arquivos de assets, minificação de javascript e stylesheets</li>
<li>compatibilidade com caches, CDNs e suporte a gzip</li>
<li>gerenciamento de sprites numa única imagem com posicionamento via CSS</li>
<li>suporte a diferentes geradores de templates, como SASS</li>
</ul>
<p>Expanda seus conhecimentos, aprenda mais sobre:</p>
<ul>
<li><a href="http://thoughtbot.com/bourbon/"target="_blank" rel="noopener">Bourbon</a></li>
<li><a href="http://compass-style.org/reference/compass/"target="_blank" rel="noopener">Compass</a></li>
<li><a href="http://guides.rubyonrails.org/asset_pipeline.html"target="_blank" rel="noopener">Rails Guides</a></li>
<li><a href="http://coderberry.me/blog/2012/04/24/asset-pipeline-for-dummies/"target="_blank" rel="noopener">Asset Pipeline for Dummies</a></li>
</ul>
<p>Esqueçam rumores, opiniões contrárias, rants e trolls. O Asset Pipeline certamente não é simples. Porém está longe do bicho de sete cabeças que costumam descrever. No início haviam muitos bugs, que já foram corrigidos e, toda vez que alguém falar em “Asset Poopline”, normalmente é problema de de <a href="http://bios.6te.net/"target="_blank" rel="noopener">BIOS</a> mais do que de ferramenta. Google e Stackoverflow, for the help.</p>
]]></content:encoded><category>rails</category><category>frontend</category><category>tutoriais</category></item><item><title>Asset Pipeline para Iniciantes</title><link>https://akitaonrails.github.io/2012/07/01/asset-pipeline-para-iniciantes/</link><guid isPermaLink="true">https://akitaonrails.github.io/2012/07/01/asset-pipeline-para-iniciantes/</guid><pubDate>Sun, 01 Jul 2012 05:34:00 -0300</pubDate><description>&lt;p&gt;Este é provavelmente um dos assuntos mais confusos para quem está iniciando com Ruby on Rails. Antigamente, as regras eram simples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;coloque todos os seus &lt;em&gt;assets&lt;/em&gt; (imagens, stylesheets e javascripts) organizados nas pastas &lt;tt&gt;public/images&lt;/tt&gt;, &lt;tt&gt;public/stylesheets&lt;/tt&gt;, &lt;tt&gt;public/javascripts&lt;/tt&gt;;&lt;/li&gt;
&lt;li&gt;utilize &lt;em&gt;helpers&lt;/em&gt; como &lt;tt&gt;image_tag&lt;/tt&gt;, &lt;tt&gt;stylesheet_link_tag&lt;/tt&gt; e &lt;tt&gt;javascript_include_tag&lt;/tt&gt;;&lt;/li&gt;
&lt;li&gt;configure seu servidor web (Apache, NGINX) para servir URIs como &lt;tt&gt;/images/rails.png&lt;/tt&gt; diretamente de &lt;tt&gt;public/images/rails.png&lt;/tt&gt; para não precisar passar pelo Rails;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pronto, está tudo preparado para funcionar. Porém, existiam e ainda existem muitas situações que essa regra não cobria e diversas técnicas, “boas práticas” e gems externas precisaram ser criadas para resolvê-las. Em particular, temos as seguintes situações cotidianas em desenvolvimento web:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Este é provavelmente um dos assuntos mais confusos para quem está iniciando com Ruby on Rails. Antigamente, as regras eram simples:</p>
<ul>
<li>coloque todos os seus <em>assets</em> (imagens, stylesheets e javascripts) organizados nas pastas <tt>public/images</tt>, <tt>public/stylesheets</tt>, <tt>public/javascripts</tt>;</li>
<li>utilize <em>helpers</em> como <tt>image_tag</tt>, <tt>stylesheet_link_tag</tt> e <tt>javascript_include_tag</tt>;</li>
<li>configure seu servidor web (Apache, NGINX) para servir URIs como <tt>/images/rails.png</tt> diretamente de <tt>public/images/rails.png</tt> para não precisar passar pelo Rails;</li>
</ul>
<p>Pronto, está tudo preparado para funcionar. Porém, existiam e ainda existem muitas situações que essa regra não cobria e diversas técnicas, “boas práticas” e gems externas precisaram ser criadas para resolvê-las. Em particular, temos as seguintes situações cotidianas em desenvolvimento web:</p>
<ul>
<li>quando se tem muitos assets, como javascripts, é considerado boa prática “minificá-los”, ou seja, otimizar ao máximo a quantidade de bytes eliminando supérfluos como espaços em branco e quebras de linha, nomes de variáveis e funções longas, etc. E além disso concatenar a maior quantidade de arquivos num único quanto possível. Em desenvolvimento, precisamos ter todos abertos e individuais para facilitar o debugging, mas em produção o correto é “compilá-los”</li>
<li>cache precisa ser usado o máximo possível e escrever o caminho a um path manualmente, como <tt><img src=“/images/rails.png”/></tt> é ruim, pois se precisarmos mudar o conteúdo dessa imagem, os usuários precisariam limpar seus caches pois o correto é configurarmos os servidores web com diretivas para manter assets no cache local por um longo período de tempo (1 ano ou mais). Helpers como <tt>image_tag</tt> criavam caminhos como <tt><img src=“/images/rails.png?12345678”/></tt>, sendo esse número derivado do timestamp de modificação do asset. Assim, se o asset era atualizado esse número mudava. Mas isso não funciona bem com muitos tipos de caches e proxies, que ignoram o que vem depois do “?”</li>
<li>quando uma página possui dezenas ou às vezes centenas de pequenas imagens e ícones (setas, botões, logotipos de seção, linhas, bordas, etc), o correto é usar a mesma técnica que usamos com stylesheets e javascripts: concatenar muitas imagens em um único arquivo maior e então utilizar CSS para manipular a posição x e y dentro dessa única imagem grande para posicioná-la corretamente onde precisamos.</li>
<li>começamos a utilizar vários tipos diferentes de geradores de templates, como LESS e SASS para gerar stylesheets, CoffeeScript para gerar Javascript, além do próprio ERB para adicionar conteúdo dinâmico nos templates.</li>
</ul>
<p>Para resolver essas e outras situações é que foi criado o chamado <a href="http://guides.rubyonrails.org/asset_pipeline.html"target="_blank" rel="noopener">Asset Pipeline</a>, que é um conjunto de bibliotecas e convenções para resolver o problema de assets da melhor forma possível. O Asset Pipeline sozinho não resolve tudo, ele é um framework para que seja possível integrar diferentes soluções de forma organizada.</p>
<p>Tudo que será explicado neste artigo vale para o Rails 3.2 e superior, existem diferenças importantes nas versões anteriores que não serão tratadas aqui. Leia o Rails Guides, especialmente os Release Notes de cada versão.</p>
<h2>Iniciando um projeto Rails<span class="hx:absolute hx:-mt-20" id="iniciando-um-projeto-rails"></span>
    <a href="#iniciando-um-projeto-rails" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Quando iniciamos um novo projeto com o comando <tt>rails new novo_projeto</tt>, o primeiro arquivo que você vai querer mexer é o <tt>Gemfile</tt>:</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">#1. Original  </span>
</span></span><span class="line"><span class="cl"><span class="n">group</span> <span class="ss">:assets</span> <span class="k">do</span>  
</span></span><span class="line"><span class="cl"> <span class="n">gem</span> <span class="s1">&#39;sass-rails&#39;</span><span class="p">,</span> <span class="s1">&#39;~\&gt; 3.2.3&#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; 3.2.1&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#1. See https://github.com/sstephenson/execjs#readme for more supported runtimes</span>
</span></span><span class="line"><span class="cl"><span class="c1">#2. gem &#39;therubyracer&#39;, :platforms =\&gt; :ruby</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;uglifier&#39;</span><span class="p">,</span> <span class="s1">&#39;\&gt;= 1.0.3&#39;</span>
</span></span><span class="line"><span class="cl">
</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">#1. Recomendado para iniciar  </span>
</span></span><span class="line"><span class="cl"><span class="n">group</span> <span class="ss">:assets</span> <span class="k">do</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;compass-rails&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">#1. See https://github.com/sstephenson/execjs#readme for more supported runtimes  </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="o">=&gt;</span> <span class="ss">:ruby</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;uglifier&#39;</span>
</span></span><span class="line"><span class="cl">
</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>Não vamos entrar na <a href="http://akitaonrails.com/2011/04/16/a-controversia-coffeescript"target="_blank" rel="noopener">controvérsia agora sobre CoffeeScript</a>. Se você está iniciando, esqueça CoffeeScript por enquanto para não complicar ainda mais. Por outro lado, usar o <a href="http://compass-style.org"target="_blank" rel="noopener">Compass</a> e particularmente o <a href="https://github.com/Compass/compass-rails/"target="_blank" rel="noopener">Compass Rails</a> é algo que nem precisamos discutir já que o Compass provê diversos mixins de Sass muito úteis.</p>
<p>Aliás, se você ainda não conhece <a href="http://sass-lang.com"target="_blank" rel="noopener">SASS</a>, faça a você mesmo um favor e aprenda. Se você entende CSS, não vai ter problemas entendendo Sass, em particular a versão “SCSS” ou “Sassy CSS” que não é mais do que um conjunto acima do CSS3. Lembrando que mesmo escolhendo usar SASS podemos misturar arquivos <tt>.css.scss</tt> e arquivos convencionais <tt>.css</tt> no mesmo projeto.</p>
<p>Ao modificar o arquivo <tt>Gemfile</tt>, lembre-se de executar os seguintes comandos no terminal:</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-bash" data-lang="bash"><span class="line"><span class="cl">bundle</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 exercitar, vamos criar um simples controller com uma única página dinâmica para entender o que podemos fazer com isso. De volta ao terminal faça 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-bash" data-lang="bash"><span class="line"><span class="cl">rm public/index.html  
</span></span><span class="line"><span class="cl">bundle <span class="nb">exec</span> rails g controller home index  </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></li>
</ul>
<p>O resultado será:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>create app/controllers/home_controller.rb 
route get &#34;home/index&#34; 
invoke erb create app/views/home 
create app/views/home/index.html.erb 
invoke test_unit create 
test/functional/home_controller_test.rb 
invoke helper create app/helpers/home_helper.rb 
invoke test_unit create 
test/unit/helpers/home_helper_test.rb 
invoke assets invoke js create app/uploads/javascripts/home.js 
invoke scss create app/uploads/stylesheets/home.css.scss</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>
<hr>
<p>E para combinar, já que estamos recomendando SCSS, vamos apagar o arquivo <tt>app/stylesheets/application.css</tt> e criar um novo:</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-bash" data-lang="bash"><span class="line"><span class="cl">rm app/stylesheets/application.css  
</span></span><span class="line"><span class="cl">touch app/stylesheets/application.css.scss  </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 nesse novo arquivo podemos colocar somente:</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-css" data-lang="css"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">import</span> <span class="s2">&#34;compass&#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>Outra boa prática é ignorar o diretório <tt>public/uploads</tt> do repositório Git (você utilizar <a href="/2012/04/09/screencasts-liberados-gratuitamente/">Git</a>, correto?). Faça 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-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;public/uploads&#34;</span> &gt;&gt; .gitignore  </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 agora já podemos iniciar o servidor local de Rails e examinar o que temos até agora:</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-bash" data-lang="bash"><span class="line"><span class="cl">bundle <span class="nb">exec</span> rails s  </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>Processo de Pré-Compilação<span class="hx:absolute hx:-mt-20" id="processo-de-pré-compilação"></span>
    <a href="#processo-de-pr%c3%a9-compila%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Resumidamente, em termos de assets temos os seguintes principais elementos e estrutura:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>app  
 assets  
 images  
 rails.png  
 javascripts  
 application.js  
 home.js  
 stylesheets  
 application.css.scss  
 home.css.scss  
 views  
 home  
 index.html.erb  
 layouts  
 application.html.erb  
config  
 application.rb  
public  
 assets  
Gemfile  
Gemfile.lock  </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 código fonte do layout <tt>app/views/layouts/application.html.erb</tt> contém 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-html" data-lang="html"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>NovoProjeto<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl"> <span class="err">&lt;</span>%= stylesheet_link_tag &#34;application&#34;, :media =&gt; &#34;all&#34; \&gt;  
</span></span><span class="line"><span class="cl"> <span class="err">&lt;</span>= javascript_include_tag &#34;application&#34; \&gt;  
</span></span><span class="line"><span class="cl"> <span class="err">&lt;</span>= csrf_meta_tags %\&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>%= yield %\&gt;</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>
<hr>
<p>Se você já tinha visto até o Rails 2.x, um layout padrão ERB não é tão diferente. Com o servidor de pé, em ambiente de desenvolvimento, vejamos o HTML gerado ao abrir <tt>http://localhost:3000/home/index</tt>:</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-html" data-lang="html"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>NovoProjeto<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;/uploads/application.css&#34;</span> <span class="na">media</span><span class="o">=</span><span class="s">&#34;all&#34;</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/css&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;/uploads/jquery.js?body=1&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>  
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;/uploads/jquery_ujs.js?body=1&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>  
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;/uploads/home.js?body=1&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>  
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;/uploads/application.js?body=1&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>  
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">meta</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;authenticity_token&#34;</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;csrf-param&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">meta</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;OFmZwwtshevVgcs1DUg56WVIQ8NcJZsri/nUubhEJCk=&#34;</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;csrf-token&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"># Home#index</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>Find me in app/views/home/index.html.erb</p>
<hr>
<p>No HTML gerado, note que os links para os assets apontam todos para <tt>/uploads</tt>. Além disso note que a chamada <tt>javascript_include_tag(“application”)</tt> expandiu para 4 javascripts diferentes. Para entender isso, precisamos examinar mais de perto o arquivo <tt>app/uploads/javascripts/application.js</tt>:</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-javascript" data-lang="javascript"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">…</span>  
</span></span><span class="line"><span class="cl"><span class="c1">//= require jquery  
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require jquery_ujs  
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require_tree .  
</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>Sobre o detalhe do jQuery, todo novo projeto Rails tem declarado <tt>gem ‘jquery-rails’</tt> no <tt>Gemfile</tt>.</p>
<p>Os arquivos <tt>application.∗</tt> podendo “∗” ser “js”, “js.coffee”, “css”, “css.scss”, “css.sass”, “js.erb”, “css.erb”, etc. Eles são conhecidos como “Manifestos”. São arquivos “guarda-chuva” que declaram todos os outros arquivos que eles dependem, em ordem, para serem concatenados em um único arquivo ao serem compilados.</p>
<p>No exemplo padrão, no <tt>application.js</tt> a primeira e segunda linha com <tt>require</tt> declaram o <tt>jquery.js</tt> e depois o <tt>jquery_ujs.js</tt> e a terceira linha com <tt>require_tree .</tt> manda carregar todos os outros arquivos javascripts no mesmo diretório que, por acaso, tem o <tt>home.js</tt> criado pelo gerador de controller que usamos antes. Agora vejam novamente o HTML gerado e verá que são exatamente os javascripts carregados na ordem que expliquei, sendo o quarto o próprio conteúdo do arquivo <tt>application.js</tt>.</p>
<p>Normalmente usar o <tt>require_tree .</tt> não é exatamente ruim se os javascripts não dependem da ordem de carregamento, mas você provavelmente vai querer declarar explicitamente coisas como plugins de jQuery para garantir que eles estão carregados antes de poder usá-los.</p>
<p>Para explicar como tudo isso funciona é importante pararmos o servidor Rails que subimos antes e reexecutá-lo em modo produçao:</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-bash" data-lang="bash"><span class="line"><span class="cl">bundle <span class="nb">exec</span> rails s e production  </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, se tentarmos carregar a mesma URL <tt>http://localhost:3000/home/index</tt> no browser, receberemos um erro 500 com o seguinte backtrace:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Started GET &#34;/home/index&#34; for 127.0.0.1 at 2012-07-01 03:31:55 -0300  
Connecting to database specified by database.yml  
Processing by HomeController#index as HTML  
 Rendered home/index.html.erb within layouts/application (12.0ms)  
Completed 500 Internal Server Error in 155ms

ActionView::Template::Error (application.css isn&#39;t precompiled):  
 2:
 3:

 4: &lt;title&gt;NovoProjeto&lt;/title&gt;

5: &lt;%= stylesheet_link_tag &#34;application&#34;, :media =&gt; &#34;all&#34; \&gt;  
 6: &lt;= javascript_include_tag &#34;application&#34; \&gt;  
 7: &lt;= csrf_meta_tags %\&gt;  
 8:
 app/views/layouts/application.html.erb:5:in `_app_views_layouts_application_html_erb__408740569075721590_70099961775620&#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>Este é o sinal que não realizamos um passo importante que deve ser executado toda vez que você realizar uma atualização em produção: pré-compilar os assets. É o processo que lê os arquivos manifesto e realiza a concatenação dos arquivos declarados e sua minificação (utilizando a gem <a href="https://github.com/lautis/uglifier"target="_blank" rel="noopener">Uglifier</a>). Portanto, precisamos executar 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-bash" data-lang="bash"><span class="line"><span class="cl">bundle <span class="nb">exec</span> rake assets:precompile</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>Lembrando que antes disso o diretório <tt>public/uploads</tt> estava originalmente vazio (e em desenvolvimento, você deve garantir que esse diretório esteja sempre vazio, já explicamos porque). Após executar a a pré-compilação, esse diretório terá os seguintes arquivos:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>application-363316399c9b02b9eb98cd1b13517abd.js  
application-363316399c9b02b9eb98cd1b13517abd.js.gz  
application-7270767b2a9e9fff880aa5de378ca791.css  
application-7270767b2a9e9fff880aa5de378ca791.css.gz  
application.css  
application.css.gz  
application.js  
application.js.gz  
manifest.yml  
rails-be8732dac73d845ac5b142c8fb5f9fb0.png  
rails.png  </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 entender vejamos o código-fonte do HTML gerado em produção:</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-html" data-lang="html"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html\&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>NovoProjeto<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;/uploads/application-7270767b2a9e9fff880aa5de378ca791.css&#34;</span> <span class="na">media</span><span class="o">=</span><span class="s">&#34;all&#34;</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/css&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;/uploads/application-363316399c9b02b9eb98cd1b13517abd.js&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>  
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">meta</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;authenticity_token&#34;</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;csrf-param&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">meta</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;OFmZwwtshevVgcs1DUg56WVIQ8NcJZsri/nUubhEJCk=&#34;</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;csrf-token&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"># Home#index</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>Find me in app/views/home/index.html.erb</p>
<hr>
<p>Compare este HTML com o anterior que analisamos gerado em ambiente de desenvolvimento. Em vez de 1 arquivo CSS e 4 Javascripts, temos apenas 1 CSS e 1 Javascript.</p>
<p>Para entendermos melhor, vejamos o que tem no arquivo <tt>public/uploads/manifest.yml</tt>:</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-bash" data-lang="bash"><span class="line"><span class="cl">rails.png: rails-be8732dac73d845ac5b142c8fb5f9fb0.png  
</span></span><span class="line"><span class="cl">application.js: application-363316399c9b02b9eb98cd1b13517abd.js  
</span></span><span class="line"><span class="cl">application.css: application-7270767b2a9e9fff880aa5de378ca791.css  </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, o arquivo <tt>application.js</tt> é idêntico ao <tt>application-363316399c9b02b9eb98cd1b13517abd.js</tt>. Se algum dos arquivos declarados no manifesto <tt>app/uploads/javascripts/application.js</tt> mudar, esse número sufixo irá mudar e o HTML apontará para o novo. Olhando novamente nossa lista de situações que precisam ser solucionadas, que apresentamos no início do arquivo, temos já até aqui a solução de 3 dos pontos:</p>
<ul>
<li>o ponto 1 explica o problema que é sempre melhor ter apenas um único arquivo de CSS ou JS do que dezenas deles separados, pois o navegador só precisa ter o peso de pedir um único arquivo (quanto mais arquivos, independente do tamanho, mais tempo vai demorar para a página renderizar). Além disso, graças ao Uglifier teremos esses arquivos “minificados”, ou seja, reescritos de forma a minimizar seu tamanho em bytes sem modificar a lógica da programação. Além disso, o pipeline vai um passo além e tem as versões de todos esses arquivos com extensão “.gz” que significa “gzip”. Se o browser fizer uma requisição dizendo que aceita conteúdo compactado em formato zip, se o web server disser que entende zip, ele pode diretamente enviar a versão do arquivo com extensão “.gz”. No exemplo acima, isso significa enviar um JS de 34kb em vez dos 98kb descomprimidos.</li>
<li>o ponto 2 explica o problema de quanto assets mudam mas o browser guarda em cache baseado na URL que ele carregou. Se ele pedisse <tt>http://localhost:3000/uploads/application.js</tt>, mesmo se o JS fosse modificado, o browser não pediria novamente porque a boa prática diz que o web server deveria enviar cabeçalho dizendo para esse tipo de arquivo ficar em cache por 1 ano. Mas como o HTML na realidade pede por <tt>http://localhost:3000/uploads/application-363316399c9b02b9eb98cd1b13517abd.js</tt>, e se o JS mudar, esse número vai mudar também, não importa mais que esses assets fiquem indefinidamente em cache, pois da próxima vez que precisar dar versão mais nova, o nome do arquivo será completamente diferente do que estava no cache.</li>
<li>finalmente, o ponto 4 explica sobre os diferente geradores de assets. Implicitamente já podemos ver isso no caso do SASS, onde arquivos com extensão “.css.scss” são convertidos em “.css”.</li>
</ul>
<p>Para reforçar o ponto 2, vamos adicionar a seguinte função no arquivo <tt>app/uploads/javascripts/application.js</tt>:</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-javascript" data-lang="javascript"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">helloWorld</span><span class="p">()</span> <span class="p">{</span>  
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Hello World&#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>Agora executamos a pré-compilação novamente:</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-bash" data-lang="bash"><span class="line"><span class="cl">bundle <span class="nb">exec</span> rake assets:precompile  </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 que temos no diretório <tt>public/uploads</tt> será:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>application-363316399c9b02b9eb98cd1b13517abd.js  
application-363316399c9b02b9eb98cd1b13517abd.js.gz  
application-4fee97e9e402a9816ab9b3edf7a4c08b.js  
application-4fee97e9e402a9816ab9b3edf7a4c08b.js.gz  
application-7270767b2a9e9fff880aa5de378ca791.css  
application-7270767b2a9e9fff880aa5de378ca791.css.gz  
application.css  
application.css.gz  
application.js  
application.js.gz  
manifest.yml  
rails-be8732dac73d845ac5b142c8fb5f9fb0.png  
rails.png</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>
<ul>
<li></li>
</ul>
<p>Como não limpamos o diretório antes, temos a versão antiga e a recente. Compare, a anterior se chamava <tt>application-363316399c9b02b9eb98cd1b13517abd.js</tt> e a nova com a função de demonstração se chama <tt>application-4fee97e9e402a9816ab9b3edf7a4c08b.js</tt>. Reiniciando o servidor Rails no ambiente de produção e vendo o novo HTML gerado, verá este trecho:</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-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;/uploads/application-4fee97e9e402a9816ab9b3edf7a4c08b.js&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&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>
<hr>
<p>Espero que esse detalhamente deixe bem claro o objetivo do pipeline de pré-compilação e as convenções de nomenclatura e quais problemas ele soluciona. Num artigo que escrevi recentemente chamado <a href="http://akitaonrails.com/2012/04/20/heroku-tips-enciclopedia-do-heroku"target="_blank" rel="noopener">Enciclopédia do Heroku</a> explico como mover esses assets pré-compilados para uma conta na Amazon S3, com o objetivo de descarregar processamento do seu servidor de aplicação, servindo a partir de um <a href="http://pt.wikipedia.org/wiki/Content_Delivery_Network"target="_blank" rel="noopener">CDN</a>, o que deve melhorar a experiência do usuário final ao carregar assets de um servidor mais próximo.</p>
<h2>Parte 2<span class="hx:absolute hx:-mt-20" id="parte-2"></span>
    <a href="#parte-2" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O artigo ficou longo, por isso vamos continuar na <a href="http://akitaonrails.com/2012/07/02/asset-pipeline-para-iniciantes-parte-2"target="_blank" rel="noopener">Parte 2</a></p>
]]></content:encoded><category>rails</category><category>frontend</category><category>tutoriais</category></item><item><title>Hanselminutes: Entrevista com David Hansson e Martin Fowler</title><link>https://akitaonrails.github.io/2007/07/08/hanselminutes-entrevista-com-david-hansson-e-martin-fowler/</link><guid isPermaLink="true">https://akitaonrails.github.io/2007/07/08/hanselminutes-entrevista-com-david-hansson-e-martin-fowler/</guid><pubDate>Sun, 08 Jul 2007 00:20:09 -0300</pubDate><description>&lt;p&gt;Como &lt;a href="http://www.akitaonrails.com/articles/2007/04/14/off-topic-seja-arrogante"target="_blank" rel="noopener"&gt;disse antes&lt;/a&gt;, eu ouço muitos podcasts. Todos os dias meu iTunes baixa mais e mais. Isso já me deixou com uma fila enorme de programas ainda a ouvir. Este &lt;a href="http://www.hanselman.com/blog/HanselminutesPodcast65MartinFowlerAndDavidHeinemeierHansson.aspx"target="_blank" rel="noopener"&gt;episódio&lt;/a&gt; do podcast &lt;strong&gt;Hanselminutes&lt;/strong&gt; , em especial, está me esperando desde a RailsConf que aconteceu mês passado.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/computerzen/505364200/"target="_blank" rel="noopener"&gt;&lt;img src="https://akitaonrails.s3.amazonaws.com/files/Scott_Hanselman.jpg" alt="" loading="lazy" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;O host do programa é Scott Hanselman, que na realidade é do mundo .NET, Microsoft, mas que também cobre outras plataformas. Nesse episódio ele estava na RailsConf 2007, em sua cidade natal que é Portland, no Oregon. Ele teve a oportunidade de entrevistar David Heinemeier Hansson e Martin Fowler ao mesmo tempo. Vocês podem ler a transcrição literal da entrevista em &lt;a href="http://perseus.franklins.net/hanselminutes_0065.pdf"target="_blank" rel="noopener"&gt;PDF&lt;/a&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Como <a href="http://www.akitaonrails.com/articles/2007/04/14/off-topic-seja-arrogante"target="_blank" rel="noopener">disse antes</a>, eu ouço muitos podcasts. Todos os dias meu iTunes baixa mais e mais. Isso já me deixou com uma fila enorme de programas ainda a ouvir. Este <a href="http://www.hanselman.com/blog/HanselminutesPodcast65MartinFowlerAndDavidHeinemeierHansson.aspx"target="_blank" rel="noopener">episódio</a> do podcast <strong>Hanselminutes</strong> , em especial, está me esperando desde a RailsConf que aconteceu mês passado.</p>
<p><a href="http://www.flickr.com/photos/computerzen/505364200/"target="_blank" rel="noopener"><img src="https://akitaonrails.s3.amazonaws.com/files/Scott_Hanselman.jpg" alt=""  loading="lazy" /></a></p>
<p>O host do programa é Scott Hanselman, que na realidade é do mundo .NET, Microsoft, mas que também cobre outras plataformas. Nesse episódio ele estava na RailsConf 2007, em sua cidade natal que é Portland, no Oregon. Ele teve a oportunidade de entrevistar David Heinemeier Hansson e Martin Fowler ao mesmo tempo. Vocês podem ler a transcrição literal da entrevista em <a href="http://perseus.franklins.net/hanselminutes_0065.pdf"target="_blank" rel="noopener">PDF</a>.</p>
<p>Eu achei esta uma das melhores entrevistas dos dois e, como sempre, resolvi que devia traduzir. Porém, essa transcrição está muito literal. Ou seja, está exatamente como eles falaram. O problema disso é que o formato está extremamente informal, disperso, <em>quebradiço</em>, por assim dizer. Eu comecei a traduzir mas o texto ficou tão emaranhado que quase no final resolvi jogar tudo fora e começar novamente. Em vez de traduzir ipsis literis como falaram, vou tentar expôr as idéias principais deles de forma mais organizada para que vocês consigam entender. Mesmo assim, recomendo ouvir o podcast, no áudio a conversa faz mais sentido.</p>
<h2>HTML e CSS<span class="hx:absolute hx:-mt-20" id="html-e-css"></span>
    <a href="#html-e-css" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O primeiro assunto começa quando Scott diz estar cansado de ficar escrevendo tags para criar telas. Ele acha que numa atualidade onde temos efeitos tridimensionais no Linux (com o projeto Beryl), o Mac OS X (que já levou 3D em conta há anos) e o recém-chegado Windows Vista, o visual ‘limitado’ do HTML talvez esteja mostrando sua idade.</p>
<p>David e Martin discordam disso, assim como este <em>humilde</em> tradutor que vos fala. Na realidade HTML e CSS são perfeitos para o trabalho. É um argumento que já foi discutido várias vezes nos últimos anos, mas é inegável que em 10 anos de história, nenhuma outra tecnologia conseguiu fazer frente ao HTML.</p>
<p>Tivemos Applets, o próprio Flash, agora temos tentativas como Apollo, Silverlight. David tem um argumento muito bom: nenhuma dessas ditas ‘alternativas’ apresenta nada de novo. Qual a diferença de um botão em Flash ou um botão em HTML? Absolutamente nenhum, ambos têm os mesmos eventos, fazem as mesmas coisas. A diferença é que em HTML é muito mais simples, padronizado. No fim do dia, um Silverlight talvez tenha efeitos-especiais, enfeites, mas não passa disso: firulas.</p>
<p>No fim do dia, o usuário ainda estará utilizando exatamente os mesmos elementos: vai arrastar o mouse sobre um botão e clicará nele. Vai escolher uma caixa de entrada e digitar seus dados. Vai clicar numa lista e escolher um ítem. Claro, existem os casos extremos, como algum Widget de Mac (ou Gadget, ou Vista), onde você queira ver a planta de sua casa, arrastar os móveis de lugar para visualizar como vai ficar.</p>
<p>Porém, o problema é esse: esse tipo de interface é o caso extremo, a exceção à regra. Somente um caso e centenas. A grande maioria dos casos de aplicação Web é o bom e velho formulário. E foi exatamente para isso que surgiu o HTML e o CSS. Flash pode ser bom para Widgets, não para aplicações Web.</p>
<p>Talvez amanhã, quando tivermos telas multi-touch ou coisas assim, alguém consiga inventar uma maneira completamente nova e muito melhor do que os paradigmas atuais. E o ponto de David é: se isso acontecer, faz sentido ir atrás. Mas enquanto essas coisas não passarem de promessas, não faz sentido segurar a respiração até acontecer. Enquanto isso, HTML e CSS são bons o suficiente para tudo que precisamos hoje.</p>
<h2>“Tela Branca”<span class="hx:absolute hx:-mt-20" id="tela-branca"></span>
    <a href="#tela-branca" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><img src="https://akitaonrails.s3.amazonaws.com/files/80160232_867ff0523b.jpg" alt=""  loading="lazy" /></p>
<p>Outro excelente argumento de David é contra a “Tela Branca”, no sentido de um quadro em branco que você precisa pintar. A tese é que a maioria das pessoas não se dá bem com telas em branco. O problema é que você tem liberdade de fazer qualquer coisa. Nesse sentido, cada novo projeto seria mais uma tela em branco. Antes de Rails tínhamos que refazer as mesmas coisas o tempo todo. Reinventar a roda. Alguns gostam disso, mas não quer dizer que é a maneira correta.</p>
<p>Novamente, esse é o problema com os ditos ‘candidatos’ a substitutos do HTML. O erro deles é achar que a Web precisa ser reiniciada, começar do zero, começar com outra tecnologia em cima de uma tela branca. Onde está a inovação? Porque começar do zero para fazer os mesmos formulários novamente? Apenas para ter uma animação extra desnecessária? Qual o ponto, senão os casos extremos? Onde está o benefício senão aumentar a confusão?</p>
<p>Como uma nota extra: não precisamos de mais ditos ‘substitutos’. David colocou um ponto interessante: as pessoas buscando a <em>Próxima Grande Coisa</em> são <em>Pesquisadores</em>, mas não podemos confundir pesquisa com desenvolvimento do dia-a-dia. Martin completou bem: desses <em>pesquisadores</em> talvez 5% consigam surgir com algo realmente revolucionário, que talvez valha a pena investir. Obviamente, não temos como saber quem são os 95% que são puro lixo, portanto só nos resta investir nos 100% para descobrir. Mas se ele precisasse chutar iria atrás das pessoas que estão fazendo pesquisas mais aprofundadas no assunto da real interação do usuário com o aplicativo. Não somente artesões de novos ‘botões animados’, mas sim aqueles que estão buscando melhorar o contexto de interface de usuário como um todo.</p>
<p>David dá outros bons argumentos. Veja o AJAX, só agora as pessoas descobriram <em>“Puxa, dá para fazer coisas incríveis com JavaScript manipulando dados em tempo real no browser.”</em> Em bom português, todos se sentiram ‘pintos no lixo’, se esbaldaram e já exageraram. Mesmo com coisas simples como Ajax as pessoas ainda tem muito o que aprender. Não precisamos de nada a mais do que isso neste momento.</p>
<p>Quando você dá telas em branco aos desenvolvedores, eles ficam obcecados. Perdem seu tempo criando enfeites e perdem o foco no verdadeiro objetivo: a interação do usuário com a aplicação. Nós ainda estamos longe de esgotar as possibilidades da tríade HTML, CSS e JavaScript. <em>“Precisamos aprender a andar antes de começar a correr”</em> e nesse assunto ainda estamos engatinhando. Se começarmos a correr desde agora ainda acabamos indo na direção errada. Portanto, não, não precisamos de mais telas em branco ainda.</p>
<p>Como nota à parte de mim mesmo, eu diria que o futuro próximo nas interfaces de usuário não será via um novo plugin vetorial ou applet Java. O correto é puro e simplesmente o <a href="http://www.whatwg.org/specs/web-apps/current-work/"target="_blank" rel="noopener">HTML 5</a>, uma evolução natural do HTML 4 e do XHTML 1.0 que já usamos hoje mas adaptado a aceitar mais eventos, mais propriedades nos elementos que já existem. Muitas das coisas do HTML 5 nós já fazemos hoje, mas para funcionar precisamos embutir quantidades obcenas de JavaScript. A idéia do consórcio formado pela Apple, Mozilla e Opera pode não ser perfeita, mas é uma boa evolução sobre um HTML que não é atualizado significativamente há uma década. Felizmente, parece que os burocratas da W3C finalmente resolveram fazer as peças moverem.</p>
<h2>Web Design<span class="hx:absolute hx:-mt-20" id="web-design"></span>
    <a href="#web-design" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><img src="https://akitaonrails.s3.amazonaws.com/files/224116549_c1f14e8480.jpg" alt=""  loading="lazy" /></p>
<p>Isso me leva a outro assunto interessante e que a maioria está cansado de saber mas continua fazendo errado. O bom e velho <em>“faça o que eu digo, mas não faça o que eu faço.”</em> Nos processos de desenvolvimento padrão de aplicações Web, primeiro a ‘equipe de design’ faz os ‘protótipos’ da aplicação: fazem uma imagem em Photoshop de cada tipo de página. Dezenas de imagens. Depois disso pronto, passa para alguém recortar e montar em tabelinhas dentro do Dreamweaver.</p>
<p><strong>Errado!</strong> Esta é a maneira mais equivocada de se considerar em um processo. Como David diz: é péssimo usar Photoshop para prototipar a Web pois são mídias completamente diferentes. Fazer cantos arredondados no Photoshop é algo trivial, em HTML e CSS é horrível. É como pedir para um escultor prototipar em argila e depois a coisa real tem que ser feita em metal. Não dá: o designer precisa estar o mais humanamente próximo do metal, moldar diretamente no material final.</p>
<p>A outra maneira errada: fazer o desenvolvedor começar a codificar antes de saber como ela deve ficar. O problema é que o desenvolvedor fará o mais simples, não o melhor. Pois não há referencial a ele. O pensamento mais comum é <em>“farei de qualquer jeito, depois o designer fará ficar bonito”</em>. Isso não funciona. <em>“Mas o designer deixou rosa, com cantos arredondados e tudo bonitinho, não entendo porque o usuário não consegue usar …”</em></p>
<p>Na 37signals os web designers começam fazendo algumas poucas páginas diretamente em HTML. Essa é outra vantagem de HTML e CSS: eles possibilitam colocar ferramentas simples diretamente nas mãos dos designers. No fundo, um web designer não é somente alguém para colocar cores bonitas, mas sim para colocar cores úteis. Um bom Web Designer na realidade é um Designer de Interação.</p>
<p>Uma vez que as primeiras páginas estão prontas, logo elas são convertidas para Rails e a partir daí o designer é obrigado a evoluir as páginas diretamente no HTML, no meio de código Ruby. Isso ainda traz outra vantagem: ninguém precisa conjecturar como a aplicação seria: ela já está funcionando. Agora qualquer mudança é sobre a coisa real. E mais: nada dos horrendos Lorem Ipsum como textos para preencher os vazios da página. Agora o designer pode entrar com dados reais.</p>
<h2>Integração Contínua: Especificações são Inúteis<span class="hx:absolute hx:-mt-20" id="integração-contínua-especificações-são-inúteis"></span>
    <a href="#integra%c3%a7%c3%a3o-cont%c3%adnua-especifica%c3%a7%c3%b5es-s%c3%a3o-in%c3%bateis" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>E como evitar que os designers quebrem o código? Primeiro, temos que parar de considerar que todo designer é tão burro assim. Existem designers burros, mas para começar estes nem deveriam estar trabalhando. Os outros se dão bem trabalhando dessa maneira, sendo ao mesmo tempo designer e usuário. E para garantir a estabilidade do trabalho: testes. Os desenvolvedores tem que criar a suíte de testes desde o começo para evitar que qualquer um quebre as coisas. Quanto mais a aplicação estiver coberta por testes, mais difícil para um designer quebrar o código.</p>
<p>Como Martin bem lembrou, esse processo de designers e desenvolvedores trabalhando juntos levam a iterações muito curtas, coisa de poucas horas ou poucos dias. As decisões são tomadas e implementadas, num processo de integração contínua. Nesse sentido, não existe a burocracia de voltar atrás, buscar alguma especificação funcional e começar mudando de lá: a aplicação em desenvolvimento é a especificação, principalmente porque, como disse David, não existe nada de funcional em uma especificação. A única coisa que se pode chamar de ‘funcional’ é uma aplicação rodando. Não há necessidade de especular <em>“como será que vai ficar”</em> ela literalmente já é, a questão agora é <em>“como melhorar?”</em></p>
<p><img src="https://akitaonrails.s3.amazonaws.com/files/443657782_c8632e2081.jpg" alt=""  loading="lazy" /></p>
<p>São os primeiros passos para um desenvolvimento ágil. Em vez de se preocupar com a burocracia, com especificações, com protótipos, com Photoshops, toma-se uma direção ‘designer-primeiro’. Mas não no sentido que os designer vão se trancar durante três meses e sair do quartinho com dúzias de layouts para passar para os desenvolvedores. As iterações são muito mais curtas, o protótipo passa para uma tela real muito rápido. E, como disse antes, é preciso uma boa dose de disciplina e testes para manter tudo rodando e evoluindo ao mesmo tempo. Parte disso, como descreveu Martin, é que o Rails meio que nos força a criar uma aplicação modularizada, que incentiva a criação de testes.</p>
<h2>Negando novamente a Tela Branca<span class="hx:absolute hx:-mt-20" id="negando-novamente-a-tela-branca"></span>
    <a href="#negando-novamente-a-tela-branca" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Quando começamos um novo projeto, digamos, em Visual C#, a ferramenta nos dá basicamente uma pasta listando as dependências de DLLs e outra pasta simples para códigos-fonte e é só. Uma das coisas que mais agradou Martin no Rails foi o fato dele pré-criar diversas pastas, cada uma com sua função. Rails tenta forçá-lo a fazer a coisa certa dificultando a coisa errada. Para que você faça feio em Rails, é necessário explicitamente querer fazer feio, apagar as pastas que já foram criadas, mover as coisas para lugares errados e assim por diante. Dá tanto trabalho fazer do jeito errado, que ninguém realmente faz isso.</p>
<p>Em .NET existe algo similar em projetos paralelos como o <em>Tree Surgeon</em>, mas a diferença é que na comunidade Rails existe algo no sub-consciente de todos que levam a essa uniformidade. Quando o gerador de Models gera um teste unitário, a idéia é que o desenvolvedor pense <em>“puxa, o gerador criou um arquivo de teste, isso significa que eu deveria estar testando”</em>. Existe motivação para fazer as coisas do jeito certo.</p>
<p>E isso só acontece porque Rails não é uma tela branca. Ela coloca uma série de limitações, ela não o deixa fazer absolutamente tudo que quiser. Pode parecer ilógico, mas a limitação traz mais liberdade de acerto. Isso porque começar numa tela em branco a cada projeto o força a tomar as mesmas decisões várias e várias vezes.</p>
<p>HTML, CSS, Rails todas são tecnologias cujo objetivo é simplificar nossas vidas tomando as decisões repetitivas por nós, limitando nossas possibilidades e com isso aumentando nossa produtividade sem ser difícil de aprender e, principalmente, sem limitar nossa criatividade – como fazem outras tecnologias. Esse é o balanço perfeito.</p>
<h2>Comunidade e Senso de Estética<span class="hx:absolute hx:-mt-20" id="comunidade-e-senso-de-estética"></span>
    <a href="#comunidade-e-senso-de-est%c3%a9tica" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Essa idéia do balanço, das convenções nos leva a outro assunto recorrente: ao senso de estética que parece que todos da comunidade Rails parecem compartilhar. Não basta cuspir código, é preciso saber codificar da mesma forma como um enólogo sabe apreciar um bom vinho.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/files/383052026_da42ce322c.jpg" alt=""  loading="lazy" /></p>
<p>David diz que uma das habilidades que todo bom programador deve ter é saber dar bons nomes. Veja o recurso de pluralização do Rails. Nós temos essa convenção onde o nome dos Models deve ser no singular e a dos Controllers no plural. Mas não somente acrescentando um “s” no final da palavra. Existe todo um sistema para cobrir praticamente todos os casos da língua inglesa. Houve até um caso famoso na época para ter o plural ‘Octopi’ da palavra singular ‘Octopus’.</p>
<p>É algo que parece fútil, sem sentido, até impensável. Ainda hoje muitos questionam se não teria sido muito mais prático deixar tudo no singular mesmo e pronto. Mas a equipe Rails bateu o martelo: gostamos deste estilo de estética. Eles gastaram um bom tempo para deixar claro que estética importa.</p>
<p>Felizmente para a comunidade Ruby, os primeiros desenvolvedores que adotaram Ruby, como David Thomas, Hal Fulton, eram programadores que também se importavam com o código. E a comunidade se organizou em torno desse mesmo estilo, desse mesmo senso de estética. E o episódio da pluralização do Rails, acabou servindo como um <em>filtro</em> para entrar na comunidade: em vez do programador gastar meses aprendendo Rails para só depois se tocar que na verdade não gosta disso, a pluralização está bem na cara, é uma das primeiras coisas que se vê. Então esse principiante pode escolher imediatamente continuar ou pular fora. Se ele tiver o mesmo estilo, o mesmo nível cultural do resto da comunidade Ruby, vai entender o recurso e vai gostar de Rails.</p>
<p>A idéia não é excluir as pessoas porque gostamos, mas porque podemos: você não vai andar nesta casa se não respeitar esses valores, ou pelo menos se não aprender a apreciá-los.</p>
<h2>Alpha Geeks<span class="hx:absolute hx:-mt-20" id="alpha-geeks"></span>
    <a href="#alpha-geeks" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Finalmente chegamos numa parte mais ‘política’ da discussão. Porque eles começaram dizendo, <em>“o que você acha que tornou .NET popular?”</em>, bem, a Microsoft disse <em>“Esta será a próxima grande coisa.”</em> E o que tornou Java popular? Bem, a IBM e a Sun disseram a mesma coisa. E Ruby? Não há nenhuma corporação por trás dela.</p>
<p>Ruby e Rails cresceram por mérito próprio. Nenhuma grande empresa precisou investir em seu marketing. 37signals? Uma empresa de uma dúzia de pessoas. ThoughtWorks? 800 pessoas. Nada tão extraordinário quanto uma Microsoft com dezenas de milhares de pessoas espalhadas pelo globo.</p>
<p>E falando na ThoughtWorks, o que Martin nos revela é interessante. Ele mesmo diz que sua empresa não é nenhuma mega-corporação. Mesmo assim, quando .NET saiu, eles investiram pesado nele, fizeram diversos projetos. O que eles têm acompanhado é que a demanda pro projetos Java estagnou: não sobe nem desce. Porém as de .NET estão caindo mas Ruby já representa 40% dos projetos da empresa. Ele é o primeiro a dizer que ainda não sabe se isso é só algo momentâneo ou se estamos diante de uma tendência sólida. Mas é algo a se considerar.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/files/49346772_0ee70562a6.jpg" alt=""  loading="lazy" /></p>
<p>Antes de falar na Microsoft, vamos falar dos Alpha Geeks. Claro, Ruby ainda é apenas um pequeno ponto comparado à larga escala de milhões de desenvolvedores Java e .NET o importante não é apenas o número absoluto de pessoas. O problema é que os chamados ‘Alpha Geeks’ são muito menos, e eles estão migrando de Java e .NET para Ruby e outras linguagens dinâmicas. Quem são esses Alpha Geeks? São os formadores e opinião, são os líderes, os cabeças das comunidades, aqueles que influenciarão as próximas grandes coisas.</p>
<p>O fato dos projetos de .NET estarem em declínio, no exemplo de ThoughtWorks, é uma indicação. É a mesma coisa com os movimentos Ágeis. Se um bom desenvolvedor se vê preso a uma empresa que o obriga a usar processos antiquados waterfall, só lhe resta abandonar essa empresa e ir atrás de alguma outra menor que use XP, por exemplo. Muitas pessoas chegam no David para dizer, <em>“eu larguei meu trabalho por causa de Java. Eu simplesmente não gosto de trabalhar num ambiente onde só usam essas ferramentas. Então preferi pegar me tornar um freelance para trabalhar com Ruby on Rails. Estou muito mais feliz agora.”</em></p>
<p>Mas eles deixam claro uma coisa: isso não quer dizer que Java seja ruim. Muito pelo contrário, eles não desgostam de JRuby. Eles consideram que foi um movimento bastante acertado da Sun ter contratado o pessoal do JRuby e ter investido nesse projeto a ponto de surgir um produto tão bom que já consegue rodar aplicações complexas escritas em Rails. Resta saber se a Microsoft vai pelo mesmo caminho com o .NET.</p>
<h2>Hidra<span class="hx:absolute hx:-mt-20" id="hidra"></span>
    <a href="#hidra" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>E voltamos ao assunto Microsoft. Eles gastam algum tempo conjecturando. O maior problema para a Microsoft é que ela fez tantas coisas ruins que perdeu a boa reputação. Nenhum deles considera que dentro da empresa Microsoft exista indivíduos que sejam verdadeiramente maus. Mas a política da corporação como um todo não está ajudando. Eles filosofam no sentido de que as pessoas podem ser boas pessoas para se conversar, tomar uma cerveja, mas no final são suas atitudes que contam quando alguém publica uma notícia dizendo que o Linux está infringindo não sei quantas patentes.</p>
<p>E o problema para ela é que uma ação má apaga todas as boas ações anteriores. Perdendo a reputação, mesmo tendo coisas realmente interessantes como LINQ, as novas ferramentas de Workflow e tudo mais, mesmo assim a comunidade vai começar a diminuir, os Alpha Geeks vão migrar para outras tecnologias. Talvez seja o que Paul Graham quis dizer quando afirmou que a <a href="http://www.paulgraham.com/microsoft.html"target="_blank" rel="noopener">Microsoft estava morta</a>, no sentido de que ela não é mais perigosa porque perdeu a relevância. Quem sabe?</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/files/521201549_db11ff1e79.jpg" alt=""  loading="lazy" /></p>
<p>O problema da Microsoft em especial, é que ela é muito grande. Quem faz todas essas más ações? Todos concordam que não se pode colocar a culpa de tudo no Bill. Não é ele diretamente quem faz cada uma dessas coisas. O problema é que eles estão sem direção, estão sem líderes. E nesse sentido eles se parecem com uma Hidra. E é por isso que muita gente não confia neles: enquanto uma pessoa lá pode estar sorrindo, outra na mesma empresa o está apunhalando pelas costas.</p>
<p>Como eu disse antes, Scott, o entrevistador, está na folha de pagamento da Microsoft, por isso é uma situação estranha. Ele não pode ser hiperbólico sobre a Microsoft nem pode bater nela. De qualquer forma tentou conciliar: será que não adiantaria para a Microsoft fazer a mesma coisa que pessoas como David e Martin? Sair pelo mundo, em conferências, dizendo as coisas boas que tem. Mas, David, afiado como sempre, deu a última rasteira: <em>“não basta dizer ao mundo que você é bom, você precisa fazer coisas boas para ser considerado bom.”</em></p>
<p>Nós julgamos as pessoas pelos seus atos, não pelo que elas falam. Em um determinado momento, o estilo selvagem e monopolista funcionou muito bem. Eles fizeram bilhões de dólares com essa estratégia. Mas agora não funciona mais. Esse tipo de ‘tradição’ não se muda do dia pra noite: tem pessoas que trabalham lá há mais de 15 anos. Portanto é muito difícil voltar aos trilhos, e eles precisam fazer algo rápido.</p>
]]></content:encoded><category>entrevistas</category><category>desenvolvimento-web</category><category>frontend</category></item></channel></rss>