<?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: David Sánchez</title>
    <description>The latest articles on DEV Community by David Sánchez (@d4vsanchez).</description>
    <link>https://dev.to/d4vsanchez</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%2F246175%2F5461f5cb-9032-4c54-819b-dc45de642ea2.png</url>
      <title>DEV Community: David Sánchez</title>
      <link>https://dev.to/d4vsanchez</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/d4vsanchez"/>
    <language>en</language>
    <item>
      <title>Use 1Password SSH Agent in WSL</title>
      <dc:creator>David Sánchez</dc:creator>
      <pubDate>Sun, 06 Nov 2022 22:20:14 +0000</pubDate>
      <link>https://dev.to/d4vsanchez/use-1password-ssh-agent-in-wsl-2j6m</link>
      <guid>https://dev.to/d4vsanchez/use-1password-ssh-agent-in-wsl-2j6m</guid>
      <description>&lt;p&gt;TIL: You can use 1Password's SSH Agent in WSL, and it's not that complicated.&lt;/p&gt;

&lt;p&gt;I've been using Windows + WSL as my main driver for a few weeks, and one of the things I missed the most was the ease of setup of the 1Password's SSH agent and Linux or macOS.&lt;/p&gt;

&lt;p&gt;After searching for a while, I found one thread in the &lt;a href="https://1password.community/discussion/127210/windows-wsl-integration-possible-using-some-extra-tools" rel="noopener noreferrer"&gt;1Password Community forum&lt;/a&gt; that linked to &lt;a href="https://stuartleeks.com/posts/wsl-ssh-key-forward-to-windows/" rel="noopener noreferrer"&gt;a post&lt;/a&gt; containing the steps to forward the SSH Agent requests from WSL to the Windows' SSH agent.&lt;/p&gt;

&lt;p&gt;Amazingly, you don't have to do anything extra to make it work with 1Password SSH agent, and I'll write the steps I did to activate it below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enable 1Password SSH Agent
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;If you have not created or imported your SSH keys into 1Password, you can do it by following &lt;a href="https://developer.1password.com/docs/ssh/manage-keys" rel="noopener noreferrer"&gt;this article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To activate 1Password's SSH agent, you must have Windows Hello activated. You can learn more about Windows Hello in &lt;a href="https://support.microsoft.com/en-us/windows/learn-about-windows-hello-and-set-it-up-dae28983-8242-bb2a-d3d1-87c9d265a5f0" rel="noopener noreferrer"&gt;this article&lt;/a&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Open your 1Password settings and go to the “Developer” section. You need to check the “Use the SSH agent” checkbox.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0nn6myup3psc5s608zds.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0nn6myup3psc5s608zds.png" alt="1Password Settings window"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's all we need to do to activate the SSH agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Download &lt;code&gt;npiperelay&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;To communicate between WSL and the 1Password SSH agent, we'd need to use &lt;code&gt;npiperelay&lt;/code&gt;. This tool allows WSL to communicate with Windows' named pipes.&lt;/p&gt;

&lt;p&gt;To install it, we need to open the &lt;a href="https://github.com/jstarks/npiperelay" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; and download the latest release. At the time of writing this post, the latest release is &lt;a href="https://github.com/jstarks/npiperelay/releases/tag/v0.1.0" rel="noopener noreferrer"&gt;v0.1.0 from July 2, 2020&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Unzip it, and paste the &lt;code&gt;npiperelay.exe&lt;/code&gt; file in any folder that's configured in your system's PATH.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you don't know how to modify your system's PATH, you can learn how to do it in &lt;a href="https://www.wikihow.com/Change-the-PATH-Environment-Variable-on-Windows" rel="noopener noreferrer"&gt;this article&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Connect WSL with 1Password's SSH agent
&lt;/h2&gt;

&lt;p&gt;Now that we have the prerequisites fulfilled, we can connect our WSL distro with the SSH agent.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I'm using Ubuntu as my WSL distro, this may change if you're using another distro.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We need to install &lt;code&gt;socat&lt;/code&gt; which is a utility to transfer data between channels, this tool will use &lt;code&gt;npiperelay&lt;/code&gt; to then communicate with the named pipes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;socat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your WSL terminal, create a new folder named &lt;code&gt;.1password&lt;/code&gt; in your home directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.1password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new file named &lt;code&gt;.agent-bridge.sh&lt;/code&gt; in your home directory. You can name this file whatever you want, this naming was just a personal preference from me.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.agent-bridge.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chmod&lt;/span&gt; +x &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.agent-bridge.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And add the following content to the newly created file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Code extracted from https://stuartleeks.com/posts/wsl-ssh-key-forward-to-windows/ with minor modifications&lt;/span&gt;

&lt;span class="c"&gt;# Configure ssh forwarding&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SSH_AUTH_SOCK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.1password/agent.sock
&lt;span class="c"&gt;# need `ps -ww` to get non-truncated command for matching&lt;/span&gt;
&lt;span class="c"&gt;# use square brackets to generate a regex match for the process we want but that doesn't match the grep command running it!&lt;/span&gt;
&lt;span class="nv"&gt;ALREADY_RUNNING&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ps &lt;span class="nt"&gt;-auxww&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"[n]piperelay.exe -ei -s //./pipe/openssh-ssh-agent"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$ALREADY_RUNNING&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"0"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-S&lt;/span&gt; &lt;span class="nv"&gt;$SSH_AUTH_SOCK&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="c"&gt;# not expecting the socket to exist as the forwarding command isn't running (http://www.tldp.org/LDP/abs/html/fto.html)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"removing previous socket..."&lt;/span&gt;
        &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nv"&gt;$SSH_AUTH_SOCK&lt;/span&gt;
    &lt;span class="k"&gt;fi
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting SSH-Agent relay..."&lt;/span&gt;
    &lt;span class="c"&gt;# setsid to force new session to keep running&lt;/span&gt;
    &lt;span class="c"&gt;# set socat to listen on $SSH_AUTH_SOCK and forward to npiperelay which then forwards to openssh-ssh-agent on windows&lt;/span&gt;
    &lt;span class="o"&gt;(&lt;/span&gt;setsid socat UNIX-LISTEN:&lt;span class="nv"&gt;$SSH_AUTH_SOCK&lt;/span&gt;,fork EXEC:&lt;span class="s2"&gt;"npiperelay.exe -ei -s //./pipe/openssh-ssh-agent"&lt;/span&gt;,nofork &amp;amp;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;.bashrc&lt;/code&gt; (or your shell's configuration file if you don't use BASH), and add the following line at the end of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.agent-bridge.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reset your Windows Terminal, and you should be able to see your keys imported into 1Password when listing the keys added to the agent.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm2avbyng3z0j8qygc0d7.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm2avbyng3z0j8qygc0d7.png" alt="SSH Agent showing imported keys"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You won't have to repeat this process again, as long as you don't remove any configuration we created.&lt;/p&gt;

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

&lt;p&gt;I'm happy to have found a way to use my SSH keys stored in 1Password when using WSL, it eases the things a bit for me as I haven't used Windows in at least a decade. I'm still trying to make myself comfortable in this operating system, but I guess time will tell what happens.&lt;/p&gt;

&lt;p&gt;Nevertheless, WSL has been an amazing tool, and I'd like to thank everyone involved on it.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Convertir un array de objetos a un objeto usando TypeScript</title>
      <dc:creator>David Sánchez</dc:creator>
      <pubDate>Tue, 03 Aug 2021 12:27:03 +0000</pubDate>
      <link>https://dev.to/d4vsanchez/convertir-un-array-de-objetos-a-un-objeto-en-typescript-1fc2</link>
      <guid>https://dev.to/d4vsanchez/convertir-un-array-de-objetos-a-un-objeto-en-typescript-1fc2</guid>
      <description>&lt;p&gt;Tuve la necesidad de convertir un arreglo de objetos (&lt;code&gt;Array&amp;lt;{ id: string, name: string }&amp;gt;&lt;/code&gt;) en un sólo objeto donde la llave era el campo &lt;code&gt;id&lt;/code&gt; y el valor era el campo &lt;code&gt;name&lt;/code&gt;. Esto puede parecer muy sencillo al inicio, y lo es, pero a la hora de tipar correctamente el resultado en TypeScript me demoré un buen tiempo investigando hasta lograr encontrar la respuesta.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Advertencia:&lt;/strong&gt; Usaré el anglicismo "tipar" y "tipado" para referirme a la acción de crear los tipos de variables en TypeScript. Esta palabra no existe en Español.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Función sin tipar
&lt;/h2&gt;

&lt;p&gt;Queremos crear una función que haga la siguiente conversión:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="nf"&gt;arrayCollectionToObject&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;First&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;B&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Second&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Third&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// { A: 'First', B: 'Second', C: 'Third' }&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Comencemos con escribir la función que realizaría esta acción sin hacer uso alguno de tipos. La función se vería más o menos así:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;arrayCollectionToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&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;blockquote&gt;
&lt;p&gt;Nota: Para disminuir un poco la complejidad del código en el ejemplo he usado una &lt;a href="https://cosasdigitales.com/articulos-diseno-web/javascript-programacion-declarativa-vs-imperativa/" rel="noopener noreferrer"&gt;aproximación imperativa&lt;/a&gt; en lugar de usar una aproximación declarativa haciendo uso del &lt;a href="https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce" rel="noopener noreferrer"&gt;reduce&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Vamos a describir que hace la función línea a línea.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&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;En esta línea simplemente estamos creando un nuevo objeto, este será el objeto en el cual realizaremos las operaciones de conversión del array.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;Aquí estamos iterando uno a uno los elementos que están en el array, haciendo uso de la sentencia &lt;a href="https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Statements/for...of" rel="noopener noreferrer"&gt;for...of&lt;/a&gt;, y dentro del bloque &lt;code&gt;for&lt;/code&gt; estamos agregando al objeto &lt;code&gt;result&lt;/code&gt; una nueva llave que tendrá como valor lo que tenga &lt;code&gt;item.id&lt;/code&gt; y que tiene como valor lo que tenga &lt;code&gt;item.name&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Aquí devolvemos nuestro objeto &lt;code&gt;result&lt;/code&gt; después de que le agregamos las llaves y valores necesarios.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problema
&lt;/h2&gt;

&lt;p&gt;Nuestro código funciona correctamente. Si le enviamos un arreglo de objetos con la estructura esperada obtendremos como resultado un sólo objeto.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="nf"&gt;arrayCollectionToObject&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;First&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;B&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Second&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Third&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// { A: 'First', B: 'Second', C: 'Third' }&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Pero hay un problema en el tipado con TypeScript, el parámetro acepta cualquier tipo variable (&lt;code&gt;any&lt;/code&gt;) y el tipo de objeto retornado es simplemente un objeto vacío (&lt;code&gt;{}&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Si a nuestra función le pasamos un argumento cualquiera, este será aceptado, TypeScript no validará nada y podremos tener errores en tiempo de ejecución.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="nf"&gt;arrayCollectionToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// TypeError. Error en tiempo de ejecución 😭&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Si usamos un editor con auto-completado (como &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt;) no podremos aprovechar el auto-completado en el objeto devuelto por la función.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcxlirkhsthgs3i3z7zw.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcxlirkhsthgs3i3z7zw.png" alt="VSCode sin auto-completado"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Mejorando el tipado de nuestra función
&lt;/h2&gt;

&lt;p&gt;Tenemos como objetivo asegurar el tipo de dato que recibirá la función, permitiendo sólo colecciones de objetos que cumplan con la estructura esperada, y también debemos mejorar el tipado del objeto que devuelve la función.&lt;/p&gt;

&lt;h3&gt;
  
  
  Asegurando el parámetro
&lt;/h3&gt;

&lt;p&gt;Para asegurar el parámetro vamos a hacer uso de &lt;a href="https://www.typescriptlang.org/docs/handbook/2/generics.html" rel="noopener noreferrer"&gt;Generics&lt;/a&gt;. Los Generics son una utilidad que permiten generalizar los tipos, permiten capturar el tipo provisto por el usuario para poder utilizar esta información del tipo en un futuro.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;arrayCollectionToObject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
  &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Resto del código...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;En este pequeño cambio estamos realizando lo siguiente:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Estamos diciendo que vamos a recibir un valor con un tipo determinado de dato y a este tipo lo llamaremos &lt;code&gt;T&lt;/code&gt;. Lo único de lo que estamos seguros es que el tipo de dato que recibimos es un objeto y que tiene por lo menos las propiedades &lt;code&gt;id&lt;/code&gt; y &lt;code&gt;name&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;La propiedad &lt;code&gt;id&lt;/code&gt; tendrá otro Generic, a este tipo determinado de dato lo llamaremos &lt;code&gt;S&lt;/code&gt; y nos servirá más adelante para poder agregar correctamente el tipo del resultado.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Aquí estamos agregando otra restricción a nuestro Generic llamado &lt;code&gt;S&lt;/code&gt;. Estamos asegurándonos que el valor que tendrá este tipo será un sub-tipo de &lt;code&gt;string&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Con este pequeño cambio ya estamos seguros de que nuestra función sólo recibirá como argumento un valor que cumpla con la estructura que esperamos. En caso de no cumplir con la estructura esperada, obtendremos un error en tiempo de compilación.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="nf"&gt;arrayCollectionToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Error en tiempo de compilación 🥳&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Asegurando el objeto resultante
&lt;/h3&gt;

&lt;p&gt;En el paso anterior logramos asegurar el tipo del parámetro que se recibirá en la función y prevenir que se le pase como argumento cualquier tipo de valor. También podemos hacer que nuestra función nos provea un tipo más específico en el resultado que se obtiene al ejecutarla.&lt;/p&gt;

&lt;p&gt;El objetivo es que el tipo del objeto resultante tenga como nombre de las llaves el valor que tenía cada elemento del arreglo en la llave &lt;code&gt;id&lt;/code&gt;. Para lograr esto, sólo debemos hacer un cambio en la siguiente linea:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;arrayCollectionToObject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="c1"&gt;// Resto del código...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Esta linea lo que hace es que un tipo de objeto cuyas llaves serán iguales a cada uno de los valores de &lt;code&gt;id&lt;/code&gt; existentes en &lt;code&gt;T&lt;/code&gt; y su valor será un &lt;code&gt;string&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;¿Recuerdas que había un Generic llamado &lt;code&gt;S&lt;/code&gt; en la declaración de la función? Resulta que el Generic es usado para poder tener un &lt;strong&gt;String literal&lt;/strong&gt;, si no hubiésemos hecho esto, TypeScript nos hubiera tipado las llaves del objeto resultante como un &lt;code&gt;string&lt;/code&gt; y no con el valor exacto de cada &lt;code&gt;id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;De esta forma, ya podemos ver que el auto-completado de nuestro editor funciona correctamente.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnjy71a8g2t0424jna8js.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnjy71a8g2t0424jna8js.png" alt="VSCode con auto-completado"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Código final
&lt;/h2&gt;

&lt;p&gt;Después de agregar los tipos nuestro código debería haber quedado de la siguiente forma:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;arrayCollectionToObject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;&lt;br&gt;
  &lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;br&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;&lt;br&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;br&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;br&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Conclusión&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;No soy un experto en TypeScript y mi experiencia con el lenguaje es poca, pero lo poco que he conocido me ha demostrado que se pueden realizar cosas muy interesantes con su sistema de tipos. Realizar este pequeño ejemplo me ayudó a fortalecer bases sobre Generics, restricción de Generics, protección de tipos y mapeado de tipos.&lt;/p&gt;

&lt;p&gt;Es cierto que encontrar los tipos correctos en nuestro código a veces puede llevarnos muchísimo tiempo, encontrarlos para este ejercicio me llevó más tiempo del que hubiera deseado, pero se debe ver esto como una inversión a futuro. El tener nuestro código con un tipado correcto nos podrá asegurar muchísimas cosas a medida que el proyecto crece.&lt;/p&gt;




&lt;p&gt;Créditos a &lt;a href="https://unsplash.com/@afgprogrammer" rel="noopener noreferrer"&gt;Mohammad Rahmani&lt;/a&gt; por la &lt;a href="https://unsplash.com/photos/unwXUc_Pqi4" rel="noopener noreferrer"&gt;foto de portada&lt;/a&gt; del artículo.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>tutorial</category>
      <category>spanish</category>
      <category>javascript</category>
    </item>
    <item>
      <title>¿Qué hay de nuevo en Next.js?</title>
      <dc:creator>David Sánchez</dc:creator>
      <pubDate>Wed, 16 Jun 2021 13:22:12 +0000</pubDate>
      <link>https://dev.to/d4vsanchez/que-hay-de-nuevo-en-next-js-1a14</link>
      <guid>https://dev.to/d4vsanchez/que-hay-de-nuevo-en-next-js-1a14</guid>
      <description>&lt;h2&gt;
  
  
  Sobre la Next.js Conf
&lt;/h2&gt;

&lt;p&gt;Hoy, 15 de Junio de 2021, fue la Next.js Conf de 2021. Esta fue virtual y corta (duró 24 minutos), el enfoque de la conferencia estuvo dividido en 2 partes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js Live, una nueva plataforma de desarrollo y colaboración creada por Vercel.&lt;/li&gt;
&lt;li&gt;Next.js 11, la nueva versión del framework.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En la conferencia se hizo demasiado énfasis en cómo mejorar la experiencia de usuario (UX) a la vez que se mejora la experiencia de desarrollo (DX). Abajo explicaré las novedades que nos trae Next.js 11 y Next.js Live.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next.js 11
&lt;/h2&gt;

&lt;p&gt;En la nueva versión de Next.js, se llevó al siguiente nivel la conexión entre UX y DX, aunque también tuvo una participación especial las mejoras en el rendimiento del framework:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hubo una mejora del &lt;strong&gt;24%&lt;/strong&gt; en el tiempo de inicio. Este tiempo es desde que se crea una nueva sesión de desarrollo y se visualiza el resultado en pantalla.&lt;/li&gt;
&lt;li&gt;Hubo una mejora de &lt;strong&gt;40%&lt;/strong&gt; en el tiempo de procesamiento de cambios. Este tiempo es desde que se guarda un cambio en el archivo y se visualiza el cambio en la pantalla.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para obtener acceso a estas mejoras de performance sólo hay que mantener actualizado Next.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i next@latest react@latest react-dom@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora, demos una mirada a los nuevos componentes que se introdujeron para mejorar el DX y el UX:&lt;/p&gt;

&lt;h3&gt;
  
  
  Mejoras en el componente de imagen
&lt;/h3&gt;

&lt;p&gt;El &lt;a href="https://nextjs.org/docs/api-reference/next/image" rel="noopener noreferrer"&gt;componente de imagen&lt;/a&gt; provisto por Next.js &lt;code&gt;next/image&lt;/code&gt;, nos ayuda a optimizar las imágenes abstrayendo toda la complejidad que esto implica.&lt;/p&gt;

&lt;p&gt;Este componente se introdujo en Next.js 10, pero obtuvo unas nuevas funcionalidades en Next.js 11:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Si se importa una imagen por medio del keyword &lt;code&gt;import&lt;/code&gt; y esta se envía al prop &lt;code&gt;src&lt;/code&gt;, el componente calculará automáticamente el &lt;em&gt;width&lt;/em&gt; y el &lt;em&gt;height&lt;/em&gt; de la imagen. Tener estos atributos ayudará a reducir el &lt;strong&gt;Cumulative Layout Shift&lt;/strong&gt;, una de las medidas que usa Web Vitals.&lt;/li&gt;
&lt;li&gt;Se podrá agregar una imagen provisional de carga. Cuando el usuario tenga una conexión a Internet de baja calidad, se le mostrará una versión difuminada del resultado final mientras termina de cargar. Lo único que se debe hacer es agregar el prop &lt;code&gt;placeholder="blur"&lt;/code&gt; a la imagen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aquí un pequeño ejemplo de cómo funciona:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/image`
import tShirt from &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="kr"&gt;public&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;shirt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;

function Backpack() {
  return (
    &amp;lt;Image
      src={tShirt}
      placeholder="blur"
      alt="Picture of a T-Shirt with Vercel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="nx"&gt;logo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;
    /&amp;gt;
  )
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El resultado es similar a lo que se ve a continuación:&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnrgvljblj78jvtqw9yt6.gif" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnrgvljblj78jvtqw9yt6.gif" alt="Funcionamiento de Placeholder en imagen"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;(El video lo he tomado del post de &lt;a href="https://nextjs.org/blog/next-11#image-placeholders" rel="noopener noreferrer"&gt;Next.js 11&lt;/a&gt;)&lt;/p&gt;

&lt;h3&gt;
  
  
  Conformance
&lt;/h3&gt;

&lt;p&gt;Se agregó &lt;a href="https://web.dev/conformance/" rel="noopener noreferrer"&gt;Conformance&lt;/a&gt;, que es un conjunto de reglas creadas por Google para ahorrarle al desarrollador tener que memorizar reglas para mantener un buen performance y poder mantenerse al día con los cambios.&lt;/p&gt;

&lt;p&gt;Conformance es una especie de linter que le provee confianza al desarrollador al limitarlo a una serie de reglas. Durante el stream, mostraron el funcionamiento de un plugin de ESLint que permite encontrar scripts que bloquean el renderizado o que previene realizar recargas completas de la página al no usar &lt;code&gt;next/Link&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Podemos usar Conformance en nuestro proyecto con Next.js 11 al ejecutar &lt;code&gt;next lint&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Componente Script
&lt;/h3&gt;

&lt;p&gt;Al igual que en Next.js 10 se introdujo el componente &lt;code&gt;next/image&lt;/code&gt;, en esta nueva versión de Next.js se introduce un nuevo componente que permitirá abstraer las complejidades que conlleva optimizar la carga de scripts: &lt;code&gt;next/script&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Script&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Componente...&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Script&lt;/span&gt;
  &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/three@0.129.0/build/three.min.js"&lt;/span&gt;
  &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"afterInteractive"&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para poder hacer uso de estas optimizaciones, el componente &lt;code&gt;Script&lt;/code&gt; recibe un prop llamado &lt;code&gt;strategy&lt;/code&gt;, este recibirá 3 valores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;beforeInteractive&lt;/strong&gt;: Este es usado para scripts críticos que deben ser cargados y ejecutados antes de que la página tenga interactividad.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;afterInteractive&lt;/strong&gt;: Este es usado para scripts que deban ser ejecutados después de que la página tenga interactividad.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;lazyOnload&lt;/strong&gt;: Este es usado para scripts que puedan esperar a cargar en momentos de inactividad en la ejecución.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Webpack 5
&lt;/h3&gt;

&lt;p&gt;Ya desde &lt;a href="https://nextjs.org/blog/next-10-2#webpack-5" rel="noopener noreferrer"&gt;Next.js 10.2&lt;/a&gt; podíamos hacer que Webpack 5 fuera el bundler que se utilizara en nuestro proyecto. Lo podíamos activar por medio de un flag en nuestro archivo de configuración de Next.js (&lt;code&gt;next.config.js&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;La novedad es que ahora Webpack 5 es el bundler por defecto y no hay necesidad de agregar un flag para que se use esta versión.&lt;/p&gt;

&lt;h3&gt;
  
  
  Migración desde Create React App (CRA)
&lt;/h3&gt;

&lt;p&gt;Esta es una herramienta que está en estado experimental en la actualidad y no se habló de ella en la conferencia, aunque aparece en el post de presentación de Next.js 11.&lt;/p&gt;

&lt;p&gt;La idea con esta herramienta es poder migrar de forma automática proyectos que fueron creados usando Create React App a usar Next.js. Se puede usar ejecutando el siguiente comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @next/codemod cra-to-next
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;De nuevo, es experimental, así que es probable que aún tenga errores.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next.js Live
&lt;/h2&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F429qxeohk4ny589lp74w.jpg" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F429qxeohk4ny589lp74w.jpg" alt="Next.js Live"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(La imagen la he tomado del post de &lt;a href="https://nextjs.org/blog/next-11#nextjs-live-preview-release" rel="noopener noreferrer"&gt;Next.js 11&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Next.js Live fue con lo que empezó la conferencia y se llevó una buena cantidad de tiempo mostrando sus características.&lt;/p&gt;

&lt;p&gt;Next.js Live es un entorno de desarrollo y colaboración en tiempo real exclusivamente para proyectos en Next.js. Nos permite tener un editor (&lt;a href="https://microsoft.github.io/monaco-editor/" rel="noopener noreferrer"&gt;Monaco&lt;/a&gt;, el mismo que usa Visual Studio Code) en el navegador para modificar nuestro proyecto de Next.js a su vez que aplica los cambios en nuestro navegador y le envía los cambios incrementales a las otras personas que tengan el proyecto abierto.&lt;/p&gt;

&lt;p&gt;Los usuarios que están visualizando nuestro proyecto pueden dibujar encima del proyecto y escribir en él, esto es muy útil si se quiere dar atención a un detalle en específico y pedir o proveer feedback. Lo interesante con estos cursores es que no sólo funcionan obteniendo la posición del mouse en el plano (X, Y) sino que Next.js Live tiene más información a nivel de componentes que permite que la posición de los cursores se vea correctamente incluso en responsive.&lt;/p&gt;

&lt;p&gt;También vale aclarar que Next.js Live nos permite realizar cambios a nuestro proyecto sin problemas cuando no tenemos conexión a Internet.&lt;/p&gt;

&lt;p&gt;Este servicio aún está en Preview y para acceder a él, primero debemos registrarnos a una lista de espera. Al momento de escribir este artículo, no he obtenido acceso a esta plataforma. Si deseas registrar tu correo a la lista de espera puedes ir a &lt;a href="https://nextjs.org/live" rel="noopener noreferrer"&gt;https://nextjs.org/live&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusiones
&lt;/h2&gt;

&lt;p&gt;La conexión de la que tanto hablaron entre UX y DX al inicio de la conferencia la entregaron con creces en la nueva versión de Next.js, las nuevas características del componente de imagen y el nuevo componente de script reducen un montón la complejidad existente en optimizar las páginas.&lt;/p&gt;

&lt;p&gt;Estoy totalmente a favor de la aproximación que han tomado en Next.js para hacer de la optimización un asunto del framework, disminuyendo así la responsabilidad que recae en los desarrolladores en la implementación de la buenas prácticas y reduciendo esto sólo a la toma de decisiones.&lt;/p&gt;

&lt;p&gt;No puedo opinar mucho de Next.js Live, más que decir que estoy ansioso de poder obtener acceso al mismo ojalá en poco tiempo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fuentes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=ze8ycxc1UzE" rel="noopener noreferrer"&gt;Next.js Conf 2021&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/blog/next-11" rel="noopener noreferrer"&gt;Next.js 11&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si les gustó el post, les agradezco darle una reacción ❤️ y compartirlo en sus redes sociales.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>webdev</category>
      <category>spanish</category>
    </item>
    <item>
      <title>My Journey into Ruby: Modules</title>
      <dc:creator>David Sánchez</dc:creator>
      <pubDate>Mon, 05 Apr 2021 12:07:25 +0000</pubDate>
      <link>https://dev.to/d4vsanchez/my-journey-into-ruby-modules-8il</link>
      <guid>https://dev.to/d4vsanchez/my-journey-into-ruby-modules-8il</guid>
      <description>&lt;p&gt;Let's talk about modules in Ruby, how they differ to classes, why are they useful and how does the Ruby method lookup works.&lt;/p&gt;

&lt;p&gt;Modules in Ruby are similar to classes, they hold multiple methods. However, the modules cannot be instantiated as classes do, the modules do not have the method &lt;code&gt;new&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can think of modules as &lt;strong&gt;mix-ins&lt;/strong&gt;, they allow you to inject some code into a class. The mix-in exports the desired functionality to a child class, without creating an "is a" relationship. In Ruby, the main difference between inheriting from a class and mixing a module is that you can mix in more than one module.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.amazon.com/Well-Grounded-Rubyist-David-Black/dp/1617295213" rel="noopener noreferrer"&gt;Well-Grounded Rubyist book&lt;/a&gt; has a really good example about when a module may be needed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 When you're designing a program and you identify a behavior or set of behaviors that may be exhibited by more than one kind of entity or object, you've found a good candidate for a module.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's review the basics of module creation. We will use "stack-likeness" as our main example. We want to create a module that introduces stack-like behavior to any class.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ A &lt;a href="https://en.wikipedia.org/wiki/Stack_(abstract_data_type)" rel="noopener noreferrer"&gt;stack&lt;/a&gt; is a one dimensional data structure that follows a particular order for the operations to be performed on it: LIFO (Last In First Out), the last element to be added into the stack will be the first element to be queried on the stack.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Module Creation
&lt;/h2&gt;

&lt;p&gt;Writing a module follows almost the same syntax that we use to create classes, the only difference is that we need to start the definition using the &lt;code&gt;module&lt;/code&gt; keyword instead of the &lt;code&gt;class&lt;/code&gt; keyword:&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;module&lt;/span&gt; &lt;span class="nn"&gt;Stacklike&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stack&lt;/span&gt;
        &lt;span class="vi"&gt;@items&lt;/span&gt; &lt;span class="o"&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;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&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;pop&lt;/span&gt;
        &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&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;What we did here is pretty simple. We're creating an instance variable called &lt;em&gt;&lt;a class="mentioned-user" href="https://dev.to/stack"&gt;@stack&lt;/a&gt;&lt;/em&gt; that is an array representing our stack (1D data structure we talked about above) and we then abstract the &lt;em&gt;push&lt;/em&gt; and &lt;em&gt;pop&lt;/em&gt; behaviors of the stack into the &lt;em&gt;push&lt;/em&gt; and &lt;em&gt;pop&lt;/em&gt; methods of the module.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mixing the Module Into a Class
&lt;/h2&gt;

&lt;p&gt;In order to mix the module into a class we are going to make use of one of three different keywords: &lt;code&gt;include&lt;/code&gt;, &lt;code&gt;prepend&lt;/code&gt; and &lt;code&gt;extend&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ For this example, I'm going to show how to use the &lt;code&gt;include&lt;/code&gt; keyword to add Stack-likeness into our class, to understand how the other keywords work we'll need to go through how the &lt;strong&gt;method lookup&lt;/strong&gt; works in Ruby and we'll do that later in this article.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&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;Stack&lt;/span&gt;
    &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Stacklike&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. That's all that is needed in order to mix our &lt;em&gt;Stacklike&lt;/em&gt; module into our &lt;em&gt;Stack&lt;/em&gt; class, and now it can behave like a stack.&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;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"First item"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Second item"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Third item"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Objects currently in the stack:"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;
&lt;span class="n"&gt;last_item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Removed from the stack:"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;last_item&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Objects currently in the stack:"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we execute this code, we'll see that the stack behavior is currently being applied to our &lt;em&gt;Stack&lt;/em&gt; class and the Stack-likeness behavior can be introduced to any other class that we want in our codebase, it is easily reusable.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Rubyists often use adjectives for module names in order to reinforce the notion that the module defines a behavior.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Method Lookup Path
&lt;/h2&gt;

&lt;p&gt;The method lookup path is a mechanism in which Ruby starts "bubbling up" in the "object chain" in order to find what object or module is the one that contains the method that we're currently calling. This is what's being used when we call &lt;code&gt;object.pop&lt;/code&gt; or &lt;code&gt;object.items&lt;/code&gt; in our past example, those methods are not explicitly defined in &lt;strong&gt;Stack&lt;/strong&gt;, but they're found in the method lookup path of the Class by including the &lt;strong&gt;Stacklike&lt;/strong&gt; module.&lt;/p&gt;

&lt;p&gt;Consider the following code:&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;module&lt;/span&gt; &lt;span class="nn"&gt;M&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;x&lt;/span&gt;
        &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Hey! I'm the module M"&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;class&lt;/span&gt; &lt;span class="nc"&gt;B&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;x&lt;/span&gt;
        &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Hey! I'm the class B"&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;class&lt;/span&gt; &lt;span class="nc"&gt;A&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;B&lt;/span&gt;
    &lt;span class="n"&gt;includes&lt;/span&gt; &lt;span class="no"&gt;M&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are creating a class (&lt;strong&gt;A&lt;/strong&gt;) that inherits from another class (&lt;strong&gt;B&lt;/strong&gt;) and also mixes the behavior that's provided by a module (&lt;strong&gt;M&lt;/strong&gt;). Now, we can instantiate the class A and call the method X, by following the Method Lookup Path, we'll get "&lt;em&gt;Hey I'm the module M&lt;/em&gt;" as the result.&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;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;x&lt;/span&gt; &lt;span class="c1"&gt;# Hey I'm the module M&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcthu43vvtyusrmvmu41e.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcthu43vvtyusrmvmu41e.png" alt="Include Method Lookup Path"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Class A object will try to find a method to execute based on the message that has been received (&lt;em&gt;x&lt;/em&gt;). If Ruby has looked up all the way in the object chain until it reaches out &lt;em&gt;Kernel&lt;/em&gt; or &lt;em&gt;BasicObject&lt;/em&gt; and still hasn't found it, then it won't be found.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️  The point of &lt;em&gt;BasicObject&lt;/em&gt; is to have as few instance methods as possible, so it doesn't provide much functionality. Most of the functionality or Ruby's fundamental methods is actually found in the &lt;em&gt;Kernel&lt;/em&gt; module, that are included in all objects that descend from &lt;em&gt;Object&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By looking at the diagram above, you'll note that the way the method &lt;em&gt;x&lt;/em&gt; is found in the example is by following the path: &lt;code&gt;[A, M, B, Object, Kernel, BasicObject]&lt;/code&gt; and as the method &lt;em&gt;x&lt;/em&gt; is found in &lt;em&gt;M&lt;/em&gt;, it will use that instead of using the one found in &lt;em&gt;B&lt;/em&gt;. You can use the &lt;code&gt;ancestors&lt;/code&gt; method to find this path in any object, it is provided by the &lt;em&gt;Kernel&lt;/em&gt; module.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ If two modules are included in the same class and they both contain a method with the same name, they're going to be searched in reverse order of inclusion. The last mixed-in module is searched first.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Prepend and Extend Keywords
&lt;/h2&gt;

&lt;p&gt;When mixing-in the first module into the class, I told that the &lt;code&gt;prepend&lt;/code&gt; and &lt;code&gt;extend&lt;/code&gt; keywords could be used to that as well. Let's see the differences between them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prepend
&lt;/h3&gt;

&lt;p&gt;It works almost the same as &lt;code&gt;include&lt;/code&gt;, the difference is that when you prepend a module to the class, the object will look for the method in the module first, before looking for it in the class.&lt;/p&gt;

&lt;p&gt;Consider this change in the code above:&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;A&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;B&lt;/span&gt;
    &lt;span class="n"&gt;prepend&lt;/span&gt; &lt;span class="no"&gt;M&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;x&lt;/span&gt;
        &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Hey! I'm the class A"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The method lookup path will change to &lt;code&gt;[M, A, B, Object, Kernel, BasicObject]&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzut1cwk5kzzkmt3y4qwn.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzut1cwk5kzzkmt3y4qwn.png" alt="Prepend Method Lookup Path"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Extend
&lt;/h3&gt;

&lt;p&gt;Both &lt;code&gt;include&lt;/code&gt; and &lt;code&gt;prepend&lt;/code&gt; will make the methods of the modules available as instance methods of the class. &lt;code&gt;extend&lt;/code&gt; works a bit different by making module's methods available as class methods instead. Extending an object doesn't add the module into the ancestor chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Super Method
&lt;/h2&gt;

&lt;p&gt;There's this keyword called &lt;code&gt;super&lt;/code&gt; that we can use inside the body of a method definition. What this keyword does is that it jumps to the next-highest definition of the current method in the method lookup path.&lt;/p&gt;

&lt;p&gt;Consider the following change in the example code:&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;module&lt;/span&gt; &lt;span class="nn"&gt;M&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;x&lt;/span&gt;
        &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Hey! I'm the module"&lt;/span&gt;
        &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"But I'm going to call the next higher-up method..."&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;
        &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Back in the module"&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;Calling the method x will result in something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;x&lt;/span&gt;

&lt;span class="c1"&gt;# Hey I'm the module&lt;/span&gt;
&lt;span class="c1"&gt;# But I'm going to call the next higher-up method&lt;/span&gt;
&lt;span class="c1"&gt;# Hey I'm the Class B&lt;/span&gt;
&lt;span class="c1"&gt;# Back in the module&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the &lt;code&gt;super&lt;/code&gt; keyword is going to call the method &lt;em&gt;x&lt;/em&gt; that is found in the Class B which is the next object that is available in the method lookup path in this example.&lt;/p&gt;

&lt;p&gt;It's also important to note that the &lt;code&gt;super&lt;/code&gt; keyword handles arguments in a different way as methods would do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When called with no argument list, it will automatically forward the arguments that were passed to the method from which it's called.&lt;/li&gt;
&lt;li&gt;When called with an empty argument list (&lt;code&gt;super()&lt;/code&gt;), it sends no arguments to the higher up method, even if there were arguments passed to the current method.&lt;/li&gt;
&lt;li&gt;When called with specific arguments (&lt;code&gt;super(1, 2, 3)&lt;/code&gt;), it sends exactly those arguments to the higher up method.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to Use Mix-Ins vs Inheritance
&lt;/h2&gt;

&lt;p&gt;Having both inheritance and modules means that you have a lot of choice, but having a lot of choice also means that you also must be very careful about the considerations both this approaches introduce.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As noted at the beginning of this post, &lt;em&gt;modules don't have instances&lt;/em&gt;. &lt;strong&gt;Entities&lt;/strong&gt; or &lt;strong&gt;things&lt;/strong&gt; are better modeled using &lt;em&gt;classes&lt;/em&gt;, while &lt;strong&gt;behaviors&lt;/strong&gt; or &lt;strong&gt;properties&lt;/strong&gt; are better encapsulated using &lt;em&gt;modules&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Classes&lt;/em&gt; can have a &lt;strong&gt;single superclass&lt;/strong&gt;, but can &lt;strong&gt;mix in as many&lt;/strong&gt; &lt;em&gt;modules&lt;/em&gt; &lt;strong&gt;as&lt;/strong&gt; &lt;strong&gt;needed&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may like to break everything into separate modules, because you think something that you write for one entity may be useful in another entities in the future. But the overmodularization also exists. You've got the tools, and is up to you to consider how to balance them.&lt;/p&gt;




&lt;p&gt;Hope you've liked this post, I learned a lot while studying modules and I tried to summarize it as much and as easy I could in this post.&lt;/p&gt;

&lt;p&gt;If you've got any feedback or question about this post, please add it as a comment. I'm sure we all could learn about this.&lt;/p&gt;

&lt;p&gt;Thanks for your time ❤️👋&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>tutorial</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Install MySQL using Docker</title>
      <dc:creator>David Sánchez</dc:creator>
      <pubDate>Sat, 27 Mar 2021 06:00:32 +0000</pubDate>
      <link>https://dev.to/d4vsanchez/install-mysql-using-docker-55fj</link>
      <guid>https://dev.to/d4vsanchez/install-mysql-using-docker-55fj</guid>
      <description>&lt;p&gt;I've been a &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; user for some years and I never get tired of the power, flexibility and organization that it brings to my personal machine. In this article I will show you how do I install MySQL in my computer using Docker in order to be able to use it while avoiding all the headaches that might come by installing the database locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Docker?
&lt;/h2&gt;

&lt;p&gt;Let's start with a short explanation about what is Docker. It is a technology that has become the standard when building and distributing applications. Docker's objective is to standardize the way to distribute software by packing the code and dependencies in artifacts called &lt;strong&gt;&lt;a href="https://www.docker.com/resources/what-container"&gt;containers&lt;/a&gt;&lt;/strong&gt;. A container will have a set of processes that executes in your machine, but are logically isolated from the rest of your system.&lt;/p&gt;

&lt;p&gt;Containers bring a lot of benefits such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility:&lt;/strong&gt; You can pack any type of application into a container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight:&lt;/strong&gt; All containers reuse the kernel between them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Portable:&lt;/strong&gt; They're designed to run the same way in any computer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low coupling:&lt;/strong&gt; They are contained and are not affected by different configuration or software that may be installed in the host OS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalable:&lt;/strong&gt; You can easily copy containers multiple times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safe:&lt;/strong&gt; A container may only access certain parts of the system that it has been granted access.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To know more about Docker you can read the official &lt;a href="https://docs.docker.com/get-started/overview/"&gt;Docker Overview article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also read how to install Docker on &lt;a href="https://docs.docker.com/docker-for-mac/install/"&gt;macOS&lt;/a&gt;, Linux (using &lt;a href="https://docs.docker.com/engine/install/ubuntu/"&gt;Ubuntu&lt;/a&gt; or &lt;a href="https://dev.to/d4vsanchez/install-docker-community-edition-in-linux-mint-2gl4"&gt;Linux Mint&lt;/a&gt;) and &lt;a href="https://docs.docker.com/docker-for-windows/install/"&gt;Windows&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is MySQL?
&lt;/h2&gt;

&lt;p&gt;Now, let's talk a bit about MySQL. It's a DBMS (Database Management System) that allows us to manage relational databases. It was created by Sun Microsystems and then absorbed by Oracle when they purchased Sun on 2010. It's a pretty popular DBMS in the community due to being Open Source and free to use for commercial purposes.&lt;/p&gt;

&lt;p&gt;You use DBMS, such as MySQL, in order to store and retrieve data for multiple purposes such as applications, websites, and services.&lt;/p&gt;

&lt;p&gt;To know more about MySQL you can read the official &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/what-is-mysql.html"&gt;What is MySQL? article&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Do I Use Docker for This?
&lt;/h2&gt;

&lt;p&gt;You might say that I'd be better if I just run a &lt;code&gt;[brew](https://brew.sh/)&lt;/code&gt; install (&lt;a href="https://twitter.com/d4vsanchez/status/1360663470727958533"&gt;I moved to macOS a while ago&lt;/a&gt;) and that's all, and while that may be true, I still find myself being a bit uncomfortable installing services that I may use sporadically. For this reason, I'm using Docker to keep my main OS free of services and configurations that may come to haunt me in the future.&lt;/p&gt;

&lt;p&gt;Another benefit I find in using Docker is that I can just upgrade or downgrade the version of the container with a simple command, just by modifying its tag.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Docker Volume
&lt;/h2&gt;

&lt;p&gt;We're installing a database in order to persist information, aren't we? So we need to find a way that our Docker containers are able to persist information in our host machine. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Docker containers doesn't persist information by default. They execute the processes and when you stop them, all the information that was written to the hard drive is gone.&lt;/p&gt;

&lt;p&gt;We will make use of Docker Volumes to persist our data through multiple executions of the containers. Docker Volumes are managed by Docker and bring us multiple advantages such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not dependent on the directory structure and the OS host machine.&lt;/li&gt;
&lt;li&gt;Easy to back up or migrate.&lt;/li&gt;
&lt;li&gt;Safely shared among multiple containers.&lt;/li&gt;
&lt;li&gt;Easy to manage.&lt;/li&gt;
&lt;li&gt;You can read more about this topic in the &lt;a href="https://docs.docker.com/storage/volumes/"&gt;Docker Volumes documentation&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In order to create a new Docker Volume it is pretty simple, we just have to execute the following command in our terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker volume create mysql-volume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! Docker has created our Volume that we'll be using to persist our data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Docker MySQL Container
&lt;/h2&gt;

&lt;p&gt;Now that we have our Docker Volume created, we can create the container that will contain the mysql binary, services and configuration.&lt;/p&gt;

&lt;p&gt;In order to do this we'll execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; mysql-db &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mysql &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mysql,target&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/mysql &lt;span class="nt"&gt;-p&lt;/span&gt; 3306:3306 &lt;span class="nt"&gt;-d&lt;/span&gt; mysql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm going to explain what every part of this command does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker run&lt;/code&gt; Creates a Docker Container based on a &lt;a href="https://docs.docker.com/get-started/overview/#docker-objects"&gt;Docker Image&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--name mysql-db&lt;/code&gt; This is the name of the container we're creating right now. Docker containers must have unique naming.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-e MYSQL_ROOT_PASSWORD=mysql&lt;/code&gt; Set an environment variable containing the password of the &lt;strong&gt;root&lt;/strong&gt; user. Please don't judge me for using this password, is just for educational purposes, but you &lt;strong&gt;should&lt;/strong&gt; change the password to something else more secure.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--mount source=mysql-volume,target=/var/lib/mysql&lt;/code&gt; This is where we told our container to use our newly created &lt;code&gt;mysql-volume&lt;/code&gt; Docker Volume to store information that's going to be written on &lt;code&gt;/var/lib/mysql&lt;/code&gt; which is the directory where MySQL persists the information.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-p 3306:3306&lt;/code&gt; We tell Docker to expose the &lt;code&gt;3306&lt;/code&gt; port of the container into the &lt;code&gt;3306&lt;/code&gt; port of our host OS in order to be able to access the MySQL service.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-d&lt;/code&gt; It just tells Docker to run the container in &lt;strong&gt;detached&lt;/strong&gt; mode or in background.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mysql&lt;/code&gt; The Docker Image that we're going to use as "blueprint" to create our container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it! After executing that line we may have MySQL running inside a Docker Container that's exposing the 3306 port into our system so we can do queries to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Installation
&lt;/h2&gt;

&lt;p&gt;We can check that our container is working correctly by connecting to the MySQL client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; mysql-db mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then entering the password we added as value of the &lt;code&gt;MYSQL_ROOT_PASSWORD&lt;/code&gt; environment variable.&lt;/p&gt;

&lt;p&gt;If you receive an output in console showing the Copyright notice and a &lt;code&gt;mysql&amp;gt;&lt;/code&gt; prompt, then it means that the container is running correctly and you can start using it now!&lt;/p&gt;

&lt;p&gt;That's all to create a Docker Container for MySQL, now you can use it for whatever you want. If you've got any question about this process, I'll be more than happy to help you so please leave it in the comments and expect an answer from me as soon as I have time to answer it.&lt;/p&gt;

&lt;p&gt;Hope you've liked the post and leave your reaction in the sidebar on the left (or in the bottom if you're in a small screen device) ❤️&lt;/p&gt;

</description>
      <category>mysql</category>
      <category>docker</category>
      <category>tutorial</category>
      <category>tooling</category>
    </item>
    <item>
      <title>My Journey into Rails: Beginnings</title>
      <dc:creator>David Sánchez</dc:creator>
      <pubDate>Mon, 15 Mar 2021 12:30:50 +0000</pubDate>
      <link>https://dev.to/d4vsanchez/my-journey-into-rails-beginnings-191i</link>
      <guid>https://dev.to/d4vsanchez/my-journey-into-rails-beginnings-191i</guid>
      <description>&lt;p&gt;I'm a software engineer whose focus is mainly in web development. Since I started coding professionally my backend experience has gravitated around Python, using Django, Flask, FastAPI, etc.&lt;/p&gt;

&lt;p&gt;I changed my employer a few months ago, and though I'm mainly a frontend developer, I'm also expected to write some backend code in order to fulfill the tickets I'm assigned to. But in the new company, we don't use Python at all, we use &lt;a href="https://www.ruby-lang.org/en/"&gt;Ruby&lt;/a&gt; and I've never used Ruby before.&lt;/p&gt;

&lt;p&gt;At the beginning, I thought that I could start reading Ruby code and since I had experience with Python it was going to make sense as I worked with it, but I was truly wrong with that. There were a bunch of syntax differences that wouldn't make sense to me at first by looking at the code and I had to search what they were supposed to do. That's clearly not a very productive way of doing things, isn't it? So that set me a challenge in order to learn Ruby &amp;amp; Ruby on Rails so I can be a bit more productive in the backend side of the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's get serious about learning Ruby
&lt;/h3&gt;

&lt;p&gt;I needed a way to learn about Ruby's syntax first so I can be more comfortable reading the code and understanding what it does. I used two resources to do this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; As I'm not 100% into backend in the new project I was able to do this while the project advances, I'm not sure this is the best approach if your time is a bit limited in the project you're currently working on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YDB__83U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jhvc63znmbqd28ta1ru2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YDB__83U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jhvc63znmbqd28ta1ru2.png" alt="Box receiving some papers with Ruby logos on them" width="400" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.amazon.com/Well-Grounded-Rubyist-David-Black/dp/1617295213/"&gt;The Well Grounded Rubyist (3rd edition)&lt;/a&gt;&lt;/strong&gt;: I love this book, it teaches every aspect of Ruby in a way that's pretty simple to understand and it has some examples and exercises that will help you review the concepts once you finish every chapter of the book.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.codecademy.com/learn/learn-ruby"&gt;Codecademy&lt;/a&gt;&lt;/strong&gt;: I haven't used this page for years, but it was clearly a great resource that was recommended to me by a friend. I used it after reading the book above and it helped me a lot to review even more the concepts, as you have to make some exercises in order to advance on it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After using both this resources, I was able to better understand the syntax of Ruby and navigate through the codebase of the project as everything now fits much better in my mind.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's different with Ruby compared to Python or Node?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ftvGUdbQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e6kh92q9ayhyzd4ynkir.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ftvGUdbQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e6kh92q9ayhyzd4ynkir.png" alt="A person looking at a screen" width="600" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parenthesis are optional&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First thing I found different between Ruby and Python or Node was the optional parenthesis to call a method (or pass messages into an object). This was of the first issues I met when I was first reading Ruby code as it was totally different from I was used to.&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;Greeter&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
        &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Hello &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;greeter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Greeter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="c1"&gt;# Create a new object of the Greeter class&lt;/span&gt;
&lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;greet&lt;/span&gt; &lt;span class="s2"&gt;"David"&lt;/span&gt; &lt;span class="c1"&gt;# prints "Hello David" in console&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Flexibility when naming methods&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ruby is very &lt;a href="https://docs.ruby-lang.org/en/2.0.0/syntax/methods_rdoc.html#label-Method+Names"&gt;flexible when it comes to naming methods&lt;/a&gt;. You will find a lot of "operators" that are just language conventions using method names to do certain actions. For example, the &lt;a href="https://ruby-doc.org/core-2.7.2/Array.html#method-i-3C-3C"&gt;"append" operator&lt;/a&gt; to add a new element into an array is just a method named &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="c1"&gt;# This will print 1 2 3 4 and 5 in console&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or the setters, that are created by appending an &lt;code&gt;=&lt;/code&gt; to the attribute name.&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;Customer&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initializer&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
        &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&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;name&lt;/span&gt; &lt;span class="c1"&gt;# This is the getter of the name attribute&lt;/span&gt;
        &lt;span class="vi"&gt;@name&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;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newName&lt;/span&gt; &lt;span class="c1"&gt;# This is the setter, look at the = in the name&lt;/span&gt;
        &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newName&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s2"&gt;"David"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Customer is &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"John"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Customer is &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  It always returns the last line
&lt;/h3&gt;

&lt;p&gt;This is something I loved when learning &lt;a href="https://doc.rust-lang.org/rust-by-example/fn.html"&gt;Rust&lt;/a&gt; and I'm glad something kinda similar is present in Ruby. Ruby will &lt;strong&gt;always&lt;/strong&gt; return the result of the last line of the expression, unless you set a &lt;strong&gt;return&lt;/strong&gt; keyword in a statement that comes before.&lt;/p&gt;

&lt;p&gt;In the example above, you can see that in the getter method I didn't set an explicit return keyword, but calling the &lt;code&gt;name&lt;/code&gt; method will anyways return the value of the &lt;code&gt;@name&lt;/code&gt; variable. That method works as if I did something as:&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;name&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="vi"&gt;@name&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Local variables vs Instance variables vs Class variables
&lt;/h3&gt;

&lt;p&gt;These kind of variables exists in all object oriented languages I've used until today, but the syntax to create them is really different to what I was used in Python.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Local variables&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These are the variables that are only available in a block and cannot be accessed outside that scope. This is the normal behavior that you find in variables that are created inside a method or that are passed as arguments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Instance variables&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These are variables that are available to all the methods of the instance. Each instance may have different values for these variables. You create this type of variable by prepending an &lt;code&gt;@&lt;/code&gt; character to the name of the variable, such as &lt;code&gt;@name&lt;/code&gt; in the example above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Class variables&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These are variables that are available in the class and all instances will have the same value, modifying it in a single instance means that &lt;strong&gt;all&lt;/strong&gt; instances will also have the new value updated. You create this type of variable by prepending the &lt;code&gt;@@&lt;/code&gt; characters to the name of the variable.&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;Customer&lt;/span&gt;
    &lt;span class="vc"&gt;@@types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Regular"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Loyal"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;types&lt;/span&gt;
        &lt;span class="vc"&gt;@@types&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;first_customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;first_customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;types&lt;/span&gt; &lt;span class="c1"&gt;# Regular, Loyal&lt;/span&gt;
&lt;span class="n"&gt;second_customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;second_customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;types&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"VIP"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;second_customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;types&lt;/span&gt; &lt;span class="c1"&gt;# Regular, Loyal &amp;amp; VIP&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;first_customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;types&lt;/span&gt; &lt;span class="c1"&gt;# Regular, Logal &amp;amp; VIP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Instance methods vs Class methods
&lt;/h3&gt;

&lt;p&gt;These type of methods also exists in all object oriented languages I've used until today, but the syntax really confused me a bit coming from a Python background.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Instance methods&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These are methods that are only accesible when you create a new instance of a class. In all the examples above I created instance methods, no big deal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Class methods&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These are methods that are accesible from the class itself and you don't need an instance of the class in order to execute them. They are created by prepending the &lt;code&gt;self.&lt;/code&gt; to the name of the method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Sample&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hello_class&lt;/span&gt;
        &lt;span class="s2"&gt;"Hello from the class method"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;Sample&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hello_class&lt;/span&gt; &lt;span class="c1"&gt;# Hello from the class method&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  And more things...
&lt;/h3&gt;

&lt;p&gt;There was some other topics that I'd like to cover here but they will make this post really long, I'll cover them later in this series in a more detailed way.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I like about Ruby?
&lt;/h2&gt;

&lt;p&gt;It looks like a very fun language to work with and I found its syntax to be elegant and easy to understand. It has a lot of useful built-in behaviors and frameworks such as Ruby on Rails makes it really easy to go from 0 to a MVP in short time.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I dislike about Ruby?
&lt;/h3&gt;

&lt;p&gt;This is not a Ruby per-se problem, but I found the Ruby on Rails learning curve to be a bit steep. There's a lot of things that in Python are totally explicit (following the "&lt;em&gt;Explicit is better than implicit&lt;/em&gt;" principle) and are not so "explicit" in Ruby, you'll need to search in multiple files in order to understand where they are actually declared and how they work.&lt;/p&gt;

&lt;h3&gt;
  
  
  That's it... for now.
&lt;/h3&gt;

&lt;p&gt;I've found Ruby to be a really fun to learn language and using it has been a total pleasure. I'm eager to learn new things about the language and Ruby on Rails in order to be a bit more productive in the backend side of the current project I'm currently working on and to achieve proficiency in this language.&lt;/p&gt;

&lt;p&gt;I love teaching others in order to better learn concepts, so I will try to make more of these posts in a tutorial-like style when I learn new concepts. If you liked this post, please be assured that I will publish new ones with other concepts or thoughts about this topic.&lt;/p&gt;




&lt;p&gt;I hope you liked this post.&lt;/p&gt;

&lt;p&gt;Please tell me in the comments if you want to have a post about a particular aspect of Ruby or if you have any comments or feedback that could help me improve this post, and please, don't forget to react to this post, that would make me really happy.&lt;/p&gt;

&lt;p&gt;Thank you for your time ❤️👋&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I made a small game in Arduino</title>
      <dc:creator>David Sánchez</dc:creator>
      <pubDate>Sat, 10 Oct 2020 23:20:36 +0000</pubDate>
      <link>https://dev.to/d4vsanchez/i-made-a-small-game-in-arduino-akp</link>
      <guid>https://dev.to/d4vsanchez/i-made-a-small-game-in-arduino-akp</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--j2G4sLPT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/rdjlevvla7enagwkuvlo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--j2G4sLPT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/rdjlevvla7enagwkuvlo.gif" alt="Final result GIF" width="268" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've always been very interested in electronics since I got an "Electronic for Kids" kit that my parents bought me more than 15 years ago, it felt like playing with LEGO but much more interesting. The thing is that as the years passed, I got more interested in software and somehow stopped doing electronics just for fun (I coursed some electronics courses in the university).&lt;/p&gt;

&lt;p&gt;With the pandemic and the lock-down, I have been recovering the interest of doing more side-projects in my free time (I don't have the guts to go out from home yet) and then I remembered that I had some electronic stuff and an Arduino in my shelves. With the stuff I found, I then started thinking about what project could I do that will help me learn new stuff in both software and hardware.&lt;/p&gt;

&lt;p&gt;After some minutes thinking about which project I could do with the stuff I had, I came up with this idea about doing a project like the game of the Chrome's Offline dinosaur where you have to jump over some obstacles. This will be possible with the 16 x 2 LCD screen I found, the Arduino, and the push button.&lt;/p&gt;

&lt;p&gt;You can check the code of the project with its corresponding breadboard view in this GitHub repository:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/d4vsanchez"&gt;
        d4vsanchez
      &lt;/a&gt; / &lt;a href="https://github.com/d4vsanchez/jump-arduino"&gt;
        jump-arduino
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A very simple game for Arduino.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
Jump&lt;/h1&gt;
&lt;p&gt;This is a very simple game for Arduino, you should avoid having the ball deflated by falling into the spikes.&lt;/p&gt;
&lt;h2&gt;
Getting started&lt;/h2&gt;
&lt;p&gt;To run this project you must have the following items:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.arduino.cc/en/Main/Software" rel="nofollow"&gt;Arduino UNO&lt;/a&gt; installed in your computer.&lt;/li&gt;
&lt;li&gt;An &lt;a href="https://store.arduino.cc/usa/arduino-uno-rev3" rel="nofollow"&gt;Arduino UNO compatible board&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A push button.&lt;/li&gt;
&lt;li&gt;An LCD screen.&lt;/li&gt;
&lt;li&gt;A 12KΩ resistor.&lt;/li&gt;
&lt;li&gt;A 220Ω resistor.&lt;/li&gt;
&lt;li&gt;Jumper wires.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Installation&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Clone the project.&lt;/li&gt;
&lt;li&gt;Open &lt;code&gt;jump.ino&lt;/code&gt; with Arduino IDE.&lt;/li&gt;
&lt;li&gt;Upload it to your Arduino UNO.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
Breadboard view&lt;/h2&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/886aa016e56877a74cabe33d956cdaec0180cd8aa41e5aeb139a02acef11153a/68747470733a2f2f7261772e6769746875622e636f6d2f64347673616e6368657a2f6a756d702d61726475696e6f2f6d61696e2f736368656d61746963732f736368656d617469632e6a7067"&gt;&lt;img src="https://camo.githubusercontent.com/886aa016e56877a74cabe33d956cdaec0180cd8aa41e5aeb139a02acef11153a/68747470733a2f2f7261772e6769746875622e636f6d2f64347673616e6368657a2f6a756d702d61726475696e6f2f6d61696e2f736368656d61746963732f736368656d617469632e6a7067" alt="Breadboard image"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
How does this work?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://twitter.com/d4vsanchez/status/1312879111346544641" rel="nofollow"&gt;I made a tweet about this&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://dev.to/d4vsanchez/i-made-a-small-game-in-arduino-akp" rel="nofollow"&gt;I also made an article with a bit of explanation about how it works&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
To Do list&lt;/h2&gt;
&lt;p&gt;The following items are missing to say this is v1 release:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I may have a bug somewhere because the LCD flickers strangely in the "Game Over" message.&lt;/li&gt;
&lt;li&gt;A better animation when the ball jumps.&lt;/li&gt;
&lt;li&gt;A blog entry explaining how I did it.&lt;/li&gt;
&lt;li&gt;Better screenshots to show…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/d4vsanchez/jump-arduino"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Let's work
&lt;/h2&gt;

&lt;p&gt;First I needed to connect the LCD screen to the Arduino. I forgot how to do that, the last time I did something in an LCD screen was at the university for a "security system" project and that was like 3 or 4 years ago. So I followed the &lt;a href="https://www.arduino.cc/en/Tutorial/LibraryExamples/HelloWorld"&gt;"Hello world" tutorial&lt;/a&gt; for LCD Screen in the Arduino website.&lt;/p&gt;

&lt;p&gt;When the screen lit up and displayed the "hello, world" message I was ready to get into the coding of the game. I divided the game development into 3 stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create the obstacles in a random position and slide them in the screen.&lt;/li&gt;
&lt;li&gt;Create the ball and the collision logic.&lt;/li&gt;
&lt;li&gt;Add the jump logic to the ball.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Generating obstacles and movement
&lt;/h3&gt;

&lt;p&gt;The obstacles are going to fill the whole bottom row of the LCD screen. I could do this with two different approaches: Using a boolean array of 16 positions or using a single &lt;code&gt;short&lt;/code&gt; typed variable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8SyQIe2x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/8iifii8c5zzoc12klu0i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8SyQIe2x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/8iifii8c5zzoc12klu0i.png" alt="Array Binary representation" width="785" height="85"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I choose the latter approach because it will use only 2 bytes, while the former would be using 16 bytes. This also meant that I have to the operations in the obstacles by doing bitwise operations.&lt;/p&gt;

&lt;p&gt;Let's start with the fundamental operation of the obstacles: "Being painted on-screen". The code can be reviewed in the &lt;a href="https://github.com/d4vsanchez/jump-arduino/blob/main/jump-arduino.ino#L67"&gt;paintObstacles()&lt;/a&gt; function, I'm going to iterate the 16 positions of the bottom row and I'll check if the current position has a 1 in the binary representation of the number. For example, if I had the number 143 its binary representation is 10001111 and those positions will be filled as an obstacle.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fMcei4Sd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/kg78mltpnfpn6y0t1z7d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fMcei4Sd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/kg78mltpnfpn6y0t1z7d.png" alt="Obstacles Bit Representation" width="785" height="43"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To check if the current position has 1 or a 0, I just do an AND bitwise operation with the rightmost digit, and then I do a right shift bitwise operation to move all the digits to the left one position.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GFSDoY4X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/hqlkx77jctet2vn7zmyb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GFSDoY4X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/hqlkx77jctet2vn7zmyb.png" alt="Obstacles Bit Representation 1 Left Shift" width="785" height="43"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To generate the new obstacles, the code can be reviewed in the &lt;a href="https://github.com/d4vsanchez/jump-arduino/blob/main/jump-arduino.ino#L56"&gt;generateObstacles()&lt;/a&gt; function and I use a simple random number generation between 0 and 9, and there is a 30% chance that you get a new obstacle in the game. To generate a new obstacle I do a left shift operation to move the digits one place to the left and then an OR operation with the rightmost digit in case you get a new obstacle.&lt;/p&gt;

&lt;p&gt;That's all for obstacles!&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating the ball and adding collision logic
&lt;/h3&gt;

&lt;p&gt;The ball will be stationary in a cell of the LCD screen, starting in the bottom cell, the code can be reviewed in the &lt;a href="https://github.com/d4vsanchez/jump-arduino/blob/main/jump-arduino.ino#L89"&gt;paintBall()&lt;/a&gt; function. The ball is always going to be in the third cell from left to right which could be represented in binary as 8912.&lt;/p&gt;

&lt;p&gt;The function to set the cursor of the LCD receives and integer from 0 to 15, so I cannot send 8192 as an argument. Getting that number is done by doing a subtraction and logarithm: First I do a &lt;code&gt;log10(8192)/log10(2)&lt;/code&gt; to obtain 13 (2 to the power of 13 is 8192) and then I subtract this number from the total number of columns which is 16, that gives me 3.&lt;/p&gt;

&lt;p&gt;To know if the ball has collided with an obstacle I just check if the ball is in the air or not (a boolean variable) and do a AND operation between &lt;code&gt;USER_POSITION&lt;/code&gt; (which is 8192) and the &lt;code&gt;currentObstacles&lt;/code&gt; value (which can be any number between 0 and 32767); if this operation returns 1 it means that the ball has collided with an obstacle. This code can be reviewed in the &lt;a href="https://github.com/d4vsanchez/jump-arduino/blob/main/jump-arduino.ino#L85"&gt;hasLost()&lt;/a&gt; function.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LKYHmGt_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/2eaiyoeqhnsoj9glf78p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LKYHmGt_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/2eaiyoeqhnsoj9glf78p.png" alt="Collision occurred" width="800" height="59"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IfVW8S8j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/fet1e4t9hqkhb8vi350t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IfVW8S8j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/fet1e4t9hqkhb8vi350t.png" alt="No collision occurred" width="800" height="59"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After implementing this logic, I tweeted the current progress of the project, this is the tweet I made when I finished the collision logic.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1312864511368589317-248" src="https://platform.twitter.com/embed/Tweet.html?id=1312864511368589317"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1312864511368589317-248');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1312864511368589317&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Translation: The idea is to make a game similar to the dinosaur game in Chrome where you have to jump obstacles. I'm missing the jump logic and refining the code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Jump Logic
&lt;/h3&gt;

&lt;p&gt;When pressing the push button I save the the current execution time in a variable. I have function called &lt;a href="https://github.com/d4vsanchez/jump-arduino/blob/main/jump-arduino.ino#L81"&gt;isInAir()&lt;/a&gt; checking if that time is lower than 2 frame executions in the game, a frame is painted every 500ms. If the return of this function is true I paint the ball in the upper row and the collision logic will return false.&lt;/p&gt;

&lt;p&gt;This is another tweet I made showing the final result.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1312879111346544641-67" src="https://platform.twitter.com/embed/Tweet.html?id=1312879111346544641"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1312879111346544641-67');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1312879111346544641&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Translation: It's got jumps 😃 I've got to refine the code that went downwards in cleanness over time, and I'll upload it to GitHub. I've got some details to fix, such as the Game Over which looks to have bug in the letters and add some details to the ball when it jumps.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Ta-da!
&lt;/h2&gt;

&lt;p&gt;That's the game project, well at least this first version. I'm thinking into adding a bit more animations and small features such as a score.&lt;/p&gt;

&lt;p&gt;I hope you all like it and reproduce your own version at home! I'll be happy to receive PRs to the project with cool stuff that you implement on your own version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I wanted to optimize as much as possible in the size of the program, that's why I made some decisions in the design of the game such as using a short data type instead of the 16 positions bool array. The program ended weighting 3048 bytes (roughly 3 KB) and using 91 bytes of dynamic memory.&lt;/p&gt;

&lt;p&gt;Doing this kind of projects will make have fun (if you like doing electronics) and will also teach you different topics related to programming such as bit management.&lt;/p&gt;




&lt;p&gt;Thanks so much for reading up to here and remember to like ❤️ and share the article if you found it interesting!&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>hardware</category>
      <category>programming</category>
      <category>hobbyist</category>
    </item>
    <item>
      <title>Rebasing in Git to maintain history's health</title>
      <dc:creator>David Sánchez</dc:creator>
      <pubDate>Tue, 30 Jun 2020 16:09:44 +0000</pubDate>
      <link>https://dev.to/d4vsanchez/rebasing-in-git-to-maintain-history-s-health-310c</link>
      <guid>https://dev.to/d4vsanchez/rebasing-in-git-to-maintain-history-s-health-310c</guid>
      <description>&lt;p&gt;Have you ever felt that the Git history of your project is becoming a big mess and when you need to find out why a change was made you encounter a lot of small commits that were just adding tiny bits of code that you forgot to add in previous commits? Well, this also happened to me.&lt;/p&gt;

&lt;p&gt;I'm a passionate learner so I'm always trying to find out new ways of doing things and questioning the current knowledge I have about a tool. When I found out that the Git history of my project was having a lot of meaningless commits, I started asking myself if there was a better way to add those changes to previously made commits so they can maintain cohesion and in the future, another developer doesn't need to solve the puzzle by searching through multiple commits to finding the reason of the change.&lt;/p&gt;

&lt;p&gt;This is when I found about &lt;strong&gt;&lt;a href="https://git-scm.com/docs/git-rebase"&gt;Rebasing&lt;/a&gt;&lt;/strong&gt; in Git. There are two ways that we can use Rebase: as a "replacement" of &lt;code&gt;git merge&lt;/code&gt; and change, drop, and squash previously made commits. Gotcha, you can see that one of the use cases of the Rebase command fits perfectly the use case I'm searching for.&lt;/p&gt;

&lt;p&gt;I'm not going to talk about &lt;code&gt;git rebase&lt;/code&gt; as a "replacement" of &lt;code&gt;git merge&lt;/code&gt; in this article, but if you are curious how is that so, I recommend you to &lt;a href="https://www.atlassian.com/git/tutorials/merging-vs-rebasing"&gt;read this article from the people at Atlassian&lt;/a&gt; where they explain it.&lt;/p&gt;

&lt;p&gt;To explain how to do Interactive Rebase in Git I've created this simple example repository that has some meaningless commits that I want to get rid of.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RMjcql9B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/gG9wSD4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RMjcql9B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/gG9wSD4.png" alt="Tig showing the commits in the repository" width="800" height="99"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By using &lt;code&gt;tig&lt;/code&gt; we can observe multiple things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I'm doing these changes in a separate branch called &lt;em&gt;feature-branch&lt;/em&gt;, it's very important that you do not use &lt;code&gt;git rebase&lt;/code&gt; on public/shared branches as it will overwrite history. This means that if you force-push the changes, anyone else who's also working in your branch will go out of sync.&lt;/li&gt;
&lt;li&gt;There are two meaningless commits in my &lt;code&gt;feature-branch&lt;/code&gt; that only fixes some accents that I forgot to add in the &lt;em&gt;Adding my personal information&lt;/em&gt; commit. We will be fusing them 2 with the original commit. &lt;/li&gt;
&lt;li&gt;I made them all quickly in my example, but you may extrapolate the things we're going to make here into bigger projects.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Let's do it
&lt;/h2&gt;

&lt;p&gt;First, we'll take the SHA-1 of the oldest commit that we want to retain as-is. In this case, the last commit we want maintaining as-is is the &lt;em&gt;Adding my initial description file&lt;/em&gt; commit. This is because we are going to be fusing the two &lt;em&gt;"Forgot ..."&lt;/em&gt; commits with the &lt;em&gt;Adding my personal information&lt;/em&gt; commit, hence not retaining it as-is.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1xXFyC_x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/2kmsF5q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1xXFyC_x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/2kmsF5q.png" alt="Tig showing that the hash of the oldest commit we want to retain is: 6c8e26c67111b0cab4ce388f4899b3b879152f55" width="800" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have the hash of the commit, we're going to execute the following command which is going to run Rebase in Interactive mode, so we can easily pick which actions we want to perform to the commits.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git rebase &lt;span class="nt"&gt;-i&lt;/span&gt; 6c8e26c^
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may notice two things in the command:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We're using only a few characters of the hash: Git allows us to refer to a commit by only using few characters from it. You can &lt;a href="https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection"&gt;check out that in Git's documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The caret at the end: In order to have our current commit available in the rebase command we will need to bring the parent of the &lt;em&gt;6c8e26c&lt;/em&gt; commit. This will let us do the fusion operation between the 3 commits.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Executing this command will open the editor that we have configured in Git with the following text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pick d7ce763 Adding my personal information
pick b049848 Forgot to add accent in my last name
pick b215b9e Forgot to add accent in my city
pick b544998 Adding software development skills

# Rebase 6c8e26c..b544998 onto b544998 (4 commands)
#
# Commands:
# p, pick &amp;lt;commit&amp;gt; = use commit
# r, reword &amp;lt;commit&amp;gt; = use commit, but edit the commit message
# e, edit &amp;lt;commit&amp;gt; = use commit, but stop for amending
# s, squash &amp;lt;commit&amp;gt; = use commit, but meld into previous commit
# f, fixup &amp;lt;commit&amp;gt; = like "squash", but discard this commit's log message
# x, exec &amp;lt;command&amp;gt; = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop &amp;lt;commit&amp;gt; = remove commit
# l, label &amp;lt;label&amp;gt; = label current HEAD with a name
# t, reset &amp;lt;label&amp;gt; = reset HEAD to a label
# m, merge [-C &amp;lt;commit&amp;gt; | -c &amp;lt;commit&amp;gt;] &amp;lt;label&amp;gt; [# &amp;lt;oneline&amp;gt;]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c &amp;lt;commit&amp;gt; to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By reading at the commands, the best fit of actions for what we want to do is the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pick the &lt;em&gt;Adding my personal information&lt;/em&gt; commit (leave it as-is)&lt;/li&gt;
&lt;li&gt;Fixup the &lt;em&gt;Forgot to add accent in my last name&lt;/em&gt; commit&lt;/li&gt;
&lt;li&gt;Fixup the &lt;em&gt;Forgot to add accent in my city&lt;/em&gt; commit&lt;/li&gt;
&lt;li&gt;Pick the &lt;em&gt;Adding software development skills&lt;/em&gt; commit (leave it as-is)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By using the Fixup action instead of the Squash action, we're telling Git to move the changes of both commits to the above commit (&lt;em&gt;Adding my personal information&lt;/em&gt;) and not ask us to rewrite the commit message or append their commit messages to the one we had originally.&lt;/p&gt;

&lt;p&gt;This is how our file will look like after making the changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pick d7ce763 Adding my personal information
fixup b049848 Forgot to add accent in my last name
fixup b215b9e Forgot to add accent in my city
pick b544998 Adding software development skills

# Rebase 6c8e26c..b544998 onto b544998 (4 commands)
#
# Commands:
# p, pick &amp;lt;commit&amp;gt; = use commit
# r, reword &amp;lt;commit&amp;gt; = use commit, but edit the commit message
# e, edit &amp;lt;commit&amp;gt; = use commit, but stop for amending
# s, squash &amp;lt;commit&amp;gt; = use commit, but meld into previous commit
# f, fixup &amp;lt;commit&amp;gt; = like "squash", but discard this commit's log message
# x, exec &amp;lt;command&amp;gt; = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop &amp;lt;commit&amp;gt; = remove commit
# l, label &amp;lt;label&amp;gt; = label current HEAD with a name
# t, reset &amp;lt;label&amp;gt; = reset HEAD to a label
# m, merge [-C &amp;lt;commit&amp;gt; | -c &amp;lt;commit&amp;gt;] &amp;lt;label&amp;gt; [# &amp;lt;oneline&amp;gt;]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c &amp;lt;commit&amp;gt; to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it's only necessary to save and quit the editor and Git will do these operations instantly.&lt;/p&gt;

&lt;p&gt;This is the final result of running our rebase command with the fixup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vZGCM1lE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/ImbWscd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vZGCM1lE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/ImbWscd.png" alt="Tig showing that the meaningful commits are gone!" width="800" height="56"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are no more meaningless commits, both have been fused with the commit that added the personal information. This way no one has to solve the puzzle by having multiple commits that fix stuff that we mistyped or forgot to do in previous commits that are not yet merged into the stable branch or shared with another coworker.&lt;/p&gt;

&lt;p&gt;You can also use this &lt;a href="https://hackernoon.com/beginners-guide-to-interactive-rebasing-346a3f9c3a6d"&gt;Interactive Rebase to edit something&lt;/a&gt; in a previous commit that you just happened to see right now, you just have to use the edit action and Git will take you to that moment in history so you can modify it. The only difference when you finish making the change is that you don't do a &lt;code&gt;git commit&lt;/code&gt; but you do a &lt;code&gt;git commit --amend&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Maintaining meaningful commits in your Git history is important, it helps other developers flow through changes in a more natural way and waste less time reading meaningless changes that were added just to comply with Git's rule to always add a commit message.&lt;/p&gt;

&lt;p&gt;Rebase will help us achieve this goal by giving us tools to traverse the history of our repository and do changes to our commits so we don't need to create a single commit to fix something insignificant but we can edit the commit in which the problem it was introduced.&lt;/p&gt;

&lt;p&gt;We may need to take care of rebasing though. It is good that we only use it in branches that are not shared with anyone else and the commits are not merged into a public branch. After our branch is merged in the main branch of the project is not a good idea to rebase it, we'll have to create a new commit.&lt;/p&gt;




&lt;p&gt;I hope to read your comments about this method and if you like the post I really appreciate you to share it and add a reaction to it!&lt;/p&gt;

</description>
      <category>git</category>
      <category>tutorial</category>
      <category>tools</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Mocking and patching in Python for unit testing</title>
      <dc:creator>David Sánchez</dc:creator>
      <pubDate>Sun, 23 Feb 2020 21:16:48 +0000</pubDate>
      <link>https://dev.to/d4vsanchez/mocking-and-patching-in-python-for-unit-testing-3j08</link>
      <guid>https://dev.to/d4vsanchez/mocking-and-patching-in-python-for-unit-testing-3j08</guid>
      <description>&lt;p&gt;&lt;a href="http://ubidots.com/"&gt;Ubidots&lt;/a&gt; is an effortless point-and-click Internet of Things (IoT) application builder with data analytics and visualization. I am an engineer at Ubidots, and it is our daily goal to seamlessly and cleanly turn your sensor data into information that matters. Hiring an engineering team to develop a platform that both &lt;a href="https://ubidots.com/features"&gt;functions&lt;/a&gt; and looks great is costly in both time and money so we did it for you. One feature that many clients enjoy is Historical Reporting which is generated and saved with Amazon S3.&lt;/p&gt;

&lt;p&gt;When developing this feature, we needed to create a unit test using Python to test feature reliability. Using a mocked class of the &lt;a href="https://github.com/boto/boto3"&gt;boto3&lt;/a&gt; module. As we were in a test environment we couldn't allow the upload of files to our S3 Bucket nor did we have the correct credentials so we needed to find a way to mock the class but including (or actually taking out) the following features:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Don't communicate with Amazon, as I don't need to upload anything.&lt;/li&gt;
&lt;li&gt;Don't raise an exception by not having the correct credentials.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For obvious reasons, I cannot copy &amp;amp; paste the Ubidots source code, but for the purpose of this article, I have included an example somewhat similar to that which we use daily.&lt;/p&gt;

&lt;h1&gt;
  
  
  Let's code!
&lt;/h1&gt;

&lt;p&gt;Let's start with the most important part of the post: The code!&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the model
&lt;/h2&gt;

&lt;p&gt;This will be our "Report model" and it's got a method that will let us create the report and send it by email.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;StringIO&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;boto3.session&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# ...
&lt;/span&gt;    &lt;span class="c1"&gt;# Information about the model
&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_report_and_send_by_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Go back to the first bit of the stream
&lt;/span&gt;        &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seek&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="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;AWS_ACCESS_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aws_secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'s3'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AWS_BUCKET_DATA&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;put_object&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="s"&gt;'file.pdf'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;ACL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'public-read'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Expires&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'%Y/%m/%d'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;ContentType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'application/pdf'&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Do some other stuff and send the file by email
&lt;/span&gt;
    &lt;span class="c1"&gt;# More methods
&lt;/span&gt;    &lt;span class="c1"&gt;# ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, I've only implemented one method of our model as this is the focus of this article, but normally this model will have additional fields and other methods which have been left off solely for simplicity reasons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the test
&lt;/h2&gt;

&lt;p&gt;This will be the file that implements the unit tests for our model, you can read a bit more about writing and running unit tests in the &lt;a href="https://docs.djangoproject.com/en/1.11/topics/testing/overview/"&gt;Django Documentation site&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mail&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestCase&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;reports.models.report&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Report&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReportTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tearDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;Report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;all&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;outbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_create_report_and_send_by_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="c1"&gt;# This is a dict with the information of the fake Report
&lt;/span&gt;        &lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# There shouldn't be emails in the outbox
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;outbox&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="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_report_and_send_by_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# There should be one email in the outbox
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;outbox&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we were to run this code without the correct AWS credentials, it will raise an exception that says that the credentials are not correct and therefore our test will fail.&lt;/p&gt;

&lt;p&gt;Here is when we need to do a mock of the &lt;a href="http://boto3.readthedocs.io/en/latest/reference/core/session.html"&gt;Session&lt;/a&gt; class from &lt;em&gt;boto3&lt;/em&gt;; preventing the session from making a connection to Amazon S3. In this case, we expect nothing to be returned, the only thing we're going to do with the mock is prevent the connection with Amazon S3.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the mock
&lt;/h2&gt;

&lt;p&gt;The first thing we need to do is import the &lt;code&gt;mock&lt;/code&gt; and &lt;code&gt;boto3&lt;/code&gt; libraries in our test file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;mock&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;boto3.session&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we need to create our mock for Session and Resource classes, and we'll implement the methods we use with the same arguments to run our test. Below you will find our "FakeSession" and "FakeResource" to be used as our mocks classes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FakeResource&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;put_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&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="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ACL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Expires&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ContentType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# We do nothing here, but return the same data type without data
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FakeSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FakeResource&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, with these two mock classes created, we can add them to the test case using the patch. Using the patch, the test will run using these fake classes instead of the real ones from &lt;em&gt;boto3&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'reports.models.report.Session'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FakeSession&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_create_report_and_send_by_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Same implementation we used before
&lt;/span&gt;    &lt;span class="c1"&gt;# ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the patch is made to the namespace of the module that we're testing, not to the namespace of the original module we want to modify.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the mock to the Test
&lt;/h2&gt;

&lt;p&gt;Our test code (including the previous steps) looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;mock&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mail&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestCase&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;boto3.session&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;reports.models.report&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Report&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FakeResource&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;put_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&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="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ACL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Expires&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ContentType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# We do nothing here, but return the same data type without data
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FakeSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FakeResource&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'reports.models.report.Session'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FakeSession&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReportTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tearDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;Report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;all&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;outbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_create_report_and_send_by_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Fake report"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# This is a dict with the information of the fake Report
&lt;/span&gt;        &lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# There shouldn't be emails in the outbox
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;outbox&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="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_report_and_send_by_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# There should be one email in the outbox
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;outbox&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When this new test case is executed it will use the implementations we've created in FakeSession each time Session from boto3 is instanced.&lt;/p&gt;




&lt;p&gt;Using the patching decorator we were able to make a mock class from a third-party (&lt;em&gt;boto3&lt;/em&gt;) work the way we needed to test some modules; even when missing some parameters that would otherwise be available in a production environment, using a mock class we could test our module without the need of building content data to run the program.&lt;/p&gt;

&lt;p&gt;Authors' thoughts: This mock should only be made when it is necessary. It is important to think about how will this modification affect the test of the module; in the case of this piece, we only modified modules to prevent boto3 from connecting to Amazon and did not raise an exception. For our needs, the S3 feature wasn't important for the correct behavior of the test.&lt;/p&gt;




&lt;p&gt;If you found this article helpful please leave your reaction and share it in your networks! And you're also very welcome to leave feedback in the comments section ❤️.&lt;/p&gt;

</description>
      <category>python</category>
      <category>testing</category>
      <category>tutorial</category>
      <category>django</category>
    </item>
    <item>
      <title>Install Docker Community Edition in Linux Mint</title>
      <dc:creator>David Sánchez</dc:creator>
      <pubDate>Sun, 16 Feb 2020 22:53:40 +0000</pubDate>
      <link>https://dev.to/d4vsanchez/install-docker-community-edition-in-linux-mint-2gl4</link>
      <guid>https://dev.to/d4vsanchez/install-docker-community-edition-in-linux-mint-2gl4</guid>
      <description>&lt;p&gt;Docker is a popular software package that makes easier to create, deploy and run multiple applications using &lt;a href="https://en.wikipedia.org/wiki/LXC"&gt;containers&lt;/a&gt;. By using containers, developers may be assured that their code will run on any other machine that may not have the same packages or configuration as the one used to create the project originally.&lt;/p&gt;

&lt;p&gt;There are two Docker versions in existence: Docker CE (Community Edition) and Docker EE (Enterprise Edition). Docker CE is free and it's the one that we'll see how to install in this tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  System Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Linux Mint&lt;/li&gt;
&lt;li&gt;A user account that has superuser privileges&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I'll use
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Terminal:&lt;/strong&gt; I'm using &lt;a href="https://gnometerminator.blogspot.com/p/introduction.html"&gt;terminator&lt;/a&gt; as my Terminal Emulator. It's not going to be visible in the tutorial but 🤷&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux Mint:&lt;/strong&gt; I'm using &lt;a href="https://linuxmint.com/rel_tricia_cinnamon_whatsnew.php"&gt;Linux Mint 19.3 (Tricia)&lt;/a&gt; as my distribution.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install Docker
&lt;/h2&gt;

&lt;p&gt;These instructions are very similar to the ones posted in &lt;a href="https://docs.docker.com/install/linux/docker-ce/ubuntu/"&gt;Docker's Documentation&lt;/a&gt;, there are going to be some differences specific to Linux Mint installation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step 1: Install dependencies to use a repository over HTTPS&lt;/li&gt;
&lt;li&gt;Step 2: Add the Docker's official GPG key&lt;/li&gt;
&lt;li&gt;Step 3: Set up the Docker repository&lt;/li&gt;
&lt;li&gt;Step 4: Install Docker Engine&lt;/li&gt;
&lt;li&gt;(Optional) Step 5: Allow non-privileged user to run Docker&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1: Install dependencies to use a repository over HTTPS
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;apt-transport-https ca-certificates curl gnupg-agent software-properties-common
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Add the Docker's official GPG key
&lt;/h3&gt;

&lt;p&gt;When you add third-party repositories in Linux, you need to add their &lt;a href="https://en.wikipedia.org/wiki/GNU_Privacy_Guard"&gt;GPG key&lt;/a&gt; to ensure that the packages that are uploaded are valid and they can be installed in your system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://download.docker.com/linux/ubuntu/gpg | &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-key add -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this command will add the Docker's official GPG key to your system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Set up the Docker repository
&lt;/h3&gt;

&lt;p&gt;This is the part I told you that will change a bit from the original instructions in Docker's website. Their instructions are only intended for Ubuntu and its derivatives, but if you follow them closely in Linux Mint, you'll end up having errors in the configuration.&lt;/p&gt;

&lt;p&gt;This following line will tell us which is the Ubuntu version that our Linux Mint is built in top of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"UBUNTU_CODENAME"&lt;/span&gt; /etc/os-release | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s1"&gt;'='&lt;/span&gt; &lt;span class="s1"&gt;'{ print $2 }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As I'm using Linux Mint 19.3 (Tricia) it will display &lt;code&gt;bionic&lt;/code&gt; as the output. This line will then be used to add the Docker repository in your system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;add-apt-repository &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="s2"&gt;"deb [arch=amd64] https://download.docker.com/linux/ubuntu &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
   &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"UBUNTU_CODENAME"&lt;/span&gt; /etc/os-release | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s1"&gt;'='&lt;/span&gt; &lt;span class="s1"&gt;'{ print $2 }'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
   stable"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Install Docker Engine
&lt;/h3&gt;

&lt;p&gt;After this step is successfully executed, we can now install the Docker Engine by running a simple &lt;code&gt;apt-get install&lt;/code&gt; command:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I'm adding an &lt;code&gt;apt-get update&lt;/code&gt; command here to make sure that we have the latest changes in the repositories. Usually, the previous step should automatically do it, but I want to be sure that they're up to date.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;docker-ce docker-ce-cli containerd.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  (Optional) Step 5: Allow a non-privileged user to run Docker
&lt;/h3&gt;

&lt;p&gt;If you try to run Docker with your normal user, you'll run into an error because Docker cannot be run by non-privileged users. This step adds your user to the &lt;code&gt;docker&lt;/code&gt; group in your system and after rebooting your computer you'll be able to run Docker without &lt;code&gt;sudo&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;&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="nv"&gt;$USER&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it, you should have Docker running correctly in your computer. If you're struggling with any step or just want to know a bit more about what does any of these commands do, just leave me a comment and I'll be more than happy to help you!&lt;/p&gt;

&lt;p&gt;This is my first post in &lt;a href="https://dev.to/d4vsanchez"&gt;Dev.to&lt;/a&gt; and I'll be very grateful if you leave me any feedback about this post in the comments ❤️.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>linux</category>
      <category>linuxmint</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
