<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lucas Araújo</title>
    <description>The latest articles on DEV Community by Lucas Araújo (@jamalcrf).</description>
    <link>https://dev.to/jamalcrf</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F681531%2F5256ea5a-7625-478b-b0c2-0693849cfb27.jpeg</url>
      <title>DEV Community: Lucas Araújo</title>
      <link>https://dev.to/jamalcrf</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jamalcrf"/>
    <language>en</language>
    <item>
      <title>Entendendo e Resolvendo o Problema N+1 com Ruby on Rails</title>
      <dc:creator>Lucas Araújo</dc:creator>
      <pubDate>Wed, 24 May 2023 03:05:01 +0000</pubDate>
      <link>https://dev.to/jamalcrf/entendendo-e-resolvendo-o-problema-n1-com-ruby-on-rails-2poa</link>
      <guid>https://dev.to/jamalcrf/entendendo-e-resolvendo-o-problema-n1-com-ruby-on-rails-2poa</guid>
      <description>&lt;p&gt;Na jornada para se tornar um desenvolvedor Rails, é inevitável encontrar certos desafios que fazem você parar, pensar e até mesmo refatorar o código que você pensava estar pronto para produção. Um desses desafios é o fatídico problema &lt;strong&gt;N+1&lt;/strong&gt;, uma armadilha comum de desempenho que tem o potencial de desacelerar a sua aplicação Rails.&lt;/p&gt;

&lt;p&gt;Entender esse problema é essencial para qualquer desenvolvedor Rails, pois ele está ligado à forma como o &lt;a href="https://guides.rubyonrails.org/active_record_basics.html"&gt;ActiveRecord&lt;/a&gt;, o ORM padrão utilizado pelo Rails, manipula as associações entre os modelos. Quando mal gerenciado, este problema pode resultar em uma quantidade excessiva de consultas ao banco de dados, prejudicando a performance do seu aplicativo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entendendo N+1
&lt;/h2&gt;

&lt;p&gt;O &lt;strong&gt;N+1&lt;/strong&gt; é um padrão comum de ineficiência de desempenho que ocorre ao trabalhar com bancos de dados relacionais, como PostgreSQL ou MySQL. Essa questão ocorre quando estamos recuperando objetos associados em uma relação de um para muitos (one-to-many) ou muitos para muitos (many-to-many). Por exemplo, suponha que você tenha modelos de &lt;code&gt;User&lt;/code&gt; e &lt;code&gt;Post&lt;/code&gt;, e cada &lt;code&gt;User&lt;/code&gt; tem muitos &lt;code&gt;Posts&lt;/code&gt;. Se você quisesse carregar todos os usuários e seus respectivos posts, você pode acabar criando um cenário de problema &lt;strong&gt;N+1&lt;/strong&gt; sem perceber.&lt;/p&gt;

&lt;p&gt;Imaginando que  que você queira mostrar todos os posts de cada usuário. Você pode ser tentado a fazer algo assim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User.all.each do |user|
  puts user.posts
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mas vamos destrinchar com calma o que esse código esta fazendo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Quando você executa &lt;code&gt;User.all.each&lt;/code&gt;, o Rails realiza uma consulta SQL para buscar todos os usuários. Isso representa a primeira consulta SQL, ou o "&lt;strong&gt;1&lt;/strong&gt;" no problema "&lt;strong&gt;N+1&lt;/strong&gt;".
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Load (6.9ms)  SELECT "users".* FROM "users"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;No momento em que você executa &lt;code&gt;user.posts.each&lt;/code&gt; dentro do loop, o Rails tem que buscar os posts para o usuário atual. Isso representa uma consulta SQL adicional para cada usuário. Esta consulta é executada para cada um dos seus usuários e são as consultas "&lt;strong&gt;N&lt;/strong&gt;" no problema "&lt;strong&gt;N+1&lt;/strong&gt;".
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Post Load (0.2ms)  SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = ?  [["user_id", 1]]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Então, supondo que na sua base de dados possuam 1000 usuários no total, seu aplicativo faz 1001 consultas ao banco de dados para este único pedaço de código. Para um número grande de usuários, isso se torna altamente ineficiente, pois cada consulta ao banco de dados consome tempo e recursos do servidor.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Resolvendo esse problema
&lt;/h2&gt;

&lt;p&gt;O problema &lt;strong&gt;N+1&lt;/strong&gt; pode ser enfrentado de várias maneiras. Algumas delas incluem a utilização de gems como a '&lt;a href="https://github.com/flyerhzm/bullet"&gt;bullet&lt;/a&gt;' ou a '&lt;a href="https://github.com/salsify/goldiloader"&gt;goldiloader&lt;/a&gt;', que são projetadas para ajudar a identificar e corrigir isso. Além do mais, técnicas de otimização de consultas, como 'preload', 'eager_load' e 'joins', também podem ser usadas dependendo do caso específico. No entanto, uma das soluções mais comuns e simples é o uso do 'eager loading'. '&lt;a href="https://www.tutorialspoint.com/entity_framework/entity_framework_eager_loading.htm#:~:text=Eager%20loading%20is%20the%20process,query%20results%20from%20the%20database."&gt;Eager loading&lt;/a&gt;' é uma técnica onde nós carregamos todas as associações necessárias de uma só vez de maneira antecipada. Isso é especialmente útil para evitar consultas excessivas ao banco de dados. No Rails, isso é facilitado pelo método &lt;code&gt;includes&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;users = User.includes(:posts)
users.each do |user|
  puts user.posts
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nesse bloco de código, a chamada &lt;code&gt;User.includes(:posts)&lt;/code&gt; instrui o &lt;a&gt;ActiveRecord&lt;/a&gt; a carregar todos os usuários e seus respectivos posts em duas consultas separadas. Primeiro, ele executa uma consulta para buscar todos os usuários. Em seguida, com base nos &lt;code&gt;IDs&lt;/code&gt; dos usuários retornados, executa uma segunda consulta para buscar todos os posts associados a esses usuários.&lt;/p&gt;

&lt;p&gt;Essas consultas são armazenadas na memória, de modo que quando você itera sobre os usuários e seus posts na sequência &lt;code&gt;user.posts&lt;/code&gt;, nenhuma consulta adicional é necessária, pois os dados já estão disponíveis.&lt;/p&gt;

&lt;p&gt;Isso reduz significativamente o total de consultas, de 1001 para apenas 2, neste caso, independentemente do número de usuários. Portanto, ao utilizar o '&lt;code&gt;includes&lt;/code&gt;' para carregar antecipadamente as associações necessárias, resolvemos eficientemente o problema &lt;strong&gt;N+1&lt;/strong&gt; e otimizamos a performance da nossa aplicação."&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>backend</category>
      <category>activerecord</category>
    </item>
    <item>
      <title>Migrações de dados em Ruby on Rails</title>
      <dc:creator>Lucas Araújo</dc:creator>
      <pubDate>Thu, 10 Mar 2022 13:36:54 +0000</pubDate>
      <link>https://dev.to/jamalcrf/migracoes-de-dados-em-ruby-on-rails-59if</link>
      <guid>https://dev.to/jamalcrf/migracoes-de-dados-em-ruby-on-rails-59if</guid>
      <description>&lt;p&gt;Desde o início dessa minha caminhada como desenvolvedor Ruby on Rails, eu venho aprendendo determinadas técnicas e ferramentas que me auxiliam no meu dia a dia. Uma vez no meu trabalho, ocorreu a necessidade de alterar os dados reais no banco de dados de produção. Eu pensei rapidamente e a primeira opção óbvia que vem à mente é usar uma migração do Rails, porém conversando com colegas da minha equipe, fui convencido de que isso não seria uma boa prática e a partir daí busquei entender um pouco mais sobre como migrar dados de maneira correta e mais segura utilizando &lt;code&gt;Ruby on Rails&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Utilizando o sistema de migrações do Rails
&lt;/h2&gt;

&lt;p&gt;Observando o &lt;em&gt;&lt;a href="https://guides.rubyonrails.org/active_record_migrations.html"&gt;Rails Guide, for Active Record Migration&lt;/a&gt;&lt;/em&gt; a primeira seção da documentação começa dizendo:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"As migrações são um recurso do Active Record que permite evoluir seu esquema de banco de dados ao longo do tempo. Em vez de escrever modificações de esquema em SQL puro, as migrações permitem que você use uma DSL Ruby para descrever as alterações em suas tabelas."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claramente, a palavra "dados" em nenhum momento foi citada no parágrafo acima, isso porque, por definição, as migrações do Rails devem ser usadas apenas para alterações do &lt;code&gt;schema&lt;/code&gt; e não para alterações de dados reais no banco de dados.&lt;/p&gt;

&lt;p&gt;Podemos criar um cenário básico para entendermos um pouco mais sobre isso. Digamos que nós precisamos alterar o estado padrão de um blog qualquer, por exemplo, sendo assim criaríamos um arquivo de migração como este abaixo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;rails&lt;/span&gt; &lt;span class="n"&gt;generate&lt;/span&gt; &lt;span class="n"&gt;migration&lt;/span&gt; &lt;span class="no"&gt;ChangeDefaultState&lt;/span&gt;
  &lt;span class="n"&gt;invoke&lt;/span&gt;  &lt;span class="n"&gt;active_record&lt;/span&gt;
  &lt;span class="n"&gt;create&lt;/span&gt;    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;migrate&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;20220305055147&lt;/span&gt;&lt;span class="n"&gt;_change_default_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nesse arquivo, faríamos a seguinte alteração como essa:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChangeDefaultState&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;5.1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;up&lt;/span&gt;
    &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;state: &lt;/span&gt;&lt;span class="s2"&gt;"Initial"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;state: &lt;/span&gt;&lt;span class="s2"&gt;"Active"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;down&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IrreversibleMigration&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feito isso, está tudo certo! É só executar a migração e esquecer dela... Meses depois aparece uma demanda no seu sistema, onde nós precisamos alterar o nome da tabela &lt;code&gt;posts&lt;/code&gt; para &lt;code&gt;articles&lt;/code&gt;. Até então é uma tarefa simples de ser executada. Alterando o nome da tabela, renomeando o modelo e pronto, só mandar para produção. Pois bem, agora nós não temos mais um modelo chamado &lt;code&gt;Post&lt;/code&gt;. Na próxima vez que você precisar configurar um ambiente de desenvolvimento, você cria seu banco de dados, executa suas migrações e obtém uma falha no seu console: Rails não sabe o que é &lt;code&gt;Post&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;rails&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="ss"&gt;:migrate&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;20220305056456&lt;/span&gt; &lt;span class="no"&gt;AddDefaultState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;migrating&lt;/span&gt; &lt;span class="o"&gt;==================================&lt;/span&gt;
&lt;span class="n"&gt;rails&lt;/span&gt; &lt;span class="n"&gt;aborted!&lt;/span&gt;
&lt;span class="no"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;An&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="n"&gt;has&lt;/span&gt; &lt;span class="n"&gt;occurred&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="n"&gt;later&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt; &lt;span class="ss"&gt;canceled:
&lt;/span&gt;&lt;span class="n"&gt;uninitialized&lt;/span&gt; &lt;span class="n"&gt;constant&lt;/span&gt; &lt;span class="no"&gt;AddDefaultState&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt;
&lt;span class="sr"&gt;/Users/&lt;/span&gt;&lt;span class="n"&gt;lucas&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="no"&gt;Projects&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;codes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;serious&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;migrate&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;20220305055147&lt;/span&gt;&lt;span class="n"&gt;_change_default_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="ss"&gt;:in&lt;/span&gt; &lt;span class="sb"&gt;`change'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A solução para esse tipo de problema, de fato, é rápida: você precisa renomear o modelo na sua migração. Entretanto esse padrão não é a melhor abordagem. Resumidamente,as migrações ficam desatualizadas rapidamente e você não tem uma maneira eficiente de descobrir, além do mais não estaria seguindo a convenção do Rails: os arquivos &lt;code&gt;db/migrate&lt;/code&gt; devem migrar apenas as estrutura do banco de dados e não os registros do banco de dados. &lt;/p&gt;

&lt;p&gt;Outro detalhe é que o sistema agora  depende da conclusão da migração de dados. Isso pode não ser um problema quando seu aplicativo é novo e seu banco de dados é pequeno. Mas e os grandes bancos de dados com milhões de registros? Sua implantação agora terá que esperar que a manipulação de dados seja concluída e isso é apenas pedir problemas, com possíveis migrações suspensas ou com falha. Dito isso, pode-se dizer que incluir os códigos de migração de dados no &lt;code&gt;db:/migrate&lt;/code&gt; é uma prática "anti-padrão".&lt;/p&gt;

&lt;h2&gt;
  
  
  Utilizando Rake Tasks para migrar dados
&lt;/h2&gt;

&lt;p&gt;Um método mais confiável, acessível e eficiente é criar &lt;code&gt;Rake Tasks&lt;/code&gt; para migrações de dados. Escrevendo um pouco mais de código nós podemos resolver esse tipo de problema.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="ss"&gt;:data&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:migrations&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Rake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;in_namespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="k"&gt;next&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"data:migrations"&lt;/span&gt;
        &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Invoking &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:"&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;change_default_state: :environment&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Changing default state for posts"&lt;/span&gt;
    &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;state: &lt;/span&gt;&lt;span class="s2"&gt;"Initial"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;state: &lt;/span&gt;&lt;span class="s2"&gt;"Active"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Changed default state for posts"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A primeira &lt;code&gt;Task&lt;/code&gt; ( rake data:migrations) executará todas as tarefas no data namespace excluindo ela mesma. Isso pode ser um pouco arriscado! então você deve se certificar de que todas as &lt;code&gt;Tasks&lt;/code&gt; nesse namespace sejam idempotentes, ou seja, realizadas com sucesso independente do número de vezes que é executada. Você deseja poder executar &lt;code&gt;rake data:migrations&lt;/code&gt; o quanto quiser sem arriscar a perda de dados.&lt;/p&gt;

&lt;p&gt;A ressalva que temos com essa abordagem é que ela não resolve o problema que mencionamos no primeiro padrão: à medida que o sistema evolui, as &lt;code&gt;Tasks&lt;/code&gt; ficarão desatualizadas.&lt;/p&gt;

&lt;p&gt;A melhor maneira é criar uma classe adicional. Podemos chamá-lo&lt;code&gt;ChangeDefaultStateForPosts&lt;/code&gt;. Será um objeto &lt;code&gt;Ruby&lt;/code&gt; simples que executará a migração de dados. Isso nos ajudará a adicionar cobertura de testes para ele.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChangeDefaultStateForPosts&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
    &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;state: &lt;/span&gt;&lt;span class="s2"&gt;"Initial"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;state: &lt;/span&gt;&lt;span class="s2"&gt;"Active"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora que temos uma classe simples e com isso podemos escrever um arquivo de testes para ela.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;ChangeDefaultStateForPosts&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"ChangeDefaultStateForPosts.call"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;let!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;state: &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"when post is in 'Initial' state"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"Initial"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"changes the state of the Initial"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;expect&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
          &lt;span class="no"&gt;ChangeDefaultStateForPosts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"when post is in another state"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"Published"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"doesn't change the state"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;expect&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
          &lt;span class="no"&gt;ChangeDefaultStateForPosts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;not_to&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sendo assim estamos seguros pois a nossa &lt;code&gt;Task&lt;/code&gt; sobre a migração de dados é protegidas pelo nosso arquivo de teste. Quando alguém tentar renomear a tabela &lt;code&gt;posts&lt;/code&gt; para &lt;code&gt;articles&lt;/code&gt;, receberá uma falha no teste. Isso os forçará a atualizar o arquivo de migração de dados.&lt;/p&gt;

&lt;p&gt;Caso essa opção seja válida e queira implementar no seu projeto, é bastante recomendado documentar todo esse processo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrações de dados com a &lt;code&gt;data_migrate&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Como todo desenvolvedor Rails, provavelmente, em algum momento desse artigo, você chegou a se perguntar se talvez existisse uma Gem para fazer esse processo de migração de dados... SIM, nós podemos usar a &lt;a href="https://github.com/ilyakatz/data-migrate"&gt;data_migrate&lt;/a&gt; para todas as nossas migrações de dados. Assim como o Rails com a tabela &lt;code&gt;schema_migrations&lt;/code&gt;, a &lt;a href="https://dev.to20220305055147"&gt;data_migrate&lt;/a&gt; usa uma tabela especifica chamada &lt;code&gt;data_migrations&lt;/code&gt; para acompanhar as migrações novas e as antigas.&lt;/p&gt;

&lt;p&gt;Para o problema que estamos tentando resolver, você pode criar uma nova migração de dados como esta:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rails generate data_migration change_default_state_for_posts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Isso adicionará uma nova migração de dados ao diretório &lt;code&gt;db/data&lt;/code&gt;. Você precisará definir os métodos &lt;code&gt;up&lt;/code&gt; e &lt;code&gt;down&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChangeDefaultStateForPosts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;5.1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;up&lt;/span&gt;
    &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;state: &lt;/span&gt;&lt;span class="s2"&gt;"Initial"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;state: &lt;/span&gt;&lt;span class="s2"&gt;"Active"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;down&lt;/span&gt;
    &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;state: &lt;/span&gt;&lt;span class="s2"&gt;"Active"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;state: &lt;/span&gt;&lt;span class="s2"&gt;"Initial"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E, em seguida, execute e verifique o status com comandos como estes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;rake&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="ss"&gt;:migrate&lt;/span&gt;
&lt;span class="n"&gt;rake&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="ss"&gt;:migrate:with_data&lt;/span&gt;
&lt;span class="n"&gt;rake&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="ss"&gt;:rollback:with_data&lt;/span&gt;
&lt;span class="n"&gt;rake&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="ss"&gt;:migrate:status:with_data&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Acredito que a melhor maneira de resolver esse problema é usar a Gem &lt;a href="https://github.com/ilyakatz/data-migrate"&gt;data_migrate&lt;/a&gt;. Você escreverá menos código, manterá todas as migrações de dados em um diretório  &lt;code&gt;db/data&lt;/code&gt; e terá uma boa maneira de acompanhar as alterações de dados.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>database</category>
      <category>backend</category>
    </item>
  </channel>
</rss>
