<?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>Forem: Sotirios Dimitrou</title>
    <description>The latest articles on Forem by Sotirios Dimitrou (@saoma1).</description>
    <link>https://forem.com/saoma1</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%2F267075%2F2011f1cc-d536-4e81-a2ec-935feccb6746.jpg</url>
      <title>Forem: Sotirios Dimitrou</title>
      <link>https://forem.com/saoma1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/saoma1"/>
    <language>en</language>
    <item>
      <title>How to migrate AASM to ENUM while keeping users in mind</title>
      <dc:creator>Sotirios Dimitrou</dc:creator>
      <pubDate>Wed, 31 May 2023 09:41:09 +0000</pubDate>
      <link>https://forem.com/saoma1/how-to-migrate-aasm-to-enum-while-keeping-users-in-mind-2390</link>
      <guid>https://forem.com/saoma1/how-to-migrate-aasm-to-enum-while-keeping-users-in-mind-2390</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, we will address a common challenge when migrating from the AASM state machine to the Rails built-in enum. The main problem we aim to solve is ensuring a seamless transition without causing disruptions or data inconsistencies for users. To tackle this issue, we will follow a systematic approach and split the migration process into two pull requests. By carefully implementing these changes, we can ensure a smooth transition while maintaining data integrity. Let's explore the solution in detail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Migrate to Enum?&lt;/strong&gt;&lt;br&gt;
As part of our teams commitment to align with Rails conventions, we have decided to migrate models that previously utilized AASM to enum. Migrating from AASM to the Rails enum feature brings benefits such as simplified code maintenance and improved performance through optimized database queries. By leveraging the native enum functionality, developers can enhance code readability, adhere to Rails conventions, and tap into the extensive community support available for working with enums in Rails.&lt;/p&gt;

&lt;p&gt;This is how a aasm state machine might look like for a &lt;code&gt;post&lt;/code&gt; model:&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="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;AASM&lt;/span&gt;

&lt;span class="n"&gt;aasm&lt;/span&gt; &lt;span class="ss"&gt;column: :aasm_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;whiny_transitions: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="ss"&gt;:draft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;enter: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:actions_on_publish&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="ss"&gt;:archived&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;enter: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:actions_on_archive&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="ss"&gt;:publish&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;transitions&lt;/span&gt; &lt;span class="ss"&gt;from: :draft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: :published&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="ss"&gt;:archive&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;transitions&lt;/span&gt; &lt;span class="ss"&gt;from: :published&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: :archived&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="ss"&gt;:unarchive&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;transitions&lt;/span&gt; &lt;span class="ss"&gt;from: :archived&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: :published&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;def&lt;/span&gt; &lt;span class="nf"&gt;actions_on_publish&lt;/span&gt;
  &lt;span class="c1"&gt;### actions to perform when publishing a post&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;actions_on_archive&lt;/span&gt;
  &lt;span class="c1"&gt;### actions to perform when archiving a post&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Challenge&lt;/strong&gt;&lt;br&gt;
The main challenge arises when we deploy the code without considering the existing data and user experience. If we were to directly switch to the new enum state column, users would encounter unexpected behavior. For instance, until the migration is completed, users would not see their old posts on the post index page. This occurs because the index page would rely on the new state column, which initially lacks the migrated data. The same issue applies to recruiters accessing the post data.&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
&lt;strong&gt;Proposed Solution&lt;/strong&gt;&lt;br&gt;
To tackle this problem, we can divide the migration process into two pull requests (PRs) and leverage a rake task to facilitate the data migration. By following this approach, we can ensure a seamless transition without causing inconvenience to users.&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
&lt;strong&gt;Step 1: PR #1&lt;/strong&gt;&lt;br&gt;
In this initial PR, we will implement the following changes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a migration: AddStateToPosts.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddStateToPosts&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;7.0&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;change&lt;/span&gt;
      &lt;span class="n"&gt;add_column&lt;/span&gt; &lt;span class="ss"&gt;:posts&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="ss"&gt;:string&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;p&gt; &lt;/p&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Implement a sync_state method in the Post model to keep the enum state synchronized with the existing aasm_state until we remove the aasm_state in PR#2. Also add the method as a before_validation.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;

  &lt;span class="n"&gt;before_validation&lt;/span&gt; &lt;span class="ss"&gt;:sync_state&lt;/span&gt;

  &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="no"&gt;AASM&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sync_state&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aasm_state&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;p&gt; &lt;/p&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Develop a rake task (single_run.rake) to migrate existing aasm_state values to the new state column.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"2023-05-28: Migrate post aasm state to enum state"&lt;/span&gt;
&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;migrate_post_aasm_state_to_enum_state: :environment&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;all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;distinct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:aasm_state&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;state&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&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;aasm_state: &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;state: &lt;/span&gt;&lt;span class="kp"&gt;nil&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;"Starting to migrate &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; posts"&lt;/span&gt;
      &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;in_batches&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;batch&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;batch&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="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="k"&gt;end&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"🏁 All done! 🏁"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;At this point you can also add a test to validate that the enum state is synced properly every time a Post's aasm_state is updated.&lt;/p&gt;

&lt;p&gt;Once the PR is merged run following code:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rake "single_run:migrate_post_aasm_state_to_enum_state"&lt;/code&gt;&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: PR #2&lt;/strong&gt;&lt;br&gt;
In this subsequent PR, we can proceed with the following changes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Remove the AASM-related code from the Post model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the enum state to the Post model, mapping the AASM states.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;state: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;draft: &lt;/span&gt;&lt;span class="s2"&gt;"draft"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;published: &lt;/span&gt;&lt;span class="s2"&gt;"published"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;archived: &lt;/span&gt;&lt;span class="s2"&gt;"archived"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;_default: &lt;/span&gt;&lt;span class="s2"&gt;"draft"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt; &lt;/p&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Rewrite the actions triggered during state transitions to utilize the enum-based approach.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;publish&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;published?&lt;/span&gt;
  &lt;span class="n"&gt;published!&lt;/span&gt;
  &lt;span class="c1"&gt;### perform additional actions when publishing a post&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;archive&lt;/span&gt;
  &lt;span class="n"&gt;archived!&lt;/span&gt;
  &lt;span class="c1"&gt;### perform additional actions when archiving a post&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;unarchive&lt;/span&gt;
  &lt;span class="n"&gt;published!&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;archived?&lt;/span&gt;
  &lt;span class="c1"&gt;### perform additional actions when unarchiving a post&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt; &lt;/p&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;Last but not least, check in your code where the aasm_state was being called on an instance and swap it out with the new enum state.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;code&gt;posts.where.not(aasm_state: "draft")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;will now become:&lt;br&gt;
&lt;code&gt;posts.where.not(state: "draft")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Also look out where the old actions where being called with a bang (!) if you want the new instance methods to work anymore.&lt;/p&gt;

&lt;p&gt;In a controller for the posts you may had something like:&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;def&lt;/span&gt; &lt;span class="nf"&gt;archive&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;archive!&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which will now become:&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;def&lt;/span&gt; &lt;span class="nf"&gt;archive&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;archive&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Of course you could also do &lt;code&gt;resource.archived!&lt;/code&gt; but that would only change the state and not call the instance method &lt;code&gt;archive&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And that's it, we are done 🎉&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt;&lt;br&gt;
By following this two-step approach and employing a rake task for data migration, we can smoothly transition from the AASM state machine to Rails' built-in enum without causing problems or downtime for users. &lt;/p&gt;

&lt;p&gt;It is crucial to consider existing data and maintain a consistent user experience during such migrations. By adhering to Rails conventions and implementing the proposed solution, we ensure code consistency and improve the maintainability of our application. &lt;/p&gt;

&lt;p&gt;Thank you for reading the whole article, it means a lot to me. This is the first of hopefully more to come.&lt;/p&gt;

</description>
      <category>gratitude</category>
    </item>
  </channel>
</rss>
