<?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: Juraj Kostolanský</title>
    <description>The latest articles on DEV Community by Juraj Kostolanský (@jkostolansky).</description>
    <link>https://dev.to/jkostolansky</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%2F65048%2F8b47a2e1-7773-4a22-a1e2-f6a8a116ffe8.jpg</url>
      <title>DEV Community: Juraj Kostolanský</title>
      <link>https://dev.to/jkostolansky</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jkostolansky"/>
    <language>en</language>
    <item>
      <title>How to wrap Rails mailer previews in a database transaction</title>
      <dc:creator>Juraj Kostolanský</dc:creator>
      <pubDate>Fri, 04 Sep 2020 08:24:24 +0000</pubDate>
      <link>https://dev.to/jkostolansky/how-to-wrap-rails-mailer-previews-in-a-database-transaction-1h69</link>
      <guid>https://dev.to/jkostolansky/how-to-wrap-rails-mailer-previews-in-a-database-transaction-1h69</guid>
      <description>&lt;p&gt;Most of the web applications today need to send some kind of email. In Ruby on Rails, this is an easy task. Just use the Action Mailer, create the email content, set up the SMTP configuration and you are ready to go.&lt;/p&gt;

&lt;p&gt;The hardest part of this process is the content creation. Especially if you are building a multi-language application, seeing the big picture just from the source code can be quite hard. Fortunately, Rails provides a way to see how these emails look right in a web browser, using the &lt;a href="https://guides.rubyonrails.org/action_mailer_basics.html#previewing-emails"&gt;Action Mailer Previews&lt;/a&gt;. Just create a class that inherits from the &lt;code&gt;ActionMailer::Preview&lt;/code&gt; and call the required mailer action.&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="c1"&gt;# test/mailers/previews/user_mailer_preview.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserMailerPreview&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionMailer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Preview&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;confirmation&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s2"&gt;"tester@example.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;UserMailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;confirmation&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;However, sometimes your mailer expects the ID of a record in the database, not the object itself. And sometimes you need to prepare this record, for example to create a new email confirmation token.&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="c1"&gt;# test/mailers/previews/user_mailer_preview.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserMailerPreview&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionMailer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Preview&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;confirmation&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare_token!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:confirmation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;UserMailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;user_id: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;confirmation&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;But this should be just a temporary change. Sure, you can wrap the whole method in a database transaction and use the database rollback command. But… How exactly?&lt;/p&gt;

&lt;p&gt;Fortunately, there is an easy way to accomplish this using some Rails monkey patching. Just create the following module:&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="c1"&gt;# lib/core_extensions/mailer_preview_rollback&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;CoreExtensions&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;MailerPreviewRollback&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;preview&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;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="k"&gt;super&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;Rollback&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;Create an initializer in your Ruby on Rails application and prepend this module:&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="c1"&gt;# config/initializers/mailer_preview_rollback.rb&lt;/span&gt;

&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;after_initialize&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"core_extensions/mailer_preview_rollback"&lt;/span&gt;
  &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MailersController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt; &lt;span class="no"&gt;CoreExtensions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MailerPreviewRollback&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And voila! All of your mailer previews are now safely wrapped in a database transaction.&lt;/p&gt;




&lt;p&gt;Original article: &lt;a href="https://www.kostolansky.sk/posts/rails-mailer-preview-database-transactions/"&gt;How to wrap Rails mailer previews in a database transaction&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to upgrade PostgreSQL from 11 to 12</title>
      <dc:creator>Juraj Kostolanský</dc:creator>
      <pubDate>Thu, 03 Oct 2019 19:40:06 +0000</pubDate>
      <link>https://dev.to/jkostolansky/how-to-upgrade-postgresql-from-11-to-12-2la6</link>
      <guid>https://dev.to/jkostolansky/how-to-upgrade-postgresql-from-11-to-12-2la6</guid>
      <description>&lt;p&gt;The new &lt;a href="https://www.postgresql.org/about/news/1976/"&gt;PostgreSQL 12&lt;/a&gt; has just been released. There are multiple ways to upgrade from the old version 11, and the easiest one is by using the &lt;a href="https://www.postgresql.org/docs/12/pgupgrade.html"&gt;pg_upgrade&lt;/a&gt; tool. Here is a quick tutorial for Ubuntu (or Debian) systems. And, please, do not forget to back up your data!&lt;/p&gt;

&lt;p&gt;Update packages and install the new PostgreSQL 12.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;postgresql-12 postgresql-server-dev-12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check if there are any differences in the config files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diff /etc/postgresql/11/main/postgresql.conf /etc/postgresql/12/main/postgresql.conf
diff /etc/postgresql/11/main/pg_hba.conf /etc/postgresql/12/main/pg_hba.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stop the PostgreSQL service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl stop postgresql.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Log in as the &lt;code&gt;postgres&lt;/code&gt; user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;su postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check clusters (notice the &lt;code&gt;--check&lt;/code&gt; argument, this will not change any data).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/usr/lib/postgresql/12/bin/pg_upgrade &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--old-datadir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/postgresql/11/main &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--new-datadir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/postgresql/12/main &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--old-bindir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/lib/postgresql/11/bin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--new-bindir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/lib/postgresql/12/bin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--old-options&lt;/span&gt; &lt;span class="s1"&gt;'-c config_file=/etc/postgresql/11/main/postgresql.conf'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--new-options&lt;/span&gt; &lt;span class="s1"&gt;'-c config_file=/etc/postgresql/12/main/postgresql.conf'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Migrate the data (without the &lt;code&gt;--check&lt;/code&gt; argument).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/usr/lib/postgresql/12/bin/pg_upgrade &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--old-datadir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/postgresql/11/main &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--new-datadir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/postgresql/12/main &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--old-bindir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/lib/postgresql/11/bin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--new-bindir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/lib/postgresql/12/bin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--old-options&lt;/span&gt; &lt;span class="s1"&gt;'-c config_file=/etc/postgresql/11/main/postgresql.conf'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--new-options&lt;/span&gt; &lt;span class="s1"&gt;'-c config_file=/etc/postgresql/12/main/postgresql.conf'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go back to the regular user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swap the ports for the old and new PostgreSQL versions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;vim /etc/postgresql/12/main/postgresql.conf
&lt;span class="c"&gt;# ...and change "port = 5433" to "port = 5432"&lt;/span&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;vim /etc/postgresql/11/main/postgresql.conf
&lt;span class="c"&gt;# ...and change "port = 5432" to "port = 5433"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the PostgreSQL service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start postgresql.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Log in as the &lt;code&gt;postgres&lt;/code&gt; user again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;su postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the new PostgreSQL version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"SELECT version();"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the generated &lt;code&gt;analyze_new_cluster&lt;/code&gt; script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./analyze_new_cluster.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Back to normal user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check which old PostgreSQL packages are installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt list &lt;span class="nt"&gt;--installed&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;postgresql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove the old PostgreSQL packages (from the listing above).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get remove postgresql-11 postgresql-server-dev-11
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove the old configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /etc/postgresql/11/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Log in as the &lt;code&gt;postgres&lt;/code&gt; user once more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;su postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, drop the old cluster data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./delete_old_cluster.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done!&lt;/p&gt;




&lt;p&gt;Original article: &lt;a href="https://www.kostolansky.sk/posts/upgrading-to-postgresql-12/"&gt;How to upgrade PostgreSQL from 11 to 12&lt;/a&gt;&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>linux</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Using PostgreSQL advisory locks to control concurrency</title>
      <dc:creator>Juraj Kostolanský</dc:creator>
      <pubDate>Thu, 04 Jul 2019 11:00:00 +0000</pubDate>
      <link>https://dev.to/jkostolansky/using-postgresql-advisory-locks-to-control-concurrency-3j0d</link>
      <guid>https://dev.to/jkostolansky/using-postgresql-advisory-locks-to-control-concurrency-3j0d</guid>
      <description>&lt;p&gt;We often need to control concurrent access to databases in Ruby on Rails applications. We need to make sure that a piece of code will run synchronously, to guarantee atomicity of multiple non-atomic operations.&lt;/p&gt;

&lt;p&gt;PostgreSQL provides various techniques to control concurrent access to data. For example, we can use the Serializable transaction isolation level. In that case, any concurrent execution of a set of Serializable transactions is guaranteed to produce the same effect as running them one at a time in some order.&lt;/p&gt;

&lt;p&gt;There are also explicit row-level locks that prevent two transactions from modifying a resource in conflicting ways. We can choose pessimistic locking if a database transaction conflict is very likely to happen. It locks the record until the transaction is done, and any other transaction trying to acquire the same lock has to wait until the lock is released.&lt;/p&gt;

&lt;p&gt;Otherwise, we can use optimistic locking. It uses a version number of the record to check whether another process has made changes to it since it was opened, and throws an exception if that has occurred and the update is ignored.&lt;/p&gt;

&lt;p&gt;Another way to control concurrency is advisory locking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advisory locking
&lt;/h2&gt;

&lt;p&gt;Advisory locks by PostgreSQL are not tied to database tables or rows. We can use them, for example, to restrict the concurrency of a specific code in our application that works with a record from a specific table, and the other parts of our application that are accessing that table wouldn't notice that.&lt;/p&gt;

&lt;p&gt;Advisory locks are application enforced locks. That means PostgreSQL isn't using them implicitly, and it is up to the application to call provided statements to obtain and release these locks, and to give a meaning to these locks.&lt;/p&gt;

&lt;p&gt;We can even use advisory locking to control synchronization of a code that isn't managing data from the database - for example, to make API calls, access files on a disk, or anything else.&lt;/p&gt;

&lt;p&gt;Advisory locks can be acquired at session level or at transaction level. The PostgreSQL &lt;a href="https://www.postgresql.org/docs/11/explicit-locking.html#ADVISORY-LOCKS"&gt;documentation&lt;/a&gt; explains the difference:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Once acquired at session level, an advisory lock is held until explicitly released or the session ends. Unlike standard lock requests, session-level advisory lock requests do not honor transaction semantics: a lock acquired during a transaction that is later rolled back will still be held following the rollback, and likewise an unlock is effective even if the calling transaction fails later. A lock can be acquired multiple times by its owning process; for each completed lock request there must be a corresponding unlock request before the lock is actually released.&lt;/p&gt;

&lt;p&gt;Transaction-level lock requests, on the other hand, behave more like regular lock requests: they are automatically released at the end of the transaction, and there is no explicit unlock operation. This behavior is often more convenient than the session-level behavior for short-term usage of an advisory lock.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are multiple functions provided by PostgreSQL to acquire and release exclusive or shared, session or transactional level advisory locks. Again, check out the &lt;a href="https://www.postgresql.org/docs/11/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS"&gt;official documentation&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;Here are some of the provided functions for session level locks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pg_advisory_lock(key bigint)&lt;/code&gt; obtains exclusive session level advisory lock. If another session already holds a lock on the same resource identifier, this function will wait until the resource becomes available. Multiple lock requests stack, so that if the resource is locked three times it must then be unlocked three times.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pg_try_advisory_lock(key bigint)&lt;/code&gt; obtains exclusive session level advisory lock if available. It's similar to &lt;code&gt;pg_advisory_lock&lt;/code&gt;, except it will not wait for the lock to become available - it will either obtain the lock and return true, or return false if the lock cannot be acquired immediately.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pg_advisory_unlock(key bigint)&lt;/code&gt; releases an exclusive session level advisory lock.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And here are some for transaction level locks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pg_advisory_xact_lock(key bigint)&lt;/code&gt; obtains exclusive transaction level advisory lock. It works the same as &lt;code&gt;pg_advisory_lock&lt;/code&gt;, except the lock is automatically released at the end of the current transaction and cannot be released explicitly.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pg_try_advisory_xact_lock(key bigint)&lt;/code&gt; obtains exclusive transaction level advisory lock if available. It works the same as &lt;code&gt;pg_try_advisory_lock&lt;/code&gt;, except the lock is automatically released at the end of the transaction and cannot be released explicitly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A simple LockManager
&lt;/h2&gt;

&lt;p&gt;We can use advisory locks in Ruby on Rails by creating a small class responsible for obtaining and releasing the lock. Let's call it the LockManager.&lt;/p&gt;

&lt;p&gt;One small inconvenience is that advisory locks in PostgreSQL use a number as an argument. However, locking a specific number isn't too readable and self-explanatory. It would be better if we could use a string for this purpose.&lt;/p&gt;

&lt;p&gt;A possible solution is to use the &lt;code&gt;Zlib.crc32&lt;/code&gt; hash function. It's commonly used for checksums, but we can use it to convert any string into the number required by PostgreSQL.&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;LockManager&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;with_transaction_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lock_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;lock_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Zlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crc32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lock_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;)&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;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt; &lt;span class="k"&gt;do&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;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SELECT pg_advisory_xact_lock(&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;lock_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;yield&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="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_session_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lock_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;lock_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Zlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crc32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lock_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;begin&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;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SELECT pg_advisory_lock(&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;lock_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;yield&lt;/span&gt;
    &lt;span class="k"&gt;ensure&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;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SELECT pg_advisory_unlock(&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;lock_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, for example, we can use this class in the following way:&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;LockManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_transaction_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"subscription_usage_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;new_usage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_subscription_usage&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_attributes!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;subscription_usage: &lt;/span&gt;&lt;span class="n"&gt;new_usage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Original article: &lt;a href="https://www.kostolansky.sk/posts/postgresql-advisory-locks/"&gt;Using PostgreSQL advisory locks to control concurrency&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>postgres</category>
      <category>locking</category>
    </item>
    <item>
      <title>How to migrate primary keys from bigint to UUID in Rails</title>
      <dc:creator>Juraj Kostolanský</dc:creator>
      <pubDate>Wed, 13 Mar 2019 13:00:00 +0000</pubDate>
      <link>https://dev.to/jkostolansky/how-to-migrate-primary-keys-from-bigint-to-uuid-jfn</link>
      <guid>https://dev.to/jkostolansky/how-to-migrate-primary-keys-from-bigint-to-uuid-jfn</guid>
      <description>&lt;p&gt;Rails uses &lt;code&gt;bigint&lt;/code&gt; as a default primary key type for PostgreSQL databases. This is a solid option, but you may consider using &lt;code&gt;UUID&lt;/code&gt; as primary keys. There is and endless debate about pros and cons of each of them, so I'm not going to repeat the arguments here. However, if you've decided to migrate to UUIDs, here is one possible solution.&lt;/p&gt;

&lt;p&gt;Let's say we have two tables in our database - &lt;code&gt;users&lt;/code&gt; and &lt;code&gt;comments&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="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:users&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="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:comments&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="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;references&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;foreign_key: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&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 possible way to migrate these tables to UUIDs is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add UUID columns to tables&lt;/li&gt;
&lt;li&gt;Migrate associations&lt;/li&gt;
&lt;li&gt;Drop ID columns, rename UUID to ID and use them as primary keys&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This migration process is in the following example, which is pretty self-explanatory.&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;MigrateToUuid&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.2&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="c1"&gt;# Add UUID columns&lt;/span&gt;
    &lt;span class="n"&gt;add_column&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="ss"&gt;:uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"gen_random_uuid()"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;add_column&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"gen_random_uuid()"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Add UUID columns for associations&lt;/span&gt;
    &lt;span class="n"&gt;add_column&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:user_uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt;

    &lt;span class="c1"&gt;# Populate UUID columns for associations&lt;/span&gt;
    &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="sh"&gt;
      UPDATE comments SET user_uuid = users.uuid
      FROM users WHERE comments.user_id = users.id;
&lt;/span&gt;&lt;span class="no"&gt;    SQL&lt;/span&gt;

    &lt;span class="c1"&gt;# Change null&lt;/span&gt;
    &lt;span class="n"&gt;change_column_null&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:user_uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;    

    &lt;span class="c1"&gt;# Migrate UUID to ID for associations&lt;/span&gt;
    &lt;span class="n"&gt;remove_column&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:user_id&lt;/span&gt;
    &lt;span class="n"&gt;rename_column&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:user_uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:user_id&lt;/span&gt;

    &lt;span class="c1"&gt;# Add indexes for associations&lt;/span&gt;
    &lt;span class="n"&gt;add_index&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:user_id&lt;/span&gt;    

    &lt;span class="c1"&gt;# Add foreign keys&lt;/span&gt;
    &lt;span class="n"&gt;add_foreign_key&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt;    

    &lt;span class="c1"&gt;# Migrate primary keys from UUIDs to IDs&lt;/span&gt;
    &lt;span class="n"&gt;remove_column&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="ss"&gt;:id&lt;/span&gt;
    &lt;span class="n"&gt;remove_column&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;
    &lt;span class="n"&gt;rename_column&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="ss"&gt;:uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;
    &lt;span class="n"&gt;rename_column&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;
    &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="s2"&gt;"ALTER TABLE users    ADD PRIMARY KEY (id);"&lt;/span&gt;
    &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="s2"&gt;"ALTER TABLE comments ADD PRIMARY KEY (id);"&lt;/span&gt;

    &lt;span class="c1"&gt;# Add indexes for ordering by date&lt;/span&gt;
    &lt;span class="n"&gt;add_index&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="ss"&gt;:created_at&lt;/span&gt;
    &lt;span class="n"&gt;add_index&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:created_at&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;gen_random_uuid()&lt;/code&gt; function is from &lt;code&gt;pgcrypto&lt;/code&gt; extension, so make sure it's enabled in your databse before running this migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EXTENSION&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;pgcrypto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also easily write the &lt;code&gt;down&lt;/code&gt; method to make the migration reversible.&lt;/p&gt;




&lt;p&gt;Original article: &lt;a href="https://www.kostolansky.sk/posts/how-to-migrate-to-uuid/"&gt;How to migrate primary keys from bigint to UUID&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>postgres</category>
      <category>uuid</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Localize your Rails enums</title>
      <dc:creator>Juraj Kostolanský</dc:creator>
      <pubDate>Tue, 26 Feb 2019 16:00:00 +0000</pubDate>
      <link>https://dev.to/jkostolansky/localize-your-rails-enums-55ki</link>
      <guid>https://dev.to/jkostolansky/localize-your-rails-enums-55ki</guid>
      <description>&lt;p&gt;When Rails 4.1 was released, it came out with a bunch of new features. One of those was ActiveRecord enums.&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;Conversation&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;enum&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:archived&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;conversation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Conversation&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;status: :active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; "active"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a handy feature. However, if you try to localize your Rails app, for example for some select boxes, or just to show the current value, you will quickly realize that there is no built-in feature to translate the enum values.&lt;/p&gt;

&lt;p&gt;Starting from Rails 5, all models will inherit from ApplicationRecord. We can use that to create our universal solution.&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;ApplicationRecord&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;Base&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;abstract_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;human_enum_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enum_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;enum_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;enum_i18n_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;enum_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pluralize&lt;/span&gt;
    &lt;span class="no"&gt;I18n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"activerecord.attributes.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;i18n_key&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;enum_i18n_key&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;enum_value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&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;This little method allows us to use the locale files in the following way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;en&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;activerecord&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;conversation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;statuses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;active&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Active conversation&lt;/span&gt;
          &lt;span class="na"&gt;archived&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Archived conversation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we can simply use the class method &lt;code&gt;human_enum_name&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="no"&gt;Conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;human_enum_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; "Active conversation"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And with an instance:&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;conversation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Conversation&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;status: :active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;Conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;human_enum_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; "Active conversation"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Actually, I use this approach quite often, so I've built a little gem called &lt;a href="https://github.com/jkostolansky/human_enum_name"&gt;human_enum_name&lt;/a&gt;. Feel free to use it!&lt;/p&gt;

&lt;p&gt;And, if you want to manage your YAML translation files in a clever way, check out my project &lt;a href="https://www.localedata.com"&gt;LocaleData.com&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Original article: &lt;a href="https://www.kostolansky.sk/posts/localize-rails-enums/"&gt;Localize your Rails enums&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>enums</category>
      <category>i18n</category>
    </item>
    <item>
      <title>How to expose your local development server</title>
      <dc:creator>Juraj Kostolanský</dc:creator>
      <pubDate>Mon, 11 Feb 2019 08:00:00 +0000</pubDate>
      <link>https://dev.to/jkostolansky/how-to-expose-your-local-development-server-3h69</link>
      <guid>https://dev.to/jkostolansky/how-to-expose-your-local-development-server-3h69</guid>
      <description>&lt;p&gt;If you need to expose your local development web server to the Internet, there are multiple tools you can use. For example: &lt;a href="https://ngrok.com"&gt;ngrok&lt;/a&gt;, &lt;a href="https://pagekite.net"&gt;pagekite&lt;/a&gt;, &lt;a href="https://forwardhq.com"&gt;forward&lt;/a&gt;, &lt;a href="https://localtunnel.me"&gt;localtunnel&lt;/a&gt;, ...&lt;/p&gt;

&lt;p&gt;Or, you can build your own for free, if you have your own VPS. It can support SSL and works behind a NAT. Here is how.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server setup
&lt;/h2&gt;

&lt;p&gt;You will need an nginx server installed on your VPS and a domain name pointing to it, like &lt;code&gt;dev.example.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then add this (or similar) nginx configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;dev.example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;rewrite&lt;/span&gt; &lt;span class="s"&gt;^&lt;/span&gt; &lt;span class="s"&gt;https://dev.example.com&lt;/span&gt;&lt;span class="nv"&gt;$request_uri&lt;/span&gt;&lt;span class="s"&gt;?&lt;/span&gt; &lt;span class="s"&gt;permanent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt; &lt;span class="s"&gt;http2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;dev.example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;access_log&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;error_log&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:8000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="s"&gt;https&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$http_host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt; &lt;span class="n"&gt;/path/to/ssl/dev.example.com/fullchain.pem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="n"&gt;/path/to/ssl/dev.example.com/privkey.pem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting part is the &lt;code&gt;proxy_pass&lt;/code&gt;. It will point &lt;code&gt;dev.example.com&lt;/code&gt; to the local port &lt;code&gt;8000&lt;/code&gt; on the server.&lt;/p&gt;

&lt;p&gt;Make sure that the following option is allowed in the &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; and reload the SSH server if needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;GatewayPorts clientspecified
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Local setup
&lt;/h2&gt;

&lt;p&gt;Now you need to create an SSH tunnel betweeen your local machine and the server.&lt;/p&gt;

&lt;p&gt;On your development machine, put this function inside your &lt;code&gt;.bashrc&lt;/code&gt; file (or &lt;code&gt;.zshrc&lt;/code&gt; if you are using ZSH). In this example, &lt;code&gt;me&lt;/code&gt; is a remote linux user for the SSH access into the VPS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;function &lt;/span&gt;devtunnel &lt;span class="o"&gt;{&lt;/span&gt;
    ssh &lt;span class="nt"&gt;-nNT&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="s2"&gt;"127.0.0.1:8000:localhost:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; me@dev.example.com
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will forward the remote port &lt;code&gt;8000&lt;/code&gt; to the local port specified as an argument; &lt;code&gt;-n&lt;/code&gt; prevents reading from stdin, &lt;code&gt;-N&lt;/code&gt; means that you do not want to execute remote commands, &lt;code&gt;-T&lt;/code&gt; disables pseudo-tty allocation, and &lt;code&gt;-R&lt;/code&gt; means reverse port forwarding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;After this setup, exposing your local development server is easy. If you are a Rails developer, you are running your local web server on the port &lt;code&gt;3000&lt;/code&gt;. To expose it publicly, run this locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;devtunnel 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, you (or someone else) can visit &lt;code&gt;dev.example.com&lt;/code&gt; to access your local server. Enjoy!&lt;/p&gt;




&lt;p&gt;Original article: &lt;a href="https://www.kostolansky.sk/posts/expose-your-local-development-server/"&gt;How to expose your local development server&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nginx</category>
      <category>linux</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The unexpected after_commit behaviour</title>
      <dc:creator>Juraj Kostolanský</dc:creator>
      <pubDate>Wed, 23 Jan 2019 20:00:00 +0000</pubDate>
      <link>https://dev.to/jkostolansky/the-unexpected-aftercommit-behaviour-4igf</link>
      <guid>https://dev.to/jkostolansky/the-unexpected-aftercommit-behaviour-4igf</guid>
      <description>&lt;p&gt;Today I've learned something interesting. The new Rails callbacks &lt;code&gt;after_create_commit&lt;/code&gt;, &lt;code&gt;after_update_commit&lt;/code&gt; and &lt;code&gt;after_destroy_commit&lt;/code&gt; can behave in a way I didn't expect.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;after_commit&lt;/code&gt; callback is a well-known part of the Ruby on Rails framework. It's called after a record has been created, updated, or destroyed and after the corresponding database transaction has been commited. It's the primary method to use if we want to trigger a background job associated with a record.&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;User&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;Base&lt;/span&gt;
  &lt;span class="n"&gt;after_commit&lt;/span&gt; &lt;span class="ss"&gt;:schedule_welcome_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;on: :create&lt;/span&gt;  

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;schedule_welcome_email&lt;/span&gt;
    &lt;span class="no"&gt;WelcomeEmailJob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform_later&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&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;The Ruby on Rails 5 came with some new &lt;code&gt;after_*_commit&lt;/code&gt; callbacks. Before it looked like this:&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;after_commit&lt;/span&gt; &lt;span class="ss"&gt;:action1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;on: :create&lt;/span&gt;
&lt;span class="n"&gt;after_commit&lt;/span&gt; &lt;span class="ss"&gt;:action2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;on: :update&lt;/span&gt;
&lt;span class="n"&gt;after_commit&lt;/span&gt; &lt;span class="ss"&gt;:action3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;on: :destroy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now we can use:&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;after_create_commit&lt;/span&gt;  &lt;span class="ss"&gt;:action1&lt;/span&gt;
&lt;span class="n"&gt;after_update_commit&lt;/span&gt;  &lt;span class="ss"&gt;:action2&lt;/span&gt;
&lt;span class="n"&gt;after_destroy_commit&lt;/span&gt; &lt;span class="ss"&gt;:action3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Let's say we want to trigger the &lt;code&gt;broadcast&lt;/code&gt; method after a record has been created or destroyed. We can try using the new callbacks:&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;Comment&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;Base&lt;/span&gt;
  &lt;span class="n"&gt;after_create_commit&lt;/span&gt;  &lt;span class="ss"&gt;:broadcast&lt;/span&gt;
  &lt;span class="n"&gt;after_destroy_commit&lt;/span&gt; &lt;span class="ss"&gt;:broadcast&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;broadcast&lt;/span&gt;
    &lt;span class="no"&gt;BroadcastJob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform_later&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&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;That looks good! The &lt;code&gt;after_destroy_commit&lt;/code&gt; works as expected. However, for some reason, the first &lt;code&gt;after_create_commit&lt;/code&gt; is never triggered. But why?&lt;/p&gt;

&lt;h2&gt;
  
  
  The reason
&lt;/h2&gt;

&lt;p&gt;Let's take a look at the source code of the &lt;code&gt;after_create_commit&lt;/code&gt; method:&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;after_create_commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;set_options_for_callbacks!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;on: :create&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;set_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:commit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:after&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, these methods are effectively aliases for the old &lt;code&gt;after_commit&lt;/code&gt; callback with the &lt;code&gt;:on&lt;/code&gt; option specified. And subsequent &lt;code&gt;after_commit&lt;/code&gt; declarations override former declarations for the same method. That can be pretty surprising!&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;To solve this issue, we can use the old callback. The &lt;code&gt;:on&lt;/code&gt; option supports an array of multiple life cycle events, so the solution is simple and looks like this:&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;after_commit&lt;/span&gt; &lt;span class="ss"&gt;:broadcast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;on: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Original article: &lt;a href="https://www.kostolansky.sk/posts/unexpected-after-commit-behaviour/"&gt;The unexpected after_commit behaviour&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>callbacks</category>
    </item>
    <item>
      <title>The Story Behind LocaleData</title>
      <dc:creator>Juraj Kostolanský</dc:creator>
      <pubDate>Fri, 09 Nov 2018 11:32:55 +0000</pubDate>
      <link>https://dev.to/jkostolansky/the-story-behind-localedata-3fna</link>
      <guid>https://dev.to/jkostolansky/the-story-behind-localedata-3fna</guid>
      <description>&lt;h2&gt;
  
  
  Identifying a pain point
&lt;/h2&gt;

&lt;p&gt;I work as a software engineer for a young ed-tech company that develops a worldwide Ruby on Rails SaaS app. We collaborate with our external translators who help us localize it into multiple languages. As you may already know, the Rails framework uses YAML file format for storing translations. This is a really nice default approach, but it has some cons, too. YAML files are best for computers and good-enough for programmers, but they are terrible for collaboration with non-technical translators. So we tried to find a simple and cheap solution.&lt;/p&gt;

&lt;p&gt;Rails allows developers to use multiple different backends to store their translations. Therefore, a possible solution could be to use an SQL database for this purpose and to build a simple web interface for editing our content. But we have a problem here. We deploy an isolated instance of our app for every customer. That means every instance has its own database and we would need (A) to edit translations for each instance or (B) to introduce higher coupling into our infrastructure — by syncing translations, using a master database, or something like that. None of this was a suitable solution.&lt;/p&gt;

&lt;p&gt;A better approach would be a micro-service for editing our existing YAML files. What we really need was a centralized interface for our external translators. We wanted to (1) upload our YAML files, (2) invite some translators, (3) let them do their job, (4) download the result. We were able to find and try such solutions. Unfortunately, they provide really cool features which we didn’t need, therefore they were too pricey for us.&lt;/p&gt;

&lt;p&gt;I’ve been always interested in micro-services built as side hustles. I’ve read the &lt;em&gt;“build something for yourself, be your first customer”&lt;/em&gt; advice too many times. This seemed to be a hot candidate for such an experiment. So, okay, challenge accepted! I bought the &lt;a href="https://www.localedata.com" rel="noopener noreferrer"&gt;localedata.com&lt;/a&gt; domain in May 2017 and started working on it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7trqiyyrnni6znsvwub.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7trqiyyrnni6znsvwub.png" alt="Before and after" width="800" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Building an MVP
&lt;/h2&gt;

&lt;p&gt;After I decided to solve this issue on my own in May 2017, it was time to choose the right technologies and start typing the code. I picked the stack that I was already very familiar with, nothing fancy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ruby on Rails as an application framework (obviously)&lt;/li&gt;
&lt;li&gt;Turbolinks with some vanilla JS for a better user experience&lt;/li&gt;
&lt;li&gt;PostgreSQL as a database&lt;/li&gt;
&lt;li&gt;Redis with Sidekiq for background jobs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There isn’t too much to talk about. The core of this project is pretty straightforward and I’ve been working on it alone. I haven’t use any wireframes or mockups. I just put a bunch of tasks and ideas into my Trello board and…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4ns6a2ktgnctdixciu8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4ns6a2ktgnctdixciu8.png" alt="Prototype" width="800" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It takes about six months from the first project idea to the first public deployment. This seems to be a very long time, however, my productivity during the summer was quite low (among other things, I was walking the beautiful Wales Coast Path). It could probably be finished much sooner, but there was no reason to rush it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8pycw8v7m50f3fjn9f2x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8pycw8v7m50f3fjn9f2x.png" alt="Git graph 1" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Besides programming LocaleData, I’ve been working full-time for the company I am employed in. Sometimes it was quite hard to push myself to work on a side project in the evening, but the key seems to be just to do something regularly, preferably daily. New feature, minor enhancement, or even a small bug fix. Five minutes or five hours. That doesn’t matter. I just need to keep that momentum going, to see some real progress. I’ve found that this really helps me stay motivated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxbh8owmxjy2qm9igcorl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxbh8owmxjy2qm9igcorl.png" alt="Git graph 2" width="800" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I publicly deployed the first release in November 2017. I canceled the third-party subscription our company was using, uploaded our translation files into LocaleData and introduced this new tool to my teammates and to our translators. Long story short: They like it even better than the previous pricey solution (and, to my surprise, not only for its zero-price).&lt;/p&gt;

&lt;h2&gt;
  
  
  Do you want to try?
&lt;/h2&gt;

&lt;p&gt;Just go to &lt;a href="https://www.localedata.com/" rel="noopener noreferrer"&gt;www.localedata.com&lt;/a&gt; and create your new account. If you have any questions or if you miss a feature, just send me a message.&lt;/p&gt;




&lt;p&gt;Original article: &lt;a href="https://www.kostolansky.sk/posts/the-story-behind-localedata-com" rel="noopener noreferrer"&gt;The Story Behind LocaleData.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>i18n</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
