<?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: CHADDA Chakib</title>
    <description>The latest articles on DEV Community by CHADDA Chakib (@zimski).</description>
    <link>https://dev.to/zimski</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%2F91172%2Fbf6ef738-b096-4a28-87a8-e03faf8181e5.jpg</url>
      <title>DEV Community: CHADDA Chakib</title>
      <link>https://dev.to/zimski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zimski"/>
    <language>en</language>
    <item>
      <title>Prevent ActiveStorage to kill your Rails app</title>
      <dc:creator>CHADDA Chakib</dc:creator>
      <pubDate>Fri, 05 Feb 2021 20:50:00 +0000</pubDate>
      <link>https://dev.to/zimski/prevent-activestorage-to-kill-your-rails-app-18p3</link>
      <guid>https://dev.to/zimski/prevent-activestorage-to-kill-your-rails-app-18p3</guid>
      <description>&lt;p&gt;ActiveStoage is a superb piece of code that makes uploads and file manipulation, resizing, and previews like a breeze on your ruby on rails application.&lt;/p&gt;

&lt;p&gt;When you're, like me, hosting your app on Heroku, using s3 or other external storage to persist your file uploads, ActiveStorage makes it as easy as adding some lines on a config file.&lt;/p&gt;

&lt;p&gt;Using ActiveStorage was for me a benediction, small efforts to maintain and it works reliably most of the time.&lt;/p&gt;

&lt;p&gt;One of the main features of the ActiveStorage is the ability to resize pictures and create variants.&lt;br&gt;
It uses &lt;em&gt;ImagicMagic&lt;/em&gt; ou &lt;em&gt;LibVibs&lt;/em&gt; libraries on the background to make this happen.&lt;/p&gt;

&lt;p&gt;To create a variant we need to write:&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;# resize the picture to fit 200px by 200px by respecting the ratio. &lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= image_tag picture.variant(resize: "200x200", auto_orient: true) %&amp;gt; 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the browser fetches the image for the first time, ActiveStorage will check if it has the asked variant of this picture ready, if not it will call &lt;em&gt;in sync&lt;/em&gt; the ImagicMagic process to do the resizing and send it back to the browser.&lt;/p&gt;

&lt;p&gt;If the picture is stored to &lt;code&gt;S3 from aws&lt;/code&gt; for example, the process will follow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download the image from S3 into the server&lt;/li&gt;
&lt;li&gt;Call the process to resize the picture&lt;/li&gt;
&lt;li&gt;Upload the variant to S3 and update the database&lt;/li&gt;
&lt;li&gt;Send an HTTP redirect to the new location of the image on S3&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This a lot of work and it takes time (some seconds on small dynos).&lt;/p&gt;

&lt;p&gt;Meanwhile, the process/thread is too busy and cannot work on other requests.&lt;/p&gt;

&lt;p&gt;Imagine, one page contains 5 or 6 pictures, this will make your server busy for resizing all the pictures, may cause a significant slow down for the following requests.&lt;/p&gt;

&lt;p&gt;You can tell me, it's not an issue, it's done only once, but when you have an app with uploads occurring all the day, running on a small dyno, this will be a serious issue.&lt;/p&gt;

&lt;p&gt;If you care about the user experience and the confidence on your product, you should fix it.&lt;/p&gt;

&lt;p&gt;You can throw more money on you Heroku, faster CPU the mitigate the issue, bare with me, I have a solution for you and will cost you only some lines of codes 😇&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Picture resizing done on a background Job
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;To make your web server happy, overload all the heavy works with your background workers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When an upload is done, start a background job to process the variants&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;ResizePhotoJob&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationJob&lt;/span&gt;
  &lt;span class="n"&gt;queue_as&lt;/span&gt; &lt;span class="ss"&gt;:default&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resize_cmd&lt;/span&gt;&lt;span class="ss"&gt;:nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;resize_cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
      &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;auto_orient: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;processed&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="c1"&gt;# the `.processed` will force the resizing to be done in sync&lt;/span&gt;
      &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;resize: &lt;/span&gt;&lt;span class="n"&gt;resize_cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;auto_orient: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;processed&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="c1"&gt;# and call it for your different needed variants&lt;/span&gt;
&lt;span class="no"&gt;ResizePhotoJob&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="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;ResizePhotoJob&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="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;resize_cmd: &lt;/span&gt;&lt;span class="s2"&gt;"250x250"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Of course, you need to write some tests for this! I will help you&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="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'test_helper'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProcessPhotosFromAttachmentJobTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveJob&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TestCase&lt;/span&gt;
  &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s1"&gt;'resize photos'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

    &lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;profiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:jamal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;io: &lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"photo_1.jpeg"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="ss"&gt;filename: &lt;/span&gt;&lt;span class="s1"&gt;'image'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;avatar&lt;/span&gt;

    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;resize: &lt;/span&gt;&lt;span class="s1"&gt;'200x200'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;auto_orient: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;key&lt;/span&gt;
    &lt;span class="n"&gt;refute&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="no"&gt;ResizePhotoJob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform_now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;resize_cmd: &lt;/span&gt;&lt;span class="s2"&gt;"200x200"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;resize: &lt;/span&gt;&lt;span class="s1"&gt;'200x200'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;auto_orient: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;key&lt;/span&gt;
    &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&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;Each variant on the ActiveStorage has a key ( a simple hash on the ImageMagic params )&lt;br&gt;
The variant is stored and retrieved using this key.&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;# ==&amp;gt; retrieve the variant key &lt;/span&gt;
&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;resize: &lt;/span&gt;&lt;span class="s1"&gt;'200x200'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;auto_orient: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test will see if the variant exists before the job executions and its existence after.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;That's good, now we process the variants on the background, and &lt;em&gt;hopefully&lt;/em&gt; when the user tries to access the picture, he will find the variant processed and ready to be served.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Unfortunately, it's not always the case 😔&lt;/p&gt;

&lt;p&gt;Even worse, some pictures will fail to resize, for example on a small Heroku dyno, there is not enough memory allowed to ImageMagic to process some big pictures &amp;gt; 15Mb.&lt;/p&gt;

&lt;p&gt;I got this error when resizing an image of 15mb (12000x9000) on a Hobby Dyno on Heroku&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;convert /tmp/ActiveStorage-36637-20210128-4-18i0017.jpg[0] &lt;span class="nt"&gt;-auto-orient&lt;/span&gt; &lt;span class="nt"&gt;-resize&lt;/span&gt; 250x250 &lt;span class="nt"&gt;-auto-orient&lt;/span&gt;
&lt;span class="s1"&gt;'/tmp/image_processing20210128-4-gnzbsa.jpg'&lt;/span&gt; failed with error:
convert-im6.q16: DistributedPixelCache &lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt; 
@error/distribute-cache.c/ConnectPixelCacheServer/244.
convert-im6.q16: cache resources exhausted
&lt;span class="s1"&gt;'/tmp/ActiveStorage-36637-20210128-4-18i0017.jpg'&lt;/span&gt; 
@error/cache.c/OpenPixelCache/3984.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hopefully, in my case, this is not the regular size of pictures uploaded on my app.&lt;/p&gt;

&lt;p&gt;For those cases, our job is useless, each request will start all the optimization task and it will take too much time.&lt;/p&gt;

&lt;p&gt;These requests will end up with a &lt;code&gt;503 timeout&lt;/code&gt; and make worse the process/thread availability to process our regular requests.&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Prevent resizing on the fly
&lt;/h1&gt;

&lt;p&gt;My solution is dead simple: &lt;br&gt;
When the variant is not available, do not resize and redirect to the original picture.&lt;/p&gt;

&lt;p&gt;Okay, the user will download a big picture, that's good enough, the user will get his picture, okay it will take much longer to download but will use the &lt;code&gt;S3 server&lt;/code&gt; resources, not mine 😆.&lt;/p&gt;

&lt;p&gt;My app will only redirect the browser to the &lt;code&gt;S3 servers&lt;/code&gt; when the actual download will occur.&lt;/p&gt;

&lt;p&gt;We will use the same code used on the tests to check if the variant is present.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;      &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;variant = &lt;/span&gt;&lt;span class="s"&gt;picture.variant(resize:&lt;/span&gt; &lt;span class="err"&gt;"200&lt;/span&gt;&lt;span class="na"&gt;x200&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;auto_orient:&lt;/span&gt; &lt;span class="na"&gt;true&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt; &lt;span class="na"&gt;picture.service.exist&lt;/span&gt;&lt;span class="err"&gt;?(&lt;/span&gt;&lt;span class="na"&gt;variant.key&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;image_tag&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;   
      &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;else&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;image_tag&lt;/span&gt; &lt;span class="na"&gt;picture&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This small line of codes will make stay a little longer on small cheap Heroku dynos, make your app more resilient, and make you save money.&lt;/p&gt;

&lt;p&gt;Glad to hear if you have done this kind of &lt;em&gt;Budget optimization&lt;/em&gt; in another way.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>heroku</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Vim tips: Speed up with Vim tabs</title>
      <dc:creator>CHADDA Chakib</dc:creator>
      <pubDate>Wed, 20 Jan 2021 16:18:46 +0000</pubDate>
      <link>https://dev.to/zimski/vim-tips-speed-up-with-vim-tabs-51k3</link>
      <guid>https://dev.to/zimski/vim-tips-speed-up-with-vim-tabs-51k3</guid>
      <description>&lt;p&gt;I struggled a long time ago with vim when playing with multiple projects at the same time or playing with a code base with conflicting folders related to multiple languages used at the same time.&lt;/p&gt;

&lt;p&gt;As an example, a typical rails code base will have a lot of ruby code and a bunch of javascript/typescript code ( stimulus, react ... ).&lt;/p&gt;

&lt;p&gt;As an example, we can have a ruby service for invoicing for example &lt;br&gt;
&lt;code&gt;.../services/fetch_invoice_service.rb&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and a &lt;code&gt;typescript&lt;/code&gt; service used to fetch something from the API&lt;br&gt;
&lt;code&gt;.../javascript/src/services/fetchDataService.ts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;On vim, using &lt;code&gt;fzf&lt;/code&gt; I found my self using the &lt;code&gt;Gfiles&lt;/code&gt;commande to find the desired file typing for example:&lt;br&gt;
&lt;code&gt;servicefetch&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Fzf&lt;/code&gt; will show ruby and javascript files on the same list.&lt;br&gt;
Sometimes is really annoying when you're working on a big codebase.&lt;/p&gt;

&lt;p&gt;Also, when you are working on multiple project on the same time with vim, usually you find your self having multiple vim instances.&lt;/p&gt;

&lt;p&gt;We can fix this with &lt;strong&gt;Vim tabs&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Vim tab to rescue
&lt;/h1&gt;

&lt;p&gt;You can create a new tab on vim using the command: &lt;code&gt;:tabnew&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Navigation between tabs is done by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;gt&lt;/code&gt; cycling between tabs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{tab number}gt&lt;/code&gt;: switch to a specific tab.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting a working directory by tab:
&lt;/h2&gt;

&lt;p&gt;To make your search for files really fast, you can set a working directory by tab.&lt;br&gt;
:tcd [path to your folder]&lt;/p&gt;

&lt;p&gt;Calling &lt;code&gt;:Gfiles&lt;/code&gt; will use this path as a root directory&lt;/p&gt;

&lt;h2&gt;
  
  
  Rails workflow
&lt;/h2&gt;

&lt;p&gt;I found my self for a rails app using three tabs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The root tab on my project&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9WhLyz6v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rl8w1z405avc17vohs74.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9WhLyz6v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rl8w1z405avc17vohs74.png" alt="Alt Text" width="880" height="506"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The javascript tab within &lt;code&gt;app/javascript&lt;/code&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KVIcbjJ_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/brzyb5q3100jrlpfmkws.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KVIcbjJ_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/brzyb5q3100jrlpfmkws.png" alt="Alt Text" width="880" height="506"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The terminals tabs &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NsROzPmM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3syj6s5kckx9rm1tuokw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NsROzPmM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3syj6s5kckx9rm1tuokw.png" alt="Alt Text" width="880" height="506"&gt;&lt;/a&gt; &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>vim</category>
      <category>rails</category>
      <category>nvim</category>
    </item>
    <item>
      <title>HEY Turbolinks! RJS is dead ...</title>
      <dc:creator>CHADDA Chakib</dc:creator>
      <pubDate>Sat, 20 Jun 2020 14:30:42 +0000</pubDate>
      <link>https://dev.to/zimski/hey-turbolinks-rjs-is-dead-13bj</link>
      <guid>https://dev.to/zimski/hey-turbolinks-rjs-is-dead-13bj</guid>
      <description>&lt;p&gt;I am super excited about the release of the new product crafted by the &lt;code&gt;Basecamp&lt;/code&gt; team, the &lt;code&gt;HEY&lt;/code&gt; email service.&lt;/p&gt;

&lt;p&gt;With all the &lt;code&gt;@dhh&lt;/code&gt; teasing about the upgraded frontend stack, I was pumped to know how the new internals work, especially how the &lt;code&gt;HEY app&lt;/code&gt; handles real-time updates and all the HTML mutations.&lt;/p&gt;

&lt;p&gt;I want to share with you my thoughts about this subject and to be clear, &lt;strong&gt;it's only how I have understood what I have observed on the browser, nothing official.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The starting point
&lt;/h2&gt;

&lt;p&gt;I tweeted yesterday about what I am seeing from the Ajax requests on the &lt;code&gt;hey.com&lt;/code&gt; app&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H6mmI9hz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/Ea3WHpMXkAAbLXU.jpg" alt="unknown tweet media content"&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--MheQ8zZr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/523785572595146752/D4QNiCz3_normal.jpeg" alt="chadda chakib profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        chadda chakib
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @zimski1990
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Interesting html mutation pattern seen on &lt;a href="https://t.co/tX5zyCrhIH"&gt;hey.com&lt;/a&gt;, the same pattern is used for updates over the socket &lt;br&gt;&lt;a href="https://twitter.com/dhh"&gt;@dhh&lt;/a&gt; we need to know more about that ! 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      09:25 AM - 19 Jun 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1273909754658684928" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1273909754658684928" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1273909754658684928" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;And this was retweeted by a &lt;code&gt;basecamp&lt;/code&gt; guy, well known for his works on rails, Trix ... etc&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--QdqoXPFY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/886701479792451584/DvhmZ3ER_normal.jpg" alt="Javan Makhmali profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Javan Makhmali
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="mentioned-user" href="https://dev.to/javan"&gt;@javan&lt;/a&gt;
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      &lt;a href="https://t.co/VR2T8tY60P"&gt;twitter.com/zimski1990/sta…&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      01:42 AM - 20 Jun 2020
    &lt;/div&gt;

      &lt;div class="ltag__twitter-tweet__quote"&gt;
        &lt;div class="ltag__twitter-tweet__quote__header"&gt;
          &lt;span class="ltag__twitter-tweet__quote__header__name"&gt;
            chadda chakib
          &lt;/span&gt;
          @zimski1990
        &lt;/div&gt;
        Interesting html mutation pattern seen on https://t.co/tX5zyCrhIH, the same pattern is used for updates over the socket 
@dhh we need to know more about that ! https://t.co/Y0Uheoo5fG
      &lt;/div&gt;

    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1274155757655785472" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1274155757655785472" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1274155757655785472" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;So I tell my self, maybe I should dig more to understand how this works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything starts with an HTML fragment
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;data-page-update=&lt;/span&gt;&lt;span class="s"&gt;"remove#posting_140----"&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;data-page-update=&lt;/span&gt;&lt;span class="s"&gt;"prepend#postings"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt;
        &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"posting_-----"&lt;/span&gt;
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"posting"&lt;/span&gt;
        &lt;span class="na"&gt;data-list-target=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;
        &lt;span class="na"&gt;data-bulk-actions-target=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;
        &lt;span class="na"&gt;data-identifier=&lt;/span&gt;&lt;span class="s"&gt;"------"&lt;/span&gt;
        &lt;span class="na"&gt;data-sort-code=&lt;/span&gt;&lt;span class="s"&gt;"-------"&lt;/span&gt;
        &lt;span class="na"&gt;data-box-freshness-target=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;
        &lt;span class="na"&gt;data-account-id=&lt;/span&gt;&lt;span class="s"&gt;"----"&lt;/span&gt;
        &lt;span class="na"&gt;data-account-purpose=&lt;/span&gt;&lt;span class="s"&gt;"home"&lt;/span&gt;
        &lt;span class="na"&gt;data-box-kind=&lt;/span&gt;&lt;span class="s"&gt;"imbox"&lt;/span&gt;
        &lt;span class="na"&gt;data-topic=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"posting__body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        ...
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I dig on the obfuscated &lt;code&gt;JavaScript&lt;/code&gt; available on the app, I understand this mechanism:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Receiving data
&lt;/h2&gt;

&lt;p&gt;The payload received from the socket or an Ajax call.&lt;br&gt;
The payload is a pure HTML.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Processing the payload
&lt;/h2&gt;

&lt;p&gt;This payload will be ingested and transformed into a valid HTML fragment document.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createRange&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;createContextualFragment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;playload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we have a valid html document, it's easier to process each tag and extract the needed informations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;template[data-page-update]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Commands to mutate the HTML
&lt;/h2&gt;

&lt;p&gt;Each tag will contain the command to be executed.&lt;br&gt;
This command is present on the &lt;code&gt;data-page-update&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;Commands I have seen on the code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;append&lt;/li&gt;
&lt;li&gt;prepend&lt;/li&gt;
&lt;li&gt;replace&lt;/li&gt;
&lt;li&gt;update&lt;/li&gt;
&lt;li&gt;remove&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The command follow this syntax: &lt;code&gt;[command]#[html_element_id]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;template..&amp;gt;&amp;lt;/template&amp;gt;&lt;/code&gt; tag can be empty, for commands like &lt;code&gt;remove&lt;/code&gt; or containing an HTML inside to be inserted somewhere.&lt;/p&gt;

&lt;p&gt;So if I follow all of this, we can see a &lt;em&gt;cleaner version&lt;/em&gt; of the &lt;code&gt;RJS&lt;/code&gt; used today on the rails app.&lt;/p&gt;

&lt;p&gt;No need to write and render a custom JS code to mutate the dom.&lt;br&gt;
With the current version, we were doing something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#posting_1409180&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#postings&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we have a clean HTML that contains the command to mutate the DOM.&lt;/p&gt;

&lt;p&gt;This follows the &lt;code&gt;Stimulus&lt;/code&gt; principals to add some attributes to the HTML to get sparkles ✨.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Content Template element 
&lt;/h2&gt;

&lt;p&gt;The documentation says that the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; is not rendered and we need some js to render it.&lt;/p&gt;

&lt;p&gt;We can imagine here to mix a standard HTML with this mutation template and render them on the page.&lt;br&gt;
The js processor will pick and execute the mutation.&lt;/p&gt;

&lt;p&gt;We can also stack all the mutations needed&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;tempate&lt;/span&gt; &lt;span class="na"&gt;mutation&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;   &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;tempate&lt;/span&gt; &lt;span class="na"&gt;mutation&lt;/span&gt; &lt;span class="err"&gt;2&lt;/span&gt;   &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;..&lt;span class="nt"&gt;&amp;lt;/tempate&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;tempate&lt;/span&gt; &lt;span class="na"&gt;mutation&lt;/span&gt; &lt;span class="err"&gt;3&lt;/span&gt;   &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;..&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;So we will get a 100% HTML over the wire, no Javascript, no JSON, and a clever client-side HTML renderer to execute these mutations.&lt;/p&gt;

&lt;p&gt;I think The main candidate to embrace this new mission is our beloved &lt;code&gt;Turbolinks&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turbolinks and sockets for a Realtime without hassle
&lt;/h2&gt;

&lt;p&gt;Using this pattern, I suppose on the next version of turbo links we can tell it to subscribe to some cable channel and will render all of this HTML mutations&lt;/p&gt;

&lt;h2&gt;
  
  
  Rails helper on the backend ??
&lt;/h2&gt;

&lt;p&gt;I hope also this will come with some helper to simply edit these template mutations.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>turbolinks</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Stripe Strong Customer Authentication &amp; Rails </title>
      <dc:creator>CHADDA Chakib</dc:creator>
      <pubDate>Sat, 19 Oct 2019 11:40:19 +0000</pubDate>
      <link>https://dev.to/zimski/stripe-strong-customer-authentication-rails-3c79</link>
      <guid>https://dev.to/zimski/stripe-strong-customer-authentication-rails-3c79</guid>
      <description>&lt;p&gt;You maybe heard about the migration of the &lt;strong&gt;Stripe CB payment&lt;/strong&gt; to &lt;code&gt;SCA Strong Customer Authentication&lt;/code&gt;&lt;br&gt;
All payment from the European countries are concerned.&lt;/p&gt;

&lt;p&gt;For more information, &lt;a href="https://stripe.com/fr/guides/strong-customer-authentication"&gt;The stripe sca doc&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  The customer experience through the SCA
&lt;/h1&gt;

&lt;p&gt;For your customer, after he puts the Credit card number, the bank can ask for a confirmation by &lt;code&gt;SMS&lt;/code&gt; or a push notification in bank app in his smartphone.&lt;/p&gt;

&lt;p&gt;Stripe now handle this and for better experience, they need to redirect the user to hosted page by stripe and will handle all the complexity.&lt;br&gt;
When succeed or failed, the user is redirected to your webapp.&lt;/p&gt;

&lt;p&gt;This is the flow that explain how this works&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JfxUYkO3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/r6l1s90mypcvdrezrhkt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JfxUYkO3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/r6l1s90mypcvdrezrhkt.png" alt="Alt Text" width="615" height="564"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main steps handled by the rails app are two&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The preparation of the checkout session [generation of the key]&lt;/li&gt;
&lt;li&gt;The validation of the payment [Verify the key]&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;
  
  
  1. The session preparation
&lt;/h1&gt;

&lt;p&gt;This will prepare &lt;code&gt;Stripe session&lt;/code&gt; for the checkout of the product and the redirection when the payment succeed or not.&lt;/p&gt;

&lt;p&gt;So we will prepare&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The name of the product&lt;/li&gt;
&lt;li&gt;The price&lt;/li&gt;
&lt;li&gt;The redirection when succeed or not
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="n"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gen_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Stripe&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Checkout&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Session&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="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;payment_method_types: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'card'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;line_items: &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
                       &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="ss"&gt;description: &lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="ss"&gt;amount: &lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="ss"&gt;currency: &lt;/span&gt;&lt;span class="s1"&gt;'eur'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="ss"&gt;quantity: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="ss"&gt;success_url: &lt;/span&gt;&lt;span class="n"&gt;confirm_product_payment_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;secret: &lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="ss"&gt;cancel_url: &lt;/span&gt;&lt;span class="n"&gt;cancel_product_payment_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@product&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 here is the &lt;code&gt;gen_secret&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We need to generate this &lt;code&gt;secret&lt;/code&gt; to be able to be sure and verify that the payment at &lt;code&gt;Stripe&lt;/code&gt; succeed and the callback is not faked&lt;/p&gt;

&lt;p&gt;We can generate this key like that in rails.&lt;br&gt;
Thanks to rails to provide everything inside the framework and no need to add foreign dependency.&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;gen_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&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;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;secret_key_base&lt;/span&gt;
  &lt;span class="vi"&gt;@crypt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MessageEncryptor&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="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;cipher: &lt;/span&gt;&lt;span class="s1"&gt;'aes-256-gcm'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="vi"&gt;@crypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encrypt_and_sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;token&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 I explained in the previous &lt;code&gt;UML flow&lt;/code&gt;, the callbacks containing this secret will be stored at &lt;code&gt;stripe&lt;/code&gt; using a secure connection from your server.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Payment process
&lt;/h2&gt;

&lt;p&gt;The payment process is classic, the customer will enter his credit card numbers and may use a strong authentication if needed (SMS code verification, push notification ... ect)&lt;/p&gt;

&lt;h2&gt;
  
  
  When the payment succeed
&lt;/h2&gt;

&lt;p&gt;At this step, the stripe servers will send to the browser the redirection to the right callback url.&lt;br&gt;
This callback is containing our &lt;code&gt;secret&lt;/code&gt; generated before.&lt;br&gt;
Time to validation !&lt;/p&gt;
&lt;h1&gt;
  
  
  2. The Validation process
&lt;/h1&gt;

&lt;p&gt;Now, our rails app should verify this callback, in ruby it's easy&lt;br&gt;
For this example I will use a simple verification.&lt;/p&gt;

&lt;p&gt;I will only verify if decrypted token is the real token of the product.&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;# in the product controller&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;confirm&lt;/span&gt;
    &lt;span class="vi"&gt;@product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_product&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;PaymentService&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;product: &lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;secret: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:secret&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="n"&gt;flash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:success&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Payment succeed"&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;flash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Oups Error !"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;product_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# inside the PaymentService&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@crypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decrypt_and_verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;token&lt;/span&gt;
      &lt;span class="c1"&gt;# handle the success of the payment&lt;/span&gt;
      &lt;span class="c1"&gt;# notifications, emails ...ect&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;false&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;h1&gt;
  
  
  The Front-end
&lt;/h1&gt;

&lt;p&gt;Thanks to &lt;code&gt;Stripe&lt;/code&gt; the front-end is easy !&lt;br&gt;
So, when the customer want to pay a product, he will click on the &lt;code&gt;payment button&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9vOC_f2c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/wonb06fcg9r94l8bc1q2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9vOC_f2c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/wonb06fcg9r94l8bc1q2.gif" alt="Alt Text" width="525" height="59"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will call our &lt;code&gt;rails app&lt;/code&gt; to prepare the session, get back the redirection and follow it.&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= button_to product_pay_path(@product),
     method: :post,
     id: "pay_sca",
     data: { disable_with: '&amp;lt;i class=&lt;/span&gt;&lt;span class="s2"&gt;"fa fa-spinner fa-spin"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/i&amp;gt;'.html_safe },
     class: "btn btn-success btn-lg btn-block",
     remote: true do  %&amp;gt;
      &amp;lt;i style="float:left;color:white" id="notes-exists" class="fas fa-lock"&amp;gt;&amp;lt;/i&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="no"&gt;Payer&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;% end &lt;/span&gt;&lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;man that's concise !, this will call our controller by ajax, show a cool animation when waiting for the response of the server and finaly follow the redirection to &lt;code&gt;Stripe&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="c1"&gt;# controller&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="vi"&gt;@company_travel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_travel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;company&lt;/span&gt;
    &lt;span class="vi"&gt;@session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;PaymentService&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;product: &lt;/span&gt;&lt;span class="n"&gt;current_product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&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 have maybe notice, we are using a CRUD resource, when a customer need to pay we &lt;code&gt;create&lt;/code&gt; a &lt;code&gt;charging resource&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The view part is simple also&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;# views/.../create.js.erb&lt;/span&gt;

&lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="n"&gt;stripe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Stripe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;%= Rails.application.secrets.stripe_public_key %&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;span class="n"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirectToCheckout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="no"&gt;We&lt;/span&gt; &lt;span class="n"&gt;put&lt;/span&gt; &lt;span class="n"&gt;here&lt;/span&gt; &lt;span class="n"&gt;our&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="no"&gt;ID&lt;/span&gt;
  &lt;span class="ss"&gt;sessionId: &lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;%= @session_id %&amp;gt;'&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="no"&gt;If&lt;/span&gt; &lt;span class="sb"&gt;`redirectToCheckout`&lt;/span&gt; &lt;span class="n"&gt;fails&lt;/span&gt; &lt;span class="n"&gt;due&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="n"&gt;or&lt;/span&gt; &lt;span class="n"&gt;network&lt;/span&gt;
  &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;display&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;localized&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;
  &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="n"&gt;using&lt;/span&gt; &lt;span class="sb"&gt;`result.error.message`&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Easy yay !&lt;/p&gt;

&lt;p&gt;I think now we are good to go.&lt;/p&gt;

&lt;p&gt;Testing this will be in an other blog post with &lt;code&gt;system tests&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As a gift, The complete service can look 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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentService&lt;/span&gt;
  &lt;span class="kp"&gt;include&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;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;url_helpers&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="vi"&gt;@product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;

    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&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;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;secret_key_base&lt;/span&gt;
    &lt;span class="vi"&gt;@cryptor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MessageEncryptor&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="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;cipher: &lt;/span&gt;&lt;span class="s1"&gt;'aes-256-gcm'&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;create_session&lt;/span&gt;
    &lt;span class="n"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gen_secret&lt;/span&gt;

    &lt;span class="no"&gt;Stripe&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Checkout&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Session&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="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;payment_method_types: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'card'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;line_items: &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
                       &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
                       &lt;span class="ss"&gt;description: &lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="ss"&gt;amount: &lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="ss"&gt;currency: &lt;/span&gt;&lt;span class="s1"&gt;'eur'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="ss"&gt;quantity: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="ss"&gt;success_url: &lt;/span&gt;&lt;span class="n"&gt;confirm_product_payment_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;secret: &lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="ss"&gt;cancel_url: &lt;/span&gt;&lt;span class="n"&gt;cancel_product_payment_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt; &lt;span class="p"&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;def&lt;/span&gt; &lt;span class="nf"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@cryptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decrypt_and_verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;token&lt;/span&gt;
      &lt;span class="c1"&gt;# handle a succeed payment&lt;/span&gt;
      &lt;span class="c1"&gt;# send notifications, invoices ... ect&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&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;gen_secret&lt;/span&gt;
    &lt;span class="vi"&gt;@cryptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encrypt_and_sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;token&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;



</description>
      <category>rails</category>
      <category>ruby</category>
      <category>stripe</category>
      <category>sca</category>
    </item>
    <item>
      <title>A little of ELISP to improve your RAILS testing</title>
      <dc:creator>CHADDA Chakib</dc:creator>
      <pubDate>Sat, 09 Feb 2019 19:25:09 +0000</pubDate>
      <link>https://dev.to/zimski/a-little-of-elisp-to-improve-your-rails-testing-5fma</link>
      <guid>https://dev.to/zimski/a-little-of-elisp-to-improve-your-rails-testing-5fma</guid>
      <description>&lt;h2&gt;
  
  
  Why spacemacs
&lt;/h2&gt;

&lt;p&gt;I am a big big fan of &lt;strong&gt;vim&lt;/strong&gt;, but I struggled with it when I was coding with multiple project in the same time.&lt;br&gt;
The big pain was also when I need to see the diffs before committing.&lt;/p&gt;

&lt;p&gt;So I moved to spacemacs (vim mode) several years ago and I am loving it.&lt;br&gt;
I still use vim to edit some config files but the main code in on spacemacs.&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing rails inside spacemacs sucks
&lt;/h2&gt;

&lt;p&gt;Now, I am coding a lot of ruby mainly for rails projects and testing function included in the official &lt;code&gt;ruby layer&lt;/code&gt; are not adapted for a modern rails test with &lt;code&gt;minitest&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;rails&lt;/code&gt; now, when we call &lt;code&gt;bundle exec rails test&lt;/code&gt; the code will try to find the &lt;code&gt;spring&lt;/code&gt; process and call the tests inside.&lt;br&gt;
The &lt;code&gt;spring&lt;/code&gt; process improve a lot the boot-up time of ruby and the loading of all your rails project by caching it.&lt;/p&gt;

&lt;p&gt;In spacemacs, your cursor is within test method and you hit &lt;code&gt;space m t t&lt;/code&gt;, this will run only this test but he is calling your test by using &lt;code&gt;ruby ..... [your test file]&lt;/code&gt; and this will not leverage all the optimization done in the rake test command shipped in rails.&lt;/p&gt;

&lt;p&gt;More dangerous, this will flush your &lt;code&gt;dev&lt;/code&gt; database and populate it with your test fixture.&lt;/p&gt;

&lt;p&gt;bad bad .... very bad thing.&lt;/p&gt;
&lt;h2&gt;
  
  
  What I was doing before the help of elips
&lt;/h2&gt;

&lt;p&gt;I have a terminal somewhere open, and when I need to run test, I switch to it and try to type the name of the test and line number of the test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bundle exec rails test test/system/ ... / ... / ... / my_test_file_test.rb:123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Elisp, please help me !
&lt;/h2&gt;

&lt;p&gt;I have written a small functions in &lt;code&gt;Elisp&lt;/code&gt; because I want learn &amp;amp; practice this powerful language.&lt;/p&gt;

&lt;p&gt;Here is the code, can be improved but who cares, it's doing the job :D&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;run-rails-test-file&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;interactive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ruby-test-find-file&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;compile&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt; &lt;span class="s"&gt;"bundle exec rails test %s"&lt;/span&gt; &lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;run-rails-test-at-point&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;interactive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let*&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ruby-test-find-file&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
           &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;buffername&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;get-file-buffer&lt;/span&gt; &lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;with-current-buffer&lt;/span&gt; &lt;span class="nv"&gt;buffername&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;line-number-at-pos&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;point&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;compile&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt; &lt;span class="s"&gt;"bundle exec rails test %s:%s"&lt;/span&gt; &lt;span class="nv"&gt;filename&lt;/span&gt; &lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;spacemacs/set-leader-keys&lt;/span&gt; &lt;span class="s"&gt;"mtt"&lt;/span&gt; &lt;span class="ss"&gt;'run-rails-test-at-point&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;spacemacs/set-leader-keys&lt;/span&gt; &lt;span class="s"&gt;"mtT"&lt;/span&gt; &lt;span class="ss"&gt;'run-rails-test-file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Let's explain this code
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;(interactive)&lt;/code&gt;: make your function callable with &lt;code&gt;space space&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fkcocx45m3lkbq2zp1ji5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fkcocx45m3lkbq2zp1ji5.png" alt="emacs call function"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ruby-test-find-file&lt;/code&gt;: this function will output the path of the current buffer if it's a test file and .... wait for it .... it will also return the last visited test file if you are in a none-test file.&lt;/p&gt;

&lt;p&gt;This is very useful, imagine your current buffer is a test file, you run the test, it fails, so you switch to the actual file containing the code, you tweak it and you want to re-run the test, &lt;strong&gt;no need to switch again to the test file&lt;/strong&gt;, this function will handle it for you.&lt;/p&gt;

&lt;p&gt;I am reusing the functions defined in the &lt;code&gt;spacemacs ruby layer&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand the code by yourself
&lt;/h2&gt;

&lt;p&gt;In Emacs, Everything is a function, and you can read the actual code of all of them.&lt;br&gt;
In spacemacs, the &lt;strong&gt;BEST&lt;/strong&gt; helper is &lt;code&gt;Space h d f&lt;/code&gt; and it will autocomplete the name of your function and show you the doc/code.&lt;/p&gt;

&lt;p&gt;Let's try to understand what means &lt;code&gt;with-current-buffer&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;type &lt;code&gt;SPACE h d f&lt;/code&gt; and start type the name of the function.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Faowq9oo688wfpi4txxek.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Faowq9oo688wfpi4txxek.png" alt="search for function"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oh look to this this cutie, hit &lt;code&gt;enter&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fjz0zxg7ktjyf2jacke2z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fjz0zxg7ktjyf2jacke2z.png" alt="the doc of the function"&gt;&lt;/a&gt;&lt;br&gt;
Everything is documented !&lt;/p&gt;

&lt;p&gt;Let's go to the code now, move your cursor on the filename and hit Enter&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fpyy2n44nr0aoh1200ivw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fpyy2n44nr0aoh1200ivw.png" alt="the code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And we can recursively search and learn more things at each key's hit.&lt;/p&gt;

&lt;p&gt;Hope you have learned something new and maybe this will help you with your rails workflow on this incredible piece of software Emacs !&lt;/p&gt;

</description>
      <category>rails</category>
      <category>emacs</category>
      <category>spacemacs</category>
      <category>elisp</category>
    </item>
    <item>
      <title>The complete guide to setup a CI/CD for Rails 6+ on Gitlab</title>
      <dc:creator>CHADDA Chakib</dc:creator>
      <pubDate>Sun, 19 Aug 2018 14:17:01 +0000</pubDate>
      <link>https://dev.to/zimski/the-complete-guide-to-setup-a-cicd-for-rails-5-on-gitlab-2f2d</link>
      <guid>https://dev.to/zimski/the-complete-guide-to-setup-a-cicd-for-rails-5-on-gitlab-2f2d</guid>
      <description>&lt;h1&gt;
  
  
  Continuous Integration/Deployment for Rails on Gitlab
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.heyzimo.com%2Fassets%2Fimages%2Fpipline_green.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.heyzimo.com%2Fassets%2Fimages%2Fpipline_green.png" alt="Gitlab piplines"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this blog post, we will through the necessary steps to setup Gitlab in order&lt;br&gt;
to run Rails build, tests &amp;amp; deployment if everything will be okay.&lt;br&gt;
I will put a particular attention on &lt;code&gt;rails system test&lt;/code&gt; and how to make them works.&lt;/p&gt;

&lt;p&gt;We will use Heroku to deploy our staging App.&lt;/p&gt;
&lt;h1&gt;
  
  
  What will we achieve ?
&lt;/h1&gt;
&lt;h2&gt;
  
  
  The build Stage
&lt;/h2&gt;

&lt;p&gt;The build will contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installation of dependencies&lt;/li&gt;
&lt;li&gt;Database setup&lt;/li&gt;
&lt;li&gt;Precompile of assets (assets &amp;amp; webpacker)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Test Stage
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The Integration tests
&lt;/h3&gt;

&lt;p&gt;In this stage, we will run all our integration tests, which basically turn to&lt;br&gt;
run:&lt;br&gt;
&lt;code&gt;bundle exec rails test&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The system tests
&lt;/h3&gt;

&lt;p&gt;This is the most exciting and important part to have in our CI.&lt;br&gt;
The system tests are very useful in term of testing complex UI requiring a massive use&lt;br&gt;
of Javascript (React of Vue app) and interacting with external services like &lt;code&gt;Google&lt;br&gt;
Map Places&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The system test will mimic a regular user by clicking and filling inputs like a regular user on our App.&lt;br&gt;
The main command executed in this stage is &lt;code&gt;bundle exec rails test:system&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The interacting fact in this case is the use of container to embed the &lt;code&gt;Selenium&lt;br&gt;
Chrome browser&lt;/code&gt; to run real browser to fetch and tests our frontend.&lt;/p&gt;
&lt;h2&gt;
  
  
  The deploy Stage
&lt;/h2&gt;

&lt;p&gt;This is an easy step, we will deploy our application to the staging environment.&lt;/p&gt;


&lt;h1&gt;
  
  
  The GITLAB-CI
&lt;/h1&gt;

&lt;p&gt;Gitlab offer to everyone ( and we should be grateful to them for all the work&lt;br&gt;
that have be done) a recipe that define how the code will be tested / deployed&lt;br&gt;
and all the services needed for these tasks.&lt;br&gt;
All the instructions are stored in &lt;code&gt;.gitlab-ci&lt;/code&gt; present in the root of our repo.&lt;/p&gt;

&lt;p&gt;This offer us a &lt;em&gt;centralized&lt;/em&gt; and an &lt;em&gt;easy way&lt;/em&gt; to manage our source code and&lt;br&gt;
our continues integration for &lt;code&gt;FREE&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  How does it works
&lt;/h2&gt;

&lt;p&gt;The CI follow these simple steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Booting one or several containers aka &lt;code&gt;services&lt;/code&gt; that you have specified in the &lt;code&gt;.gitlab-ci&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Copy your repo in the main container.&lt;/li&gt;
&lt;li&gt;Run all scripts wanted in it&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Use the cache to speedup the CI
&lt;/h2&gt;

&lt;p&gt;Gitlab allows us to cache folders and files and use them for the next jobs.&lt;br&gt;
No need to recompile all dependencies or even download them.&lt;br&gt;
In our case, caching all the &lt;code&gt;gems&lt;/code&gt; and &lt;code&gt;node_modules&lt;/code&gt; will save us several minutes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Use artifacts to debug our tests
&lt;/h2&gt;

&lt;p&gt;When the system tests fails, the test will save the &lt;code&gt;screenshots&lt;/code&gt; in a &lt;code&gt;temp&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;artifacts&lt;/code&gt; make possible for us to save those files and tie them to the job.&lt;/p&gt;

&lt;p&gt;This will help us a lot when we want to debug a failing system tests.&lt;/p&gt;


&lt;h1&gt;
  
  
  Let's do it
&lt;/h1&gt;
&lt;h2&gt;
  
  
  1. The build
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Prepare the build container
&lt;/h3&gt;

&lt;p&gt;The build will be executed in a container, so we should have a container with&lt;br&gt;
all the dependencies needed bundled inside.&lt;/p&gt;

&lt;p&gt;For the modern rails app we should include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ruby&lt;/li&gt;
&lt;li&gt;Node + Yarn&lt;/li&gt;
&lt;li&gt;Some system libraries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the &lt;code&gt;dockerfile&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;FROM ruby:2.4.3

RUN curl &lt;span class="nt"&gt;-sS&lt;/span&gt; https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN curl &lt;span class="nt"&gt;-sL&lt;/span&gt; https://deb.nodesource.com/setup_8.x | bash -
RUN &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb https://dl.yarnpkg.com/debian/ stable main"&lt;/span&gt; | &lt;span class="nb"&gt;tee&lt;/span&gt; /etc/apt/sources.list.d/yarn.list
RUN apt-get update &lt;span class="nt"&gt;-qqy&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt;  &lt;span class="nt"&gt;-qqyy&lt;/span&gt; yarn nodejs postgresql postgresql-contrib libpq-dev cmake

RUN &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Easy yay !&lt;/p&gt;

&lt;p&gt;Build the container&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nb"&gt;.&lt;/span&gt;
Sending build context to Docker daemon  2.048kB
Step 1/6 : FROM ruby:2.6.5
2.6.5: Pulling from library/ruby
16ea0e8c8879: Pull &lt;span class="nb"&gt;complete
&lt;/span&gt;50024b0106d5: Pull &lt;span class="nb"&gt;complete
&lt;/span&gt;ff95660c6937: Pull &lt;span class="nb"&gt;complete
&lt;/span&gt;9c7d0e5c0bc2: Pull &lt;span class="nb"&gt;complete
&lt;/span&gt;29c4fb388fdf: Pull &lt;span class="nb"&gt;complete
&lt;/span&gt;069ad1aadbe0: Pull &lt;span class="nb"&gt;complete
&lt;/span&gt;e7188792d9dd: Pull &lt;span class="nb"&gt;complete
&lt;/span&gt;bae7e74440d1: Pull &lt;span class="nb"&gt;complete
&lt;/span&gt;Digest: sha256:2285f291f222e1b53d22449cc52bad2112f519bcce60248ea1c4d5e8f14c7c04
Status: Downloaded newer image &lt;span class="k"&gt;for &lt;/span&gt;ruby:2.6.5
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 2ff4e698f315
Step 2/6 : RUN curl &lt;span class="nt"&gt;-sS&lt;/span&gt; https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Running &lt;span class="k"&gt;in &lt;/span&gt;abb67e50af3e
Warning: apt-key output should not be parsed &lt;span class="o"&gt;(&lt;/span&gt;stdout is not a terminal&lt;span class="o"&gt;)&lt;/span&gt;
OK
Removing intermediate container abb67e50af3e
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 461e2dd2134d
Step 3/6 : RUN curl &lt;span class="nt"&gt;-sL&lt;/span&gt; https://deb.nodesource.com/setup_8.x | bash -
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Running &lt;span class="k"&gt;in &lt;/span&gt;414f508a391c

&lt;span class="c"&gt;## Installing the NodeSource Node.js 8.x LTS Carbon repo...&lt;/span&gt;

.....
Processing triggers &lt;span class="k"&gt;for &lt;/span&gt;libc-bin &lt;span class="o"&gt;(&lt;/span&gt;2.28-10&lt;span class="o"&gt;)&lt;/span&gt; ...
Removing intermediate container af1183021a8d
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 603cab5f6952
Step 6/6 : RUN &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Running &lt;span class="k"&gt;in &lt;/span&gt;53c5950a25c1
Removing intermediate container 53c5950a25c1
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 42b50699301e
Successfully built 42b50699301e

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

&lt;/div&gt;



&lt;p&gt;Tag it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker tag 42b50699301e registry.gitlab.com/[ORG]/[REPO]/[CONTAINER]:v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we should publish this container to enable GitlabCI to use it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Gitlab&lt;/code&gt; provide for us a container registry ! for free again !&lt;/p&gt;

&lt;p&gt;So we just need to push this container in the project registry.&lt;/p&gt;

&lt;p&gt;First, you should login to gitlab registry&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker login registry.gitlab.com
&lt;span class="c"&gt;# use your gitlab credential&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and PUSH&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker push registry.gitlab.com/[ORG]/[REPO]/[CONTAINER]:v1 &lt;span class="c"&gt;# v1 is my version tag&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have &lt;code&gt;ADSL&lt;/code&gt; internet connection with a poor uploading speed, you can go&lt;br&gt;
take a nap ;)&lt;/p&gt;

&lt;p&gt;Once the push finishes, we are good to go to the next step.&lt;/p&gt;
&lt;h3&gt;
  
  
  The build script
&lt;/h3&gt;

&lt;p&gt;This is the main build part in the gitlab-ci file&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;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;registry.gitlab.com/[ORG]/[REPO]/[CONTAINER]:v1"&lt;/span&gt;

&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;LC_ALL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;C.UTF-8&lt;/span&gt;
  &lt;span class="na"&gt;LANG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en_US.UTF-8&lt;/span&gt;
  &lt;span class="na"&gt;LANGUAGE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en_US.UTF-8&lt;/span&gt;
  &lt;span class="na"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test"&lt;/span&gt;
  &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test_db&lt;/span&gt;
  &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;runner&lt;/span&gt;
  &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# cache gems and node_modules for next usage&lt;/span&gt;
&lt;span class="na"&gt;.default-cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;default-cache&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;untracked&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-project-key-5.2&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node_modules/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;vendor/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;public/&lt;/span&gt;

&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default-cache&lt;/span&gt;
  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres:latest&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ruby -v&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node -v&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;yarn --version&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;which ruby&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gem install bundler  --no-ri --no-rdoc&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle install  --jobs $(nproc) "${FLAGS[@]}" --path=vendor&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;yarn install&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cp config/database.gitlab config/database.yml&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;RAILS_ENV=test bundle exec rake db:create db:schema:load&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;RAILS_ENV=test bundle exec rails assets:precompile&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we are using the previously created image to host the build.&lt;/p&gt;

&lt;p&gt;We should add to the project a &lt;code&gt;config/database.gitlab&lt;/code&gt; to replace the original&lt;br&gt;
database config and use custom host and credential to connect to postgres&lt;br&gt;
container booted by the GitlabCI.&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gitlab when reading this line will bootup a database container (postgress) and&lt;br&gt;
will use the variables defined before to setup the database&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  POSTGRES_DB: test_db
  POSTGRES_USER: runner
  POSTGRES_PASSWORD: &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;config/database.gitlab&lt;/code&gt; will tell our rails app how to connect to the&lt;br&gt;
database, so before the app boots, the &lt;code&gt;database.yml&lt;/code&gt; will be replaced by the&lt;br&gt;
custom one.&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;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql&lt;/span&gt;
  &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unicode&lt;/span&gt;
  &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;runner&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test_db&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. The Integration Tests script
&lt;/h2&gt;

&lt;p&gt;No need more explanation for this&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;integration_test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default-cache&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres:latest&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis:alpine&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gem install bundler  --no-ri --no-rdoc&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle install  --jobs $(nproc) "${FLAGS[@]}" --path=vendor&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cp config/database.gitlab config/database.yml&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle install --jobs $(nproc) "${FLAGS[@]}" --path=vendor&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;RAILS_ENV=test bundle exec rake db:create db:schema:load&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;RAILS_ENV=test bundle exec rails assets:precompile&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle exec rake test&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. The System Tests script
&lt;/h2&gt;

&lt;p&gt;The infrastructure to make possible the system test is quite interesting.&lt;/p&gt;

&lt;p&gt;To run the test we should start a browser (in a container) and fetch the page&lt;br&gt;
from the rails server (from an other container).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.heyzimo.com%2F%2Fassets%2Fimages%2Fsystem_tests.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.heyzimo.com%2F%2Fassets%2Fimages%2Fsystem_tests.png" alt="System tests &amp;amp; containers"&gt;&lt;/a&gt;&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;system_test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default-cache&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres:latest&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis:alpine&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;selenium/standalone-chrome:latest&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gem install bundler  --no-ri --no-rdoc&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle install  --jobs $(nproc) "${FLAGS[@]}" --path=vendor&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cp config/database.gitlab config/database.yml&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export selenium_remote_url="http://selenium__standalone-chrome:4444/wd/hub/"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle install  --jobs $(nproc) "${FLAGS[@]}" --path=vendor&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;RAILS_ENV=test bundle exec rake db:create db:schema:load&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;RAILS_ENV=test bundle exec rails assets:precompile&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle exec rake test:system&lt;/span&gt;
  &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;on_failure&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tmp/screenshots/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should tell to capybara to use the right &lt;code&gt;IP&lt;/code&gt; instead of &lt;code&gt;localhost&lt;/code&gt;, because here we have the browser&lt;br&gt;
and the server in two different containers.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;environment/test.rb&lt;/code&gt;, add these lines&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;net&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ip_address_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detect&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ipv4_private?&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'localhost'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ip_address&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_mailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_url_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:host&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;domain&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8200&lt;/span&gt;
  &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and we should tell the system test where to find the &lt;code&gt;chrome driver&lt;/code&gt; to control the browser, update &lt;code&gt;application_system_test_case.rb&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="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"test_helper"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"socket"&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;prepare_options&lt;/span&gt;
  &lt;span class="n"&gt;driver_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;desired_capabilities: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;chromeOptions: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;args: &lt;/span&gt;&lt;span class="sx"&gt;%w[headless disable-gpu disable-dev-shm-usage]&lt;/span&gt; &lt;span class="c1"&gt;# preserve memory &amp;amp; cpu consumption&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;driver_options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:url&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'selenium_remote_url'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'selenium_remote_url'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="n"&gt;driver_options&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationSystemTestCase&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SystemTestCase&lt;/span&gt;
  &lt;span class="n"&gt;driven_by&lt;/span&gt; &lt;span class="ss"&gt;:selenium&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;using: :chrome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;screen_size: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1400&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="ss"&gt;options: &lt;/span&gt;&lt;span class="n"&gt;prepare_options&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;rails system test&lt;/code&gt; will take screenshots and save them to &lt;code&gt;tmp/screenshots&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.heyzimo.com%2F%2Fassets%2Fimages%2Fscreenshots.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.heyzimo.com%2F%2Fassets%2Fimages%2Fscreenshots.png" alt="System tests &amp;amp; scrennshots"&gt;&lt;/a&gt;{:class="img-responsive"}&lt;/p&gt;

&lt;p&gt;As you can see, the screenshots are stored and attached to job, Neat!&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The Staging deployment
&lt;/h2&gt;

&lt;p&gt;This will deploy our code if &lt;code&gt;build&lt;/code&gt; and &lt;code&gt;tests&lt;/code&gt; stages succeed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;deploy_staging:
  stage: deploy
  variables:
    HEROKU_APP_NAME: YOUR_HEROKU_APP_NAME
  dependencies:
    - integration_test
    - system_test
  only:
    - master
  script:
    - gem install dpl
    - dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;HEROKU_API_KEY&lt;/code&gt; is stored in a safe place in the settings of the project&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.heyzimo.com%2F%2Fassets%2Fimages%2Fgitlab_variables.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.heyzimo.com%2F%2Fassets%2Fimages%2Fgitlab_variables.png" alt="Gitlab CI variables"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more information about this, go to &lt;a href="https://docs.gitlab.com/ee/ci/variables/" rel="noopener noreferrer"&gt;Gitlab variables documentation&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;Gitlab&lt;/code&gt; is an amazing project and provide a very nice spot where everything is&lt;br&gt;
well integrated, the coding experience is enhanced.&lt;/p&gt;

&lt;p&gt;Finally, let's hope that the migration to &lt;code&gt;Google compute engine&lt;/code&gt; will provide a&lt;br&gt;
better robustness to the project and less issues.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Longue vie à Gitlab !!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

&lt;p&gt;Here is the complete &lt;a href="https://gitlab.com/snippets/1745898" rel="noopener noreferrer"&gt;&lt;code&gt;Gitlab CI&lt;/code&gt; file&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gitlab</category>
      <category>rails</category>
      <category>test</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
